1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6
7class TemplateWriter(object):
8  '''Abstract base class for writing policy templates in various formats.
9  The methods of this class will be called by PolicyTemplateGenerator.
10  '''
11
12  def __init__(self, platforms, config):
13    '''Initializes a TemplateWriter object.
14
15    Args:
16      platforms: List of platforms for which this writer can write policies.
17      config: A dictionary of information required to generate the template.
18        It contains some key-value pairs, including the following examples:
19          'build': 'chrome' or 'chromium'
20          'branding': 'Google Chrome' or 'Chromium'
21          'mac_bundle_id': The Mac bundle id of Chrome. (Only set when building
22            for Mac.)
23      messages: List of all the message strings from the grd file. Most of them
24        are also present in the policy data structures that are passed to
25        methods. That is the preferred way of accessing them, this should only
26        be used in exceptional cases. An example for its use is the
27        IDS_POLICY_WIN_SUPPORTED_WINXPSP2 message in ADM files, because that
28        cannot be associated with any policy or group.
29    '''
30    self.platforms = platforms
31    self.config = config
32
33  def IsDeprecatedPolicySupported(self, policy):
34    '''Checks if the given deprecated policy is supported by the writer.
35
36    Args:
37      policy: The dictionary of the policy.
38
39    Returns:
40      True if the writer chooses to include the deprecated 'policy' in its
41      output.
42    '''
43    return False
44
45  def IsFuturePolicySupported(self, policy):
46    '''Checks if the given future policy is supported by the writer.
47
48    Args:
49      policy: The dictionary of the policy.
50
51    Returns:
52      True if the writer chooses to include the deprecated 'policy' in its
53      output.
54    '''
55    return False
56
57  def IsPolicySupported(self, policy):
58    '''Checks if the given policy is supported by the writer.
59    In other words, the set of platforms supported by the writer
60    has a common subset with the set of platforms that support
61    the policy.
62
63    Args:
64      policy: The dictionary of the policy.
65
66    Returns:
67      True if the writer chooses to include 'policy' in its output.
68    '''
69    if ('deprecated' in policy and policy['deprecated'] is True and
70        not self.IsDeprecatedPolicySupported(policy)):
71      return False
72
73    if ('future' in policy and policy['future'] is True and
74        not self.IsFuturePolicySupported(policy)):
75      return False
76
77    if '*' in self.platforms:
78      # Currently chrome_os is only catched here.
79      return True
80    for supported_on in policy['supported_on']:
81      for supported_on_platform in supported_on['platforms']:
82        if supported_on_platform in self.platforms:
83          return True
84    return False
85
86  def CanBeRecommended(self, policy):
87    '''Checks if the given policy can be recommended.'''
88    return policy.get('features', {}).get('can_be_recommended', False)
89
90  def CanBeMandatory(self, policy):
91    '''Checks if the given policy can be mandatory.'''
92    return policy.get('features', {}).get('can_be_mandatory', True)
93
94  def IsPolicySupportedOnPlatform(self, policy, platform):
95    '''Checks if |policy| is supported on |platform|.
96
97    Args:
98      policy: The dictionary of the policy.
99      platform: The platform to check; one of 'win', 'mac', 'linux' or
100        'chrome_os'.
101    '''
102    is_supported = lambda x: platform in x['platforms']
103    return any(filter(is_supported, policy['supported_on']))
104
105  def _GetPoliciesForWriter(self, group):
106    '''Filters the list of policies in the passed group that are supported by
107    the writer.
108
109    Args:
110      group: The dictionary of the policy group.
111
112    Returns: The list of policies of the policy group that are compatible
113      with the writer.
114    '''
115    if not 'policies' in group:
116      return []
117    result = []
118    for policy in group['policies']:
119      if self.IsPolicySupported(policy):
120        result.append(policy)
121    return result
122
123  def Init(self):
124    '''Initializes the writer. If the WriteTemplate method is overridden, then
125    this method must be called as first step of each template generation
126    process.
127    '''
128    pass
129
130  def WriteTemplate(self, template):
131    '''Writes the given template definition.
132
133    Args:
134      template: Template definition to write.
135
136    Returns:
137      Generated output for the passed template definition.
138    '''
139    self.messages = template['messages']
140    self.Init()
141    template['policy_definitions'] = \
142        self.PreprocessPolicies(template['policy_definitions'])
143    self.BeginTemplate()
144    for policy in template['policy_definitions']:
145      if policy['type'] == 'group':
146        child_policies = self._GetPoliciesForWriter(policy)
147        child_recommended_policies = filter(self.CanBeRecommended,
148                                            child_policies)
149        if child_policies:
150          # Only write nonempty groups.
151          self.BeginPolicyGroup(policy)
152          for child_policy in child_policies:
153            # Nesting of groups is currently not supported.
154            self.WritePolicy(child_policy)
155          self.EndPolicyGroup()
156        if child_recommended_policies:
157          self.BeginRecommendedPolicyGroup(policy)
158          for child_policy in child_recommended_policies:
159            self.WriteRecommendedPolicy(child_policy)
160          self.EndRecommendedPolicyGroup()
161      elif self.IsPolicySupported(policy):
162        self.WritePolicy(policy)
163        if self.CanBeRecommended(policy):
164          self.WriteRecommendedPolicy(policy)
165    self.EndTemplate()
166
167    return self.GetTemplateText()
168
169  def PreprocessPolicies(self, policy_list):
170    '''Preprocesses a list of policies according to a given writer's needs.
171    Preprocessing steps include sorting policies and stripping unneeded
172    information such as groups (for writers that ignore them).
173    Subclasses are encouraged to override this method, overriding
174    implementations may call one of the provided specialized implementations.
175    The default behaviour is to use SortPoliciesGroupsFirst().
176
177    Args:
178      policy_list: A list containing the policies to sort.
179
180    Returns:
181      The sorted policy list.
182    '''
183    return self.SortPoliciesGroupsFirst(policy_list)
184
185  def WritePolicy(self, policy):
186    '''Appends the template text corresponding to a policy into the
187    internal buffer.
188
189    Args:
190      policy: The policy as it is found in the JSON file.
191    '''
192    raise NotImplementedError()
193
194  def WriteRecommendedPolicy(self, policy):
195    '''Appends the template text corresponding to a recommended policy into the
196    internal buffer.
197
198    Args:
199      policy: The recommended policy as it is found in the JSON file.
200    '''
201    # TODO
202    #raise NotImplementedError()
203    pass
204
205  def BeginPolicyGroup(self, group):
206    '''Appends the template text corresponding to the beginning of a
207    policy group into the internal buffer.
208
209    Args:
210      group: The policy group as it is found in the JSON file.
211    '''
212    pass
213
214  def EndPolicyGroup(self):
215    '''Appends the template text corresponding to the end of a
216    policy group into the internal buffer.
217    '''
218    pass
219
220  def BeginRecommendedPolicyGroup(self, group):
221    '''Appends the template text corresponding to the beginning of a recommended
222    policy group into the internal buffer.
223
224    Args:
225      group: The recommended policy group as it is found in the JSON file.
226    '''
227    pass
228
229  def EndRecommendedPolicyGroup(self):
230    '''Appends the template text corresponding to the end of a recommended
231    policy group into the internal buffer.
232    '''
233    pass
234
235  def BeginTemplate(self):
236    '''Appends the text corresponding to the beginning of the whole
237    template into the internal buffer.
238    '''
239    raise NotImplementedError()
240
241  def EndTemplate(self):
242    '''Appends the text corresponding to the end of the whole
243    template into the internal buffer.
244    '''
245    pass
246
247  def GetTemplateText(self):
248    '''Gets the content of the internal template buffer.
249
250    Returns:
251      The generated template from the the internal buffer as a string.
252    '''
253    raise NotImplementedError()
254
255  def SortPoliciesGroupsFirst(self, policy_list):
256    '''Sorts a list of policies alphabetically. The order is the
257    following: first groups alphabetically by caption, then other policies
258    alphabetically by name. The order of policies inside groups is unchanged.
259
260    Args:
261      policy_list: The list of policies to sort. Sub-lists in groups will not
262        be sorted.
263    '''
264    policy_list.sort(key=self.GetPolicySortingKeyGroupsFirst)
265    return policy_list
266
267  def FlattenGroupsAndSortPolicies(self, policy_list, sorting_key=None):
268    '''Sorts a list of policies according to |sorting_key|, defaulting
269    to alphabetical sorting if no key is given. If |policy_list| contains
270    policies with type="group", it is flattened first, i.e. any groups' contents
271    are inserted into the list as first-class elements and the groups are then
272    removed.
273    '''
274    new_list = []
275    for policy in policy_list:
276      if policy['type'] == 'group':
277        for grouped_policy in policy['policies']:
278          new_list.append(grouped_policy)
279      else:
280        new_list.append(policy)
281    if sorting_key == None:
282      sorting_key = self.GetPolicySortingKeyName
283    new_list.sort(key=sorting_key)
284    return new_list
285
286  def GetPolicySortingKeyName(self, policy):
287    return policy['name']
288
289  def GetPolicySortingKeyGroupsFirst(self, policy):
290    '''Extracts a sorting key from a policy. These keys can be used for
291    list.sort() methods to sort policies.
292    See TemplateWriter.SortPolicies for usage.
293    '''
294    is_group = policy['type'] == 'group'
295    if is_group:
296      # Groups are sorted by caption.
297      str_key = policy['caption']
298    else:
299      # Regular policies are sorted by name.
300      str_key = policy['name']
301    # Groups come before regular policies.
302    return (not is_group, str_key)
303