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