| Index: tools/grit/grit/gather/policy_json.py
|
| diff --git a/tools/grit/grit/gather/policy_json.py b/tools/grit/grit/gather/policy_json.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..cd7875fffcabe4447d887e078bc4c50774fcba33
|
| --- /dev/null
|
| +++ b/tools/grit/grit/gather/policy_json.py
|
| @@ -0,0 +1,263 @@
|
| +#!/usr/bin/python2.4
|
| +# Copyright (c) 2011 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.
|
| +
|
| +'''Support for "policy_templates.json" format used by the policy template
|
| +generator as a source for generating ADM,ADMX,etc files.'''
|
| +
|
| +import types
|
| +import pprint
|
| +import re
|
| +
|
| +from grit.gather import skeleton_gatherer
|
| +from grit import util
|
| +from grit import tclib
|
| +from xml.dom import minidom
|
| +
|
| +
|
| +class PolicyJson(skeleton_gatherer.SkeletonGatherer):
|
| + '''Collects and translates the following strings from policy_templates.json:
|
| + - captions,descriptions and labels of policies
|
| + - captions of enumeration items
|
| + - misc strings from the 'messages' section
|
| + Translatable strings may have untranslateable placeholders with the same
|
| + format that is used in .grd files.
|
| + '''
|
| +
|
| + def __init__(self, text):
|
| + if util.IsExtraVerbose():
|
| + print text
|
| + skeleton_gatherer.SkeletonGatherer.__init__(self)
|
| + self.text_ = text
|
| +
|
| + def _ParsePlaceholder(self, placeholder, msg):
|
| + '''Extracts a placeholder from a DOM node and adds it to a tclib Message.
|
| +
|
| + Args:
|
| + placeholder: A DOM node of the form:
|
| + <ph name="PLACEHOLDER_NAME">Placeholder text<ex>Example value</ex></ph>
|
| + msg: The placeholder is added to this message.
|
| + '''
|
| + text = []
|
| + example_text = []
|
| + for node1 in placeholder.childNodes:
|
| + if (node1.nodeType == minidom.Node.TEXT_NODE):
|
| + text.append(node1.data)
|
| + elif (node1.nodeType == minidom.Node.ELEMENT_NODE and
|
| + node1.tagName == 'ex'):
|
| + for node2 in node1.childNodes:
|
| + example_text.append(node2.toxml())
|
| + else:
|
| + raise Exception('Unexpected element inside a placeholder: ' +
|
| + node2.toxml())
|
| + if example_text == []:
|
| + # In such cases the original text is okay for an example.
|
| + example_text = text
|
| + msg.AppendPlaceholder(tclib.Placeholder(
|
| + placeholder.attributes['name'].value,
|
| + ''.join(text).strip(),
|
| + ''.join(example_text).strip()))
|
| +
|
| + def _ParseMessage(self, string, desc):
|
| + '''Parses a given string and adds it to the output as a translatable chunk
|
| + with a given description.
|
| +
|
| + Args:
|
| + string: The message string to parse.
|
| + desc: The description of the message (for the translators).
|
| + '''
|
| + msg = tclib.Message(description=desc)
|
| + xml = '<msg>' + string + '</msg>'
|
| + node = minidom.parseString(xml).childNodes[0]
|
| + for child in node.childNodes:
|
| + if child.nodeType == minidom.Node.TEXT_NODE:
|
| + msg.AppendText(child.data)
|
| + elif child.nodeType == minidom.Node.ELEMENT_NODE:
|
| + if child.tagName == 'ph':
|
| + self._ParsePlaceholder(child, msg)
|
| + else:
|
| + raise Exception("Not implemented.")
|
| + else:
|
| + raise Exception("Not implemented.")
|
| + self.skeleton_.append(self.uberclique.MakeClique(msg))
|
| +
|
| + def _ParseNode(self, node):
|
| + '''Traverses the subtree of a DOM node, and register a tclib message for
|
| + all the <message> nodes.
|
| + '''
|
| + att_text = []
|
| + if node.attributes:
|
| + items = node.attributes.items()
|
| + items.sort()
|
| + for key, value in items:
|
| + att_text.append(' %s=\"%s\"' % (key, value))
|
| + self._AddNontranslateableChunk("<%s%s>" %
|
| + (node.tagName, ''.join(att_text)))
|
| + if node.tagName == 'message':
|
| + msg = tclib.Message(description=node.attributes['desc'])
|
| + for child in node.childNodes:
|
| + if child.nodeType == minidom.Node.TEXT_NODE:
|
| + if msg == None:
|
| + self._AddNontranslateableChunk(child.data)
|
| + else:
|
| + msg.AppendText(child.data)
|
| + elif child.nodeType == minidom.Node.ELEMENT_NODE:
|
| + if child.tagName == 'ph':
|
| + self._ParsePlaceholder(child, msg)
|
| + else:
|
| + assert False
|
| + self.skeleton_.append(self.uberclique.MakeClique(msg))
|
| + else:
|
| + for child in node.childNodes:
|
| + if child.nodeType == minidom.Node.TEXT_NODE:
|
| + self._AddNontranslateableChunk(child.data)
|
| + elif node.nodeType == minidom.Node.ELEMENT_NODE:
|
| + self._ParseNode(child)
|
| +
|
| + self._AddNontranslateableChunk("</%s>" % node.tagName)
|
| +
|
| + def _AddIndentedNontranslateableChunk(self, depth, string):
|
| + '''Adds a nontranslateable chunk of text to the internally stored output.
|
| +
|
| + Args:
|
| + depth: The number of double spaces to prepend to the next argument string.
|
| + string: The chunk of text to add.
|
| + '''
|
| + result = []
|
| + while depth > 0:
|
| + result.append(' ')
|
| + depth = depth - 1
|
| + result.append(string)
|
| + self._AddNontranslateableChunk(''.join(result))
|
| +
|
| + def _GetDescription(self, item, item_type, parent_item, key):
|
| + '''Creates a description for a translatable message. The description gives
|
| + some context for the person who will translate this message.
|
| +
|
| + Args:
|
| + item: A policy or an enumeration item.
|
| + item_type: 'enum_item' | 'policy'
|
| + parent_item: The owner of item. (A policy of type group or enum.)
|
| + key: The name of the key to parse.
|
| + depth: The level of indentation.
|
| + '''
|
| + key_map = {
|
| + 'desc': 'Description',
|
| + 'caption': 'Caption',
|
| + 'label': 'Label',
|
| + }
|
| + if type == 'policy':
|
| + return '%s of the policy named %s' % (key_map[key], item['name'])
|
| + elif type == 'enum_item':
|
| + return ('%s of the option named %s in policy %s' %
|
| + (key_map[key], item['name'], parent_item['name']))
|
| +
|
| + def _AddPolicyKey(self, item, item_type, parent_item, key, depth):
|
| + '''Given a policy/enumeration item and a key, adds that key and its value
|
| + into the output.
|
| + E.g.:
|
| + 'example_value': 123
|
| + If key indicates that the value is a translatable string, then it is parsed
|
| + as a translatable string.
|
| +
|
| + Args:
|
| + item: A policy or an enumeration item.
|
| + item_type: 'enum_item' | 'policy'
|
| + parent_item: The owner of item. (A policy of type group or enum.)
|
| + key: The name of the key to parse.
|
| + depth: The level of indentation.
|
| + '''
|
| + self._AddIndentedNontranslateableChunk(depth, "'%s': " % key)
|
| + if key in ('desc', 'caption', 'label'):
|
| + self._AddNontranslateableChunk("'''")
|
| + self._ParseMessage(
|
| + item[key],
|
| + self._GetDescription(item, item_type, parent_item, key))
|
| + self._AddNontranslateableChunk("''',\n")
|
| + else:
|
| + str_val = item[key]
|
| + if type(str_val) == types.StringType:
|
| + str_val = "'%s'" % str_val
|
| + else:
|
| + str_val = str(str_val)
|
| + self._AddNontranslateableChunk(str_val + ',\n')
|
| +
|
| + def _AddItems(self, items, item_type, parent_item, depth):
|
| + '''Parses and adds a list of items from the JSON file. Items can be policies
|
| + or parts of an enum policy.
|
| +
|
| + Args:
|
| + items: Either a list of policies or a list of dictionaries.
|
| + item_type: 'enum_item' | 'policy'
|
| + parent_item: If items contains a list of policies, then this is the policy
|
| + group that owns them. If items contains a list of enumeration items,
|
| + then this is the enum policy that holds them.
|
| + depth: Indicates the depth of our position in the JSON hierarchy. Used to
|
| + add nice line-indent to the output.
|
| + '''
|
| + for item1 in items:
|
| + self._AddIndentedNontranslateableChunk(depth, "{\n")
|
| + for key in item1.keys():
|
| + if key == 'items':
|
| + self._AddIndentedNontranslateableChunk(depth + 1, "'items': [\n")
|
| + self._AddItems(item1['items'], 'enum_item', item1, depth + 2)
|
| + self._AddIndentedNontranslateableChunk(depth + 1, "],\n")
|
| + elif key == 'policies':
|
| + self._AddIndentedNontranslateableChunk(depth + 1, "'policies': [\n")
|
| + self._AddItems(item1['policies'], 'policy', item1, depth + 2)
|
| + self._AddIndentedNontranslateableChunk(depth + 1, "],\n")
|
| + else:
|
| + self._AddPolicyKey(item1, item_type, parent_item, key, depth + 1)
|
| + self._AddIndentedNontranslateableChunk(depth, "},\n")
|
| +
|
| + def _AddMessages(self):
|
| + '''Processed and adds the 'messages' section to the output.'''
|
| + self._AddNontranslateableChunk(" 'messages': {\n")
|
| + for name, message in self.data['messages'].iteritems():
|
| + self._AddNontranslateableChunk(" '%s': {\n" % name)
|
| + self._AddNontranslateableChunk(" 'text': '''")
|
| + self._ParseMessage(message['text'], message['desc'])
|
| + self._AddNontranslateableChunk("'''\n")
|
| + self._AddNontranslateableChunk(" },\n")
|
| + self._AddNontranslateableChunk(" },\n")
|
| +
|
| + def _AddPlaceholders(self):
|
| + '''Adds the 'placeholders' section to the output.'''
|
| + self._AddNontranslateableChunk(" 'placeholders':\n")
|
| + self._AddNontranslateableChunk(
|
| + pprint.pformat(self.data['placeholders'], indent=2, depth=2))
|
| +
|
| + # Although we use the RegexpGatherer base class, we do not use the
|
| + # _RegExpParse method of that class to implement Parse(). Instead, we
|
| + # parse using a DOM parser.
|
| + def Parse(self):
|
| + if (self.have_parsed_):
|
| + return
|
| + self.have_parsed_ = True
|
| +
|
| + self.data = eval(self.text_)
|
| +
|
| + self._AddNontranslateableChunk('{\n')
|
| + self._AddNontranslateableChunk(" 'policy_definitions': [\n")
|
| + self._AddItems(self.data['policy_definitions'], 'policy', None, 2)
|
| + self._AddNontranslateableChunk(" ],\n")
|
| + self._AddMessages()
|
| + self._AddPlaceholders()
|
| + self._AddNontranslateableChunk('\n}')
|
| +
|
| + def Escape(self, text):
|
| + # \ -> \\
|
| + # ' -> \'
|
| + # " -> \"
|
| + return text.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
|
| +
|
| + def FromFile(filename_or_stream, extkey=None, encoding='cp1252'):
|
| + if isinstance(filename_or_stream, types.StringTypes):
|
| + if util.IsVerbose():
|
| + print "PolicyJson reading file %s, encoding %s" % (
|
| + filename_or_stream, encoding)
|
| + filename_or_stream = \
|
| + util.WrapInputStream(file(filename_or_stream, 'r'), encoding)
|
| + return PolicyJson(filename_or_stream.read())
|
| + FromFile = staticmethod(FromFile)
|
|
|