aboutsummaryrefslogtreecommitdiff
path: root/grit/format/policy_templates/writers/template_writer.py
blob: bd48425178776e2c52e4880b95f87eb833916d25 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.


class TemplateWriter(object):
  '''Abstract base class for writing policy templates in various formats.
  The methods of this class will be called by PolicyTemplateGenerator.
  '''

  def __init__(self, platforms, config):
    '''Initializes a TemplateWriter object.

    Args:
      platforms: List of platforms for which this writer can write policies.
      config: A dictionary of information required to generate the template.
        It contains some key-value pairs, including the following examples:
          'build': 'chrome' or 'chromium'
          'branding': 'Google Chrome' or 'Chromium'
          'mac_bundle_id': The Mac bundle id of Chrome. (Only set when building
            for Mac.)
      messages: List of all the message strings from the grd file. Most of them
        are also present in the policy data structures that are passed to
        methods. That is the preferred way of accessing them, this should only
        be used in exceptional cases. An example for its use is the
        IDS_POLICY_WIN_SUPPORTED_WINXPSP2 message in ADM files, because that
        cannot be associated with any policy or group.
    '''
    self.platforms = platforms
    self.config = config

  def IsDeprecatedPolicySupported(self, policy):
    '''Checks if the given deprecated policy is supported by the writer.

    Args:
      policy: The dictionary of the policy.

    Returns:
      True if the writer chooses to include the deprecated 'policy' in its
      output.
    '''
    return False

  def IsFuturePolicySupported(self, policy):
    '''Checks if the given future policy is supported by the writer.

    Args:
      policy: The dictionary of the policy.

    Returns:
      True if the writer chooses to include the deprecated 'policy' in its
      output.
    '''
    return False

  def IsPolicySupported(self, policy):
    '''Checks if the given policy is supported by the writer.
    In other words, the set of platforms supported by the writer
    has a common subset with the set of platforms that support
    the policy.

    Args:
      policy: The dictionary of the policy.

    Returns:
      True if the writer chooses to include 'policy' in its output.
    '''
    if ('deprecated' in policy and policy['deprecated'] is True and
        not self.IsDeprecatedPolicySupported(policy)):
      return False

    if ('future' in policy and policy['future'] is True and
        not self.IsFuturePolicySupported(policy)):
      return False

    if '*' in self.platforms:
      # Currently chrome_os is only catched here.
      return True
    for supported_on in policy['supported_on']:
      for supported_on_platform in supported_on['platforms']:
        if supported_on_platform in self.platforms:
          return True
    return False

  def CanBeRecommended(self, policy):
    '''Checks if the given policy can be recommended.'''
    return policy.get('features', {}).get('can_be_recommended', False)

  def CanBeMandatory(self, policy):
    '''Checks if the given policy can be mandatory.'''
    return policy.get('features', {}).get('can_be_mandatory', True)

  def IsPolicySupportedOnPlatform(self, policy, platform):
    '''Checks if |policy| is supported on |platform|.

    Args:
      policy: The dictionary of the policy.
      platform: The platform to check; one of 'win', 'mac', 'linux' or
        'chrome_os'.
    '''
    is_supported = lambda x: platform in x['platforms']
    return any(filter(is_supported, policy['supported_on']))

  def _GetPoliciesForWriter(self, group):
    '''Filters the list of policies in the passed group that are supported by
    the writer.

    Args:
      group: The dictionary of the policy group.

    Returns: The list of policies of the policy group that are compatible
      with the writer.
    '''
    if not 'policies' in group:
      return []
    result = []
    for policy in group['policies']:
      if self.IsPolicySupported(policy):
        result.append(policy)
    return result

  def Init(self):
    '''Initializes the writer. If the WriteTemplate method is overridden, then
    this method must be called as first step of each template generation
    process.
    '''
    pass

  def WriteTemplate(self, template):
    '''Writes the given template definition.

    Args:
      template: Template definition to write.

    Returns:
      Generated output for the passed template definition.
    '''
    self.messages = template['messages']
    self.Init()
    template['policy_definitions'] = \
        self.PreprocessPolicies(template['policy_definitions'])
    self.BeginTemplate()
    for policy in template['policy_definitions']:
      if policy['type'] == 'group':
        child_policies = self._GetPoliciesForWriter(policy)
        child_recommended_policies = filter(self.CanBeRecommended,
                                            child_policies)
        if child_policies:
          # Only write nonempty groups.
          self.BeginPolicyGroup(policy)
          for child_policy in child_policies:
            # Nesting of groups is currently not supported.
            self.WritePolicy(child_policy)
          self.EndPolicyGroup()
        if child_recommended_policies:
          self.BeginRecommendedPolicyGroup(policy)
          for child_policy in child_recommended_policies:
            self.WriteRecommendedPolicy(child_policy)
          self.EndRecommendedPolicyGroup()
      elif self.IsPolicySupported(policy):
        self.WritePolicy(policy)
        if self.CanBeRecommended(policy):
          self.WriteRecommendedPolicy(policy)
    self.EndTemplate()

    return self.GetTemplateText()

  def PreprocessPolicies(self, policy_list):
    '''Preprocesses a list of policies according to a given writer's needs.
    Preprocessing steps include sorting policies and stripping unneeded
    information such as groups (for writers that ignore them).
    Subclasses are encouraged to override this method, overriding
    implementations may call one of the provided specialized implementations.
    The default behaviour is to use SortPoliciesGroupsFirst().

    Args:
      policy_list: A list containing the policies to sort.

    Returns:
      The sorted policy list.
    '''
    return self.SortPoliciesGroupsFirst(policy_list)

  def WritePolicy(self, policy):
    '''Appends the template text corresponding to a policy into the
    internal buffer.

    Args:
      policy: The policy as it is found in the JSON file.
    '''
    raise NotImplementedError()

  def WriteRecommendedPolicy(self, policy):
    '''Appends the template text corresponding to a recommended policy into the
    internal buffer.

    Args:
      policy: The recommended policy as it is found in the JSON file.
    '''
    # TODO
    #raise NotImplementedError()
    pass

  def BeginPolicyGroup(self, group):
    '''Appends the template text corresponding to the beginning of a
    policy group into the internal buffer.

    Args:
      group: The policy group as it is found in the JSON file.
    '''
    pass

  def EndPolicyGroup(self):
    '''Appends the template text corresponding to the end of a
    policy group into the internal buffer.
    '''
    pass

  def BeginRecommendedPolicyGroup(self, group):
    '''Appends the template text corresponding to the beginning of a recommended
    policy group into the internal buffer.

    Args:
      group: The recommended policy group as it is found in the JSON file.
    '''
    pass

  def EndRecommendedPolicyGroup(self):
    '''Appends the template text corresponding to the end of a recommended
    policy group into the internal buffer.
    '''
    pass

  def BeginTemplate(self):
    '''Appends the text corresponding to the beginning of the whole
    template into the internal buffer.
    '''
    raise NotImplementedError()

  def EndTemplate(self):
    '''Appends the text corresponding to the end of the whole
    template into the internal buffer.
    '''
    pass

  def GetTemplateText(self):
    '''Gets the content of the internal template buffer.

    Returns:
      The generated template from the the internal buffer as a string.
    '''
    raise NotImplementedError()

  def SortPoliciesGroupsFirst(self, policy_list):
    '''Sorts a list of policies alphabetically. The order is the
    following: first groups alphabetically by caption, then other policies
    alphabetically by name. The order of policies inside groups is unchanged.

    Args:
      policy_list: The list of policies to sort. Sub-lists in groups will not
        be sorted.
    '''
    policy_list.sort(key=self.GetPolicySortingKeyGroupsFirst)
    return policy_list

  def FlattenGroupsAndSortPolicies(self, policy_list, sorting_key=None):
    '''Sorts a list of policies according to |sorting_key|, defaulting
    to alphabetical sorting if no key is given. If |policy_list| contains
    policies with type="group", it is flattened first, i.e. any groups' contents
    are inserted into the list as first-class elements and the groups are then
    removed.
    '''
    new_list = []
    for policy in policy_list:
      if policy['type'] == 'group':
        for grouped_policy in policy['policies']:
          new_list.append(grouped_policy)
      else:
        new_list.append(policy)
    if sorting_key == None:
      sorting_key = self.GetPolicySortingKeyName
    new_list.sort(key=sorting_key)
    return new_list

  def GetPolicySortingKeyName(self, policy):
    return policy['name']

  def GetPolicySortingKeyGroupsFirst(self, policy):
    '''Extracts a sorting key from a policy. These keys can be used for
    list.sort() methods to sort policies.
    See TemplateWriter.SortPolicies for usage.
    '''
    is_group = policy['type'] == 'group'
    if is_group:
      # Groups are sorted by caption.
      str_key = policy['caption']
    else:
      # Regular policies are sorted by name.
      str_key = policy['name']
    # Groups come before regular policies.
    return (not is_group, str_key)