OLD | NEW |
---|---|
1 # Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 import json | |
4 | 5 |
5 from copy import deepcopy | 6 from features_model import FeaturesModel |
6 from operator import itemgetter | 7 from third_party.json_schema_compiler.json_parse import Parse |
7 | 8 |
8 from third_party.json_schema_compiler.json_parse import OrderedDict, Parse | 9 def _ConvertAndSortDocs(properties, platform): |
not at google - send to devlin
2013/07/31 18:50:20
"Convert" needs a less generic name (what's it con
jshumway
2013/08/01 22:27:50
Done.
| |
10 '''Convert docs and children into lists and sort them, first by level, then | |
11 by name. | |
12 ''' | |
13 def sort_key(item): | |
14 '''Key function to sort items primarily by level (according to index into | |
15 levels) then subsort by name. | |
16 ''' | |
17 levels = ('required', 'recommended', 'only_one', 'optional') | |
18 | |
19 return (levels.index(item.get('level', 'optional')), item['name']) | |
20 | |
21 def convert_and_sort(properties): | |
22 for key, value in properties.items(): | |
23 if 'example' in value: | |
24 value['has_example'] = True | |
25 example = json.dumps(value['example']) | |
26 if example == '{}': | |
27 value['example'] = '{...}' | |
28 elif example == '[]': | |
29 value['example'] = '[...]' | |
30 else: | |
31 value['example'] = example | |
32 | |
33 if 'children' in value: | |
34 properties[key]['children'] = convert_and_sort(value['children']) | |
35 | |
36 return sorted(properties.values(), key=sort_key) | |
37 | |
38 name = properties['name'] | |
39 name['example'] = name['example'].replace('{{title}}', platform.capitalize()) | |
40 | |
41 return convert_and_sort(properties) | |
42 | |
43 def _Annotate(properties): | |
not at google - send to devlin
2013/07/31 18:50:20
"Annotate" needs a less generic name (what's it an
jshumway
2013/08/01 22:27:50
Done.
| |
44 '''Add level annotations to level groups in a manifest property list and | |
45 child lists. Requeries a list of properties sorted by level, with children | |
46 in sorted lists as well. | |
47 ''' | |
48 annotations = { | |
49 'required': 'Required', | |
50 'recommended': 'Recommended', | |
51 'only_one': 'Pick one (or none)', | |
52 'optional': 'Optional' | |
53 } | |
54 | |
55 def add_annotation(item, annotation): | |
56 if not 'annotations' in item: | |
57 item['annotations'] = [] | |
58 item['annotations'].insert(0, annotation) | |
59 | |
60 def annotate(parent_level, properties): | |
61 current_level = parent_level | |
62 for item in properties: | |
63 level = item.get('level', 'optional') | |
64 if level != current_level: | |
65 add_annotation(item, annotations[level]) | |
66 current_level = level | |
67 if 'children' in item: | |
68 annotate(level, item['children']) | |
69 if properties: | |
70 properties[-1]['is_last'] = True | |
71 | |
72 annotate('required', properties) | |
73 return properties | |
74 | |
75 def _RestructureChildren(features): | |
76 '''Features whose names are of the form 'parent.child' are moved to be part | |
77 of the 'parent' dictionary under the key 'children'. Names are changed to | |
78 the 'child' section of the original name. | |
79 ''' | |
80 def add_child(features, parent, child_name, value): | |
81 value['name'] = child_name | |
82 if not 'children' in features[parent]: | |
83 features[parent]['children'] = {} | |
84 features[parent]['children'][child_name] = value | |
85 | |
86 def insert_children(features): | |
87 for name in features.keys(): | |
88 if '.' in name: | |
89 value = features.pop(name) | |
90 parent, child_name = name.split('.', 1) | |
91 add_child(features, parent, child_name, value) | |
92 | |
93 for value in features.values(): | |
94 if 'children' in value: | |
95 insert_children(value['children']) | |
96 | |
97 insert_children(features) | |
98 return features | |
9 | 99 |
10 class ManifestDataSource(object): | 100 class ManifestDataSource(object): |
11 '''Provides a template with access to manifest properties specific to apps or | 101 '''Provides a template with access to manifest properties specific to apps or |
12 extensions. | 102 extensions. |
13 ''' | 103 ''' |
14 def __init__(self, | 104 def __init__(self, |
15 compiled_fs_factory, | 105 compiled_fs_factory, |
16 file_system, | 106 file_system, |
17 manifest_path, | 107 manifest_path, |
18 features_path): | 108 features_path): |
19 self._manifest_path = manifest_path | 109 self._manifest_path = manifest_path |
20 self._features_path = features_path | 110 self._features_path = features_path |
21 self._file_system = file_system | 111 self._file_system = file_system |
22 self._cache = compiled_fs_factory.Create( | 112 self._cache = compiled_fs_factory.Create( |
23 self._CreateManifestData, ManifestDataSource) | 113 self._CreateManifestData, ManifestDataSource) |
24 | 114 |
25 def _ApplyAppsTransformations(self, manifest): | 115 def GetFeatures(self): |
26 manifest['required'][0]['example'] = 'Application' | 116 manifest_json = Parse(self._file_system.ReadSingle(self._manifest_path)) |
27 manifest['optional'][-1]['is_last'] = True | 117 manifest_features = FeaturesModel.LoadFeaturesJson( |
118 Parse(self._file_system.ReadSingle(self._features_path))) | |
28 | 119 |
29 def _ApplyExtensionsTransformations(self, manifest): | 120 return manifest_features.MergeWith(manifest_json) |
30 manifest['optional'][-1]['is_last'] = True | |
31 | 121 |
32 def _CreateManifestData(self, _, content): | 122 def _CreateManifestData(self, _, content): |
33 '''Take the contents of |_manifest_path| and create apps and extensions | 123 '''Combine the contents of |_manifest_path| and |_features_path| into a |
34 versions of a manifest example based on the contents of |_features_path|. | 124 master dictionary then create filtered lists to be used by templates. |
35 ''' | 125 ''' |
36 def create_manifest_dict(): | 126 manifest_json = Parse(content) |
37 manifest_dict = OrderedDict() | 127 manifest_features = FeaturesModel.LoadFeaturesJson( |
38 for category in ('required', 'only_one', 'recommended', 'optional'): | 128 Parse(self._file_system.ReadSingle(self._features_path))) |
39 manifest_dict[category] = [] | |
40 return manifest_dict | |
41 | 129 |
42 apps = create_manifest_dict() | 130 manifest_features = manifest_features.MergeWith(manifest_json) |
43 extensions = create_manifest_dict() | 131 apps_features = _Annotate(_ConvertAndSortDocs(_RestructureChildren( |
132 manifest_features.Filter(platform='app').Get()), 'app')) | |
133 extensions_features = _Annotate(_ConvertAndSortDocs(_RestructureChildren( | |
134 manifest_features.Filter(platform='extension').Get()), 'extension')) | |
not at google - send to devlin
2013/07/31 18:50:20
pull this repeated code into a little helper funct
jshumway
2013/08/01 22:27:50
Done.
| |
44 | 135 |
45 manifest_json = Parse(content) | 136 return { |
46 features_json = Parse(self._file_system.ReadSingle( | 137 'apps': apps_features, |
47 self._features_path)) | 138 'extensions': extensions_features |
48 | 139 } |
49 def add_property(feature, manifest_key, category): | |
50 '''If |feature|, from features_json, has the correct extension_types, add | |
51 |manifest_key| to either apps or extensions. | |
52 ''' | |
53 added = False | |
54 extension_types = feature['extension_types'] | |
55 if extension_types == 'all' or 'platform_app' in extension_types: | |
56 apps[category].append(deepcopy(manifest_key)) | |
57 added = True | |
58 if extension_types == 'all' or 'extension' in extension_types: | |
59 extensions[category].append(deepcopy(manifest_key)) | |
60 added = True | |
61 return added | |
62 | |
63 # Property types are: required, only_one, recommended, and optional. | |
64 for category in manifest_json: | |
65 for manifest_key in manifest_json[category]: | |
66 # If a property is in manifest.json but not _manifest_features, this | |
67 # will cause an error. | |
68 feature = features_json[manifest_key['name']] | |
69 if add_property(feature, manifest_key, category): | |
70 del features_json[manifest_key['name']] | |
71 | |
72 # All of the properties left in features_json are assumed to be optional. | |
73 for feature in features_json.keys(): | |
74 item = features_json[feature] | |
75 # Handles instances where a features entry is a union with a whitelist. | |
76 if isinstance(item, list): | |
77 item = item[0] | |
78 add_property(item, {'name': feature}, 'optional') | |
79 | |
80 apps['optional'].sort(key=itemgetter('name')) | |
81 extensions['optional'].sort(key=itemgetter('name')) | |
82 | |
83 self._ApplyAppsTransformations(apps) | |
84 self._ApplyExtensionsTransformations(extensions) | |
85 | |
86 return {'apps': apps, 'extensions': extensions} | |
87 | 140 |
88 def get(self, key): | 141 def get(self, key): |
89 return self._cache.GetFromFile(self._manifest_path)[key] | 142 return self._cache.GetFromFile(self._manifest_path)[key] |
OLD | NEW |