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 | 4 |
5 from collections import OrderedDict | |
6 from copy import deepcopy | 5 from copy import deepcopy |
7 from operator import itemgetter | 6 import json |
8 | 7 |
8 from features_utility import FeatureFile | |
9 from third_party.json_schema_compiler.json_parse import Parse | 9 from third_party.json_schema_compiler.json_parse import Parse |
10 | 10 |
11 def _ConvertAndSortDocs(properties, platform): | |
12 '''Convert docs and subdocs into lists and sort them, first by level, then | |
13 by name. | |
14 ''' | |
15 def sort_key(item): | |
16 '''Key function to sort items primarily by level (according to index into | |
17 levels) then subsort by name. | |
18 ''' | |
19 levels = ('required', 'recommended', 'only_one', 'optional') | |
20 | |
21 return (levels.index(item.get('level', 'optional')), item['name']) | |
22 | |
23 def convert_and_sort(properties): | |
24 for key in properties.keys(): | |
25 value = properties[key] | |
not at google - send to devlin
2013/07/29 23:30:09
for key, value in properties.items() ?
jshumway
2013/07/30 23:15:50
Done.
| |
26 if 'example' in value: | |
27 value['has_example'] = True | |
28 example = json.dumps(value['example']) | |
29 if example == '{}': | |
30 value['example'] = '{...}' | |
31 elif example == '[]': | |
32 value['example'] = '[...]' | |
33 else: | |
34 value['example'] = example | |
35 | |
36 if 'subdocs' in value: | |
37 properties[key]['subdocs'] = convert_and_sort(value['subdocs']) | |
38 | |
39 return sorted(properties.values(), key=sort_key) | |
40 | |
41 name = properties['name'] | |
42 name['example'] = name['example'].replace('{{title}}', platform.capitalize()) | |
43 | |
44 return convert_and_sort(properties) | |
45 | |
46 def _Annotate(properties): | |
47 '''Add level annotations to level groups in a manifest property list and | |
48 subdoc lists. Requeries a list of properties sorted by level, with subdocs | |
49 in sorted lists as well. | |
50 ''' | |
51 annotations = { | |
52 'required': 'Required', | |
53 'recommended': 'Recommended', | |
54 'only_one': 'Pick one (or none)', | |
55 'optional': 'Optional' | |
56 } | |
57 | |
58 def add_annotation(item, annotation): | |
59 if not 'annotations' in item: | |
60 item['annotations'] = [] | |
61 item['annotations'].insert(0, annotation) | |
62 | |
63 def annotate(parent_level, properties): | |
64 current_level = parent_level | |
65 for item in properties: | |
66 level = item.get('level', 'optional') | |
67 if level != current_level: | |
68 add_annotation(item, annotations[level]) | |
69 current_level = level | |
70 if 'subdocs' in item: | |
71 annotate(level, item['subdocs']) | |
72 if properties: | |
73 properties[-1]['is_last'] = True | |
74 | |
75 annotate('required', properties) | |
76 return properties | |
77 | |
11 class ManifestDataSource(object): | 78 class ManifestDataSource(object): |
12 '''Provides a template with access to manifest properties specific to apps or | 79 '''Provides a template with access to manifest properties specific to apps or |
13 extensions. | 80 extensions. |
14 ''' | 81 ''' |
15 def __init__(self, | 82 def __init__(self, |
16 compiled_fs_factory, | 83 compiled_fs_factory, |
17 file_system, | 84 file_system, |
18 manifest_path, | 85 manifest_path, |
19 features_path): | 86 features_path): |
20 self._manifest_path = manifest_path | 87 self._manifest_path = manifest_path |
21 self._features_path = features_path | 88 self._features_path = features_path |
22 self._file_system = file_system | 89 self._file_system = file_system |
23 self._cache = compiled_fs_factory.Create( | 90 self._cache = compiled_fs_factory.Create( |
24 self._CreateManifestData, ManifestDataSource) | 91 self._CreateManifestData, ManifestDataSource) |
25 | 92 |
26 def _ApplyAppsTransformations(self, manifest): | 93 def _CreateManifestData(self, _, content): |
27 manifest['required'][0]['example'] = 'Application' | 94 '''Combine the contents of |_manifest_path| and |_features_path| into a |
28 manifest['optional'][-1]['is_last'] = True | 95 master dictionary then create filtered lists to be used by templates. |
96 ''' | |
97 manifest_json = Parse(content) | |
98 manifest_features = FeatureFile( | |
99 Parse(self._file_system.ReadSingle(self._features_path))) | |
100 manifest_features.MergeWith(manifest_json) | |
29 | 101 |
30 def _ApplyExtensionsTransformations(self, manifest): | 102 apps_features = deepcopy(manifest_features).Filter('app').InsertSubDocs() |
31 manifest['optional'][-1]['is_last'] = True | 103 apps_features = _Annotate(_ConvertAndSortDocs(apps_features, 'app')) |
32 | 104 |
33 def _CreateManifestData(self, _, content): | 105 extensions_features = ( |
34 '''Take the contents of |_manifest_path| and create apps and extensions | 106 deepcopy(manifest_features).Filter('extension').InsertSubDocs()) |
35 versions of a manifest example based on the contents of |_features_path|. | 107 extensions_features = _Annotate( |
36 ''' | 108 _ConvertAndSortDocs(extensions_features, 'extension')) |
not at google - send to devlin
2013/07/29 23:30:09
if we immutify the FeatureModel class the all this
jshumway
2013/07/30 23:15:50
Done.
| |
37 def create_manifest_dict(): | |
38 manifest_dict = OrderedDict() | |
39 for category in ('required', 'only_one', 'recommended', 'optional'): | |
40 manifest_dict[category] = [] | |
41 return manifest_dict | |
42 | 109 |
43 apps = create_manifest_dict() | 110 return { |
44 extensions = create_manifest_dict() | 111 'apps': apps_features, |
45 | 112 'extensions': extensions_features, |
46 manifest_json = Parse(content) | 113 'all_features': manifest_features |
47 features_json = Parse(self._file_system.ReadSingle( | 114 } |
48 self._features_path)) | |
49 | |
50 def add_property(feature, manifest_key, category): | |
51 '''If |feature|, from features_json, has the correct extension_types, add | |
52 |manifest_key| to either apps or extensions. | |
53 ''' | |
54 added = False | |
55 extension_types = feature['extension_types'] | |
56 if extension_types == 'all' or 'platform_app' in extension_types: | |
57 apps[category].append(deepcopy(manifest_key)) | |
58 added = True | |
59 if extension_types == 'all' or 'extension' in extension_types: | |
60 extensions[category].append(deepcopy(manifest_key)) | |
61 added = True | |
62 return added | |
63 | |
64 # Property types are: required, only_one, recommended, and optional. | |
65 for category in manifest_json: | |
66 for manifest_key in manifest_json[category]: | |
67 # If a property is in manifest.json but not _manifest_features, this | |
68 # will cause an error. | |
69 feature = features_json[manifest_key['name']] | |
70 if add_property(feature, manifest_key, category): | |
71 del features_json[manifest_key['name']] | |
72 | |
73 # All of the properties left in features_json are assumed to be optional. | |
74 for feature in features_json.keys(): | |
75 item = features_json[feature] | |
76 # Handles instances where a features entry is a union with a whitelist. | |
77 if isinstance(item, list): | |
78 item = item[0] | |
79 add_property(item, {'name': feature}, 'optional') | |
80 | |
81 apps['optional'].sort(key=itemgetter('name')) | |
82 extensions['optional'].sort(key=itemgetter('name')) | |
83 | |
84 self._ApplyAppsTransformations(apps) | |
85 self._ApplyExtensionsTransformations(extensions) | |
86 | |
87 return {'apps': apps, 'extensions': extensions} | |
88 | 115 |
89 def get(self, key): | 116 def get(self, key): |
90 return self._cache.GetFromFile(self._manifest_path)[key] | 117 return self._cache.GetFromFile(self._manifest_path)[key] |
OLD | NEW |