blob: d3462f3b175c453f473e1ec67a2e3377f665ffc9 [file] [log] [blame]
#!/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.
import copy
import re
def IsGroupOrAtomicGroup(policy):
return policy['type'] == 'group' or policy['type'] == 'atomic_group'
class PolicyTemplateGenerator:
'''Generates template text for a particular platform.
This class is used to traverse a JSON structure from a .json template
definition metafile and merge GUI message string definitions that come
from a .grd resource tree onto it. After this, it can be used to output
this data to policy template files using TemplateWriter objects.
'''
def _ImportMessage(self, msg_txt):
msg_txt = msg_txt.decode('utf-8')
lines = msg_txt.split('\n')
# Strip any extra leading spaces, but keep useful indentation:
min_leading_spaces = min(list(self._IterateLeadingSpaces(lines)) or [0])
if min_leading_spaces > 0:
lstrip_pattern = re.compile('^[ ]{1,%s}' % min_leading_spaces)
lines = [lstrip_pattern.sub('', line) for line in lines]
# Strip all trailing spaces:
lines = [line.rstrip() for line in lines]
return "\n".join(lines)
def _IterateLeadingSpaces(self, lines):
'''Yields the number of leading spaces on each line, skipping lines which
have no leading spaces.'''
for line in lines:
match = re.search('^[ ]+', line)
if match:
yield len(match.group(0))
def __init__(self, config, policy_data):
'''Initializes this object with all the data necessary to output a
policy template.
Args:
config: Writer configuration.
policy_data: The list of defined policies and groups, as parsed from the
policy metafile. See
components/policy/resources/policy_templates.json
for description and content.
'''
# List of all the policies. Create a copy since the data is modified.
self._policy_data = copy.deepcopy(policy_data)
# Localized messages to be inserted to the policy_definitions structure:
self._messages = self._policy_data['messages']
self._config = config
for key in self._messages.keys():
self._messages[key]['text'] = self._ImportMessage(
self._messages[key]['text'])
self._AddGroups(self._policy_data['policy_definitions'])
self._AddAtomicGroups(self._policy_data['policy_definitions'],
self._policy_data['policy_atomic_group_definitions'])
self._policy_data[
'policy_atomic_group_definitions'] = self._ExpandAtomicGroups(
self._policy_data['policy_definitions'],
self._policy_data['policy_atomic_group_definitions'])
self._ProcessPolicyList(
self._policy_data['policy_atomic_group_definitions'])
self._policy_data['policy_definitions'] = self._ExpandGroups(
self._policy_data['policy_definitions'])
self._policy_definitions = self._policy_data['policy_definitions']
self._ProcessPolicyList(self._policy_definitions)
def _ProcessSupportedOn(self, supported_on):
'''Parses and converts the string items of the list of supported platforms
into dictionaries.
Args:
supported_on: The list of supported platforms. E.g.:
['chrome.win:8-10', 'chrome_frame:10-']
Returns:
supported_on: The list with its items converted to dictionaries. E.g.:
[{
'product': 'chrome',
'platforms': 'win',
'since_version': '8',
'until_version': '10'
}, {
'product': 'chrome_frame',
'platforms': 'win',
'since_version': '10',
'until_version': ''
}]
'''
result = []
for supported_on_item in supported_on:
product_platform_part, version_part = supported_on_item.split(':')
if '.' in product_platform_part:
product, platform = product_platform_part.split('.')
if platform == '*':
# e.g.: 'chrome.*:8-10'
platforms = ['linux', 'mac', 'win']
else:
# e.g.: 'chrome.win:-10'
platforms = [platform]
else:
# e.g.: 'chrome_frame:7-'
product, platform = {
'android': ('chrome', 'android'),
'webview_android': ('webview', 'android'),
'chrome_os': ('chrome_os', 'chrome_os'),
'chrome_frame': ('chrome_frame', 'win'),
}[product_platform_part]
platforms = [platform]
since_version, until_version = version_part.split('-')
result.append({
'product': product,
'platforms': platforms,
'since_version': since_version,
'until_version': until_version
})
return result
def _ProcessPolicy(self, policy):
'''Processes localized message strings in a policy or a group.
Also breaks up the content of 'supported_on' attribute into a list.
Args:
policy: The data structure of the policy or group, that will get message
strings here.
'''
if policy['type'] != 'atomic_group':
policy['desc'] = self._ImportMessage(policy['desc'])
policy['caption'] = self._ImportMessage(policy['caption'])
if 'label' in policy:
policy['label'] = self._ImportMessage(policy['label'])
if 'arc_support' in policy:
policy['arc_support'] = self._ImportMessage(policy['arc_support'])
if IsGroupOrAtomicGroup(policy):
self._ProcessPolicyList(policy['policies'])
elif policy['type'] in ('string-enum', 'int-enum', 'string-enum-list'):
# Iterate through all the items of an enum-type policy, and add captions.
for item in policy['items']:
item['caption'] = self._ImportMessage(item['caption'])
if not IsGroupOrAtomicGroup(policy):
if not 'label' in policy:
# If 'label' is not specified, then it defaults to 'caption':
policy['label'] = policy['caption']
policy['supported_on'] = self._ProcessSupportedOn(policy['supported_on'])
def _ProcessPolicyList(self, policy_list):
'''Adds localized message strings to each item in a list of policies and
groups. Also breaks up the content of 'supported_on' attributes into lists
of dictionaries.
Args:
policy_list: A list of policies and groups. Message strings will be added
for each item and to their child items, recursively.
'''
for policy in policy_list:
self._ProcessPolicy(policy)
def GetTemplateText(self, template_writer):
'''Generates the text of the template from the arguments given
to the constructor, using a given TemplateWriter.
Args:
template_writer: An object implementing TemplateWriter. Its methods
are called here for each item of self._policy_data.
Returns:
The text of the generated template.
'''
# Create a copy, so that writers can't screw up subsequent writers.
policy_data_copy = copy.deepcopy(self._policy_data)
return template_writer.WriteTemplate(policy_data_copy)
def _AddGroups(self, policy_list):
'''Adds a 'group' field, which is set to be the group's name, to the
policies that are part of a group.
Args:
policy_list: A list of policies and groups whose policies will have a
'group' field added.
'''
groups = [policy for policy in policy_list if policy['type'] == 'group']
policy_lookup = {
policy['name']: policy
for policy in policy_list
if not IsGroupOrAtomicGroup(policy)
}
for group in groups:
for policy_name in group['policies']:
policy_lookup[policy_name]['group'] = group['name']
def _AddAtomicGroups(self, policy_list, policy_atomic_groups):
'''Adds an 'atomic_group' field to the policies that are part of an atomic
group.
Args:
policy_list: A list of policies and groups.
policy_atomic_groups: A list of policy atomic groups
'''
policy_lookup = {
policy['name']: policy
for policy in policy_list
if not IsGroupOrAtomicGroup(policy)
}
for group in policy_atomic_groups:
for policy_name in group['policies']:
policy_lookup[policy_name]['atomic_group'] = group['name']
break
def _ExpandAtomicGroups(self, policy_list, policy_atomic_groups):
'''Replaces policies names inside atomic group definitions for actual
policies definitions.
Args:
policy_list: A list of policies and groups.
Returns:
Modified policy_list
'''
policies = [
policy for policy in policy_list if not IsGroupOrAtomicGroup(policy)
]
for group in policy_atomic_groups:
group['type'] = 'atomic_group'
expanded = self._ExpandGroups(policies + policy_atomic_groups)
expanded = [policy for policy in expanded if IsGroupOrAtomicGroup(policy)]
return copy.deepcopy(expanded)
def _ExpandGroups(self, policy_list):
'''Replaces policies names inside group definitions for actual policies
definitions. If policy does not belong to any group, leave it as is.
Args:
policy_list: A list of policies and groups.
Returns:
Modified policy_list
'''
groups = [policy for policy in policy_list if IsGroupOrAtomicGroup(policy)]
policies = {
policy['name']: policy
for policy in policy_list
if not IsGroupOrAtomicGroup(policy)
}
policies_in_groups = set()
result_policies = []
for group in groups:
group_policies = group['policies']
expanded_policies = [
policies[policy_name] for policy_name in group_policies
]
assert policies_in_groups.isdisjoint(group_policies)
policies_in_groups.update(group_policies)
group['policies'] = expanded_policies
result_policies.append(group)
result_policies.extend([
policy for policy in policy_list if not IsGroupOrAtomicGroup(policy) and
policy['name'] not in policies_in_groups
])
return result_policies