1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5from copy import deepcopy 6import json 7 8from data_source import DataSource 9from future import Future 10from manifest_features import ConvertDottedKeysToNested 11from platform_util import GetPlatforms, PluralToSingular 12 13 14def _ListifyAndSortDocs(features, app_name): 15 '''Convert a |feautres| dictionary, and all 'children' dictionaries, into 16 lists recursively. Sort lists first by 'level' then by name. 17 ''' 18 def sort_key(item): 19 '''Key function to sort items primarily by level (according to index into 20 levels) then subsort by name. 21 ''' 22 levels = ('required', 'recommended', 'only_one', 'optional') 23 24 return (levels.index(item.get('level', 'optional')), item['name']) 25 26 def coerce_example_to_feature(feature): 27 '''To display json in examples more clearly, convert the example of 28 |feature| into the feature format, with a name and children, to be rendered 29 by the templates. Only applicable to examples that are dictionaries. 30 ''' 31 if not isinstance(feature.get('example'), dict): 32 if 'example' in feature: 33 feature['example'] = json.dumps(feature['example']) 34 return 35 # Add any keys/value pairs in the dict as children 36 for key, value in feature['example'].iteritems(): 37 if not 'children' in feature: 38 feature['children'] = {} 39 feature['children'][key] = { 'name': key, 'example': value } 40 del feature['example'] 41 del feature['has_example'] 42 43 def convert_and_sort(features): 44 for key, value in features.items(): 45 if 'example' in value: 46 value['has_example'] = True 47 example = json.dumps(value['example']) 48 if example == '{}': 49 value['example'] = '{...}' 50 elif example == '[]': 51 value['example'] = '[...]' 52 elif example == '[{}]': 53 value['example'] = '[{...}]' 54 else: 55 coerce_example_to_feature(value) 56 if 'children' in value: 57 features[key]['children'] = convert_and_sort(value['children']) 58 return sorted(features.values(), key=sort_key) 59 60 # Replace {{platform}} in the 'name' manifest property example with 61 # |app_name|, the convention that the normal template rendering uses. 62 # TODO(kalman): Make the example a template and pass this through there. 63 if 'name' in features: 64 name = features['name'] 65 name['example'] = name['example'].replace('{{platform}}', app_name) 66 67 features = convert_and_sort(features) 68 69 return features 70 71def _AddLevelAnnotations(features): 72 '''Add level annotations to |features|. |features| and children lists must be 73 sorted by 'level'. Annotations are added to the first item in a group of 74 features of the same 'level'. 75 76 The last item in a list has 'is_last' set to True. 77 ''' 78 annotations = { 79 'required': 'Required', 80 'recommended': 'Recommended', 81 'only_one': 'Pick one (or none)', 82 'optional': 'Optional' 83 } 84 85 def add_annotation(item, annotation): 86 if not 'annotations' in item: 87 item['annotations'] = [] 88 item['annotations'].insert(0, annotation) 89 90 def annotate(parent_level, features): 91 current_level = parent_level 92 for item in features: 93 level = item.get('level', 'optional') 94 if level != current_level: 95 add_annotation(item, annotations[level]) 96 current_level = level 97 if 'children' in item: 98 annotate(level, item['children']) 99 if features: 100 features[-1]['is_last'] = True 101 102 annotate('required', features) 103 return features 104 105class ManifestDataSource(DataSource): 106 '''Provides access to the properties in manifest features. 107 ''' 108 def __init__(self, server_instance, _): 109 self._platform_bundle = server_instance.platform_bundle 110 self._object_store = server_instance.object_store_creator.Create( 111 ManifestDataSource) 112 113 def _CreateManifestDataForPlatform(self, platform): 114 future_manifest_features = self._platform_bundle.GetFeaturesBundle( 115 platform).GetManifestFeatures() 116 def resolve(): 117 manifest_features = future_manifest_features.Get() 118 return _AddLevelAnnotations(_ListifyAndSortDocs( 119 ConvertDottedKeysToNested(deepcopy(manifest_features)), 120 app_name=PluralToSingular(platform).capitalize())) 121 return Future(callback=resolve) 122 123 def _CreateManifestData(self): 124 manifest_data_futures = dict((p, self._CreateManifestDataForPlatform(p)) 125 for p in GetPlatforms()) 126 def resolve(): 127 return dict((platform, future.Get()) 128 for platform, future in manifest_data_futures.iteritems()) 129 return Future(callback=resolve) 130 131 def _GetCachedManifestData(self): 132 data = self._object_store.Get('manifest_data').Get() 133 if data is None: 134 data = self._CreateManifestData().Get() 135 self._object_store.Set('manifest_data', data) 136 return data 137 138 def get(self, key): 139 return self._GetCachedManifestData().get(key) 140 141 def Refresh(self, path): 142 return self._CreateManifestData() 143