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 5 """Updates EnterprisePolicies enum in histograms.xml file with policy 6 definitions read from policy_templates.json. 7 8 If the file was pretty-printed, the updated version is pretty-printed too. 9 """ 10 11 import re 12 import sys 13 14 from ast import literal_eval 15 from optparse import OptionParser 16 from xml.dom import minidom 17 18 from diffutil import PromptUserToAcceptDiff 19 from pretty_print import PrettyPrintNode 20 21 HISTOGRAMS_PATH = 'histograms.xml' 22 POLICY_TEMPLATES_PATH = 23 '../../../components/policy/resources/policy_templates.json' 24 ENUM_NAME = 'EnterprisePolicies' 25 26 class UserError(Exception): 27 def __init__(self, message): 28 Exception.__init__(self, message) 29 30 @property 31 def message(self): 32 return self.args[0] 33 34 35 def FlattenPolicies(policy_definitions, policy_list): 36 """Appends a list of policies defined in |policy_definitions| to 37 |policy_list|, flattening subgroups. 38 39 Args: 40 policy_definitions: A list of policy definitions and groups, as in 41 policy_templates.json file. 42 policy_list: A list that has policy definitions appended to it. 43 """ 44 for policy in policy_definitions: 45 if policy['type'] == 'group': 46 FlattenPolicies(policy['policies'], policy_list) 47 else: 48 policy_list.append(policy) 49 50 51 def ParsePlaceholders(text): 52 """Parse placeholders in |text|, making it more human-readable. The format of 53 |text| is exactly the same as in captions in policy_templates.json: it can 54 contain XML tags (ph, ex) and $1-like substitutions. Note that this function 55 does only a very simple parsing that is not fully correct, but should be 56 enough for all practical situations. 57 58 Args: 59 text: A string containing placeholders. 60 61 Returns: 62 |text| with placeholders removed or replaced by readable text. 63 """ 64 text = re.sub(r'\$\d+', '', text) # Remove $1-like substitutions. 65 text = re.sub(r'<[^>]+>', '', text) # Remove XML tags. 66 return text 67 68 69 def UpdateHistogramDefinitions(policy_templates, doc): 70 """Sets the children of <enum name="EnterprisePolicies" ...> node in |doc| to 71 values generated from policy ids contained in |policy_templates|. 72 73 Args: 74 policy_templates: A list of dictionaries, defining policies or policy 75 groups. The format is exactly the same as in 76 policy_templates.json file. 77 doc: A minidom.Document object representing parsed histogram definitions 78 XML file. 79 """ 80 # Find EnterprisePolicies enum. 81 for enum_node in doc.getElementsByTagName('enum'): 82 if enum_node.attributes['name'].value == ENUM_NAME: 83 policy_enum_node = enum_node 84 break 85 else: 86 raise UserError('No policy enum node found') 87 88 # Remove existing values. 89 while policy_enum_node.hasChildNodes(): 90 policy_enum_node.removeChild(policy_enum_node.lastChild) 91 92 # Add a "Generated from (...)" comment 93 comment = ' Generated from {0} '.format(POLICY_TEMPLATES_PATH) 94 policy_enum_node.appendChild(doc.createComment(comment)) 95 96 # Add values generated from policy templates. 97 ordered_policies = [] 98 FlattenPolicies(policy_templates['policy_definitions'], ordered_policies) 99 ordered_policies.sort(key=lambda policy: policy['id']) 100 for policy in ordered_policies: 101 node = doc.createElement('int') 102 node.attributes['value'] = str(policy['id']) 103 node.attributes['label'] = ParsePlaceholders(policy['caption']) 104 policy_enum_node.appendChild(node) 105 106 107 def main(): 108 if len(sys.argv) > 1: 109 print >>sys.stderr, 'No arguments expected!' 110 sys.stderr.write(__doc__) 111 sys.exit(1) 112 113 with open(POLICY_TEMPLATES_PATH, 'rb') as f: 114 policy_templates = literal_eval(f.read()) 115 with open(HISTOGRAMS_PATH, 'rb') as f: 116 histograms_doc = minidom.parse(f) 117 f.seek(0) 118 xml = f.read() 119 120 UpdateHistogramDefinitions(policy_templates, histograms_doc) 121 122 new_xml = PrettyPrintNode(histograms_doc) 123 if PromptUserToAcceptDiff(xml, new_xml, 'Is the updated version acceptable?'): 124 with open(HISTOGRAMS_PATH, 'wb') as f: 125 f.write(new_xml) 126 127 128 if __name__ == '__main__': 129 try: 130 main() 131 except UserError as e: 132 print >>sys.stderr, e.message 133 sys.exit(1) 134