| 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)
 | 
| 
 |