# Copyright 2013 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. """Updates EnterprisePolicies enum in histograms.xml file with policy definitions read from policy_templates.json. If the file was pretty-printed, the updated version is pretty-printed too. """ import re import sys from ast import literal_eval from optparse import OptionParser from xml.dom import minidom from diffutil import PromptUserToAcceptDiff from pretty_print import PrettyPrintNode HISTOGRAMS_PATH = 'histograms.xml' POLICY_TEMPLATES_PATH = '../../../components/policy/resources/policy_templates.json' ENUM_NAME = 'EnterprisePolicies' class UserError(Exception): def __init__(self, message): Exception.__init__(self, message) @property def message(self): return self.args[0] def FlattenPolicies(policy_definitions, policy_list): """Appends a list of policies defined in |policy_definitions| to |policy_list|, flattening subgroups. Args: policy_definitions: A list of policy definitions and groups, as in policy_templates.json file. policy_list: A list that has policy definitions appended to it. """ for policy in policy_definitions: if policy['type'] == 'group': FlattenPolicies(policy['policies'], policy_list) else: policy_list.append(policy) def ParsePlaceholders(text): """Parse placeholders in |text|, making it more human-readable. The format of |text| is exactly the same as in captions in policy_templates.json: it can contain XML tags (ph, ex) and $1-like substitutions. Note that this function does only a very simple parsing that is not fully correct, but should be enough for all practical situations. Args: text: A string containing placeholders. Returns: |text| with placeholders removed or replaced by readable text. """ text = re.sub(r'\$\d+', '', text) # Remove $1-like substitutions. text = re.sub(r'<[^>]+>', '', text) # Remove XML tags. return text def UpdateHistogramDefinitions(policy_templates, doc): """Sets the children of node in |doc| to values generated from policy ids contained in |policy_templates|. Args: policy_templates: A list of dictionaries, defining policies or policy groups. The format is exactly the same as in policy_templates.json file. doc: A minidom.Document object representing parsed histogram definitions XML file. """ # Find EnterprisePolicies enum. for enum_node in doc.getElementsByTagName('enum'): if enum_node.attributes['name'].value == ENUM_NAME: policy_enum_node = enum_node break else: raise UserError('No policy enum node found') # Remove existing values. while policy_enum_node.hasChildNodes(): policy_enum_node.removeChild(policy_enum_node.lastChild) # Add a "Generated from (...)" comment comment = ' Generated from {0} '.format(POLICY_TEMPLATES_PATH) policy_enum_node.appendChild(doc.createComment(comment)) # Add values generated from policy templates. ordered_policies = [] FlattenPolicies(policy_templates['policy_definitions'], ordered_policies) ordered_policies.sort(key=lambda policy: policy['id']) for policy in ordered_policies: node = doc.createElement('int') node.attributes['value'] = str(policy['id']) node.attributes['label'] = ParsePlaceholders(policy['caption']) policy_enum_node.appendChild(node) def main(): if len(sys.argv) > 1: print >>sys.stderr, 'No arguments expected!' sys.stderr.write(__doc__) sys.exit(1) with open(POLICY_TEMPLATES_PATH, 'rb') as f: policy_templates = literal_eval(f.read()) with open(HISTOGRAMS_PATH, 'rb') as f: histograms_doc = minidom.parse(f) f.seek(0) xml = f.read() UpdateHistogramDefinitions(policy_templates, histograms_doc) new_xml = PrettyPrintNode(histograms_doc) if PromptUserToAcceptDiff(xml, new_xml, 'Is the updated version acceptable?'): with open(HISTOGRAMS_PATH, 'wb') as f: f.write(new_xml) if __name__ == '__main__': try: main() except UserError as e: print >>sys.stderr, e.message sys.exit(1)