• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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