1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6from writers import template_writer
7
8
9class XMLFormattedWriter(template_writer.TemplateWriter):
10  '''Helper class for generating XML-based templates.
11  '''
12
13  def AddElement(self, parent, name, attrs=None, text=None):
14    '''
15    Adds a new XML Element as a child to an existing element or the Document.
16
17    Args:
18      parent: An XML element or the document, where the new element will be
19        added.
20      name: The name of the new element.
21      attrs: A dictionary of the attributes' names and values for the new
22        element.
23      text: Text content for the new element.
24
25    Returns:
26      The created new element.
27    '''
28    if attrs == None:
29      attrs = {}
30
31    doc = parent.ownerDocument
32    element = doc.createElement(name)
33    for key, value in sorted(attrs.iteritems()):
34      element.setAttribute(key, value)
35    if text:
36      element.appendChild(doc.createTextNode(text))
37    parent.appendChild(element)
38    return element
39
40  def AddText(self, parent, text):
41    '''Adds text to a parent node.
42    '''
43    doc = parent.ownerDocument
44    parent.appendChild(doc.createTextNode(text))
45
46  def AddAttribute(self, parent, name, value):
47    '''Adds a new attribute to the parent Element. If an attribute with the
48    given name already exists then it will be replaced.
49    '''
50    doc = parent.ownerDocument
51    attribute = doc.createAttribute(name)
52    attribute.value = value
53    parent.setAttributeNode(attribute)
54
55  def AddComment(self, parent, comment):
56    '''Adds a comment node.'''
57    parent.appendChild(parent.ownerDocument.createComment(comment))
58
59  def ToPrettyXml(self, doc, **kwargs):
60    # return doc.toprettyxml(indent='  ')
61    # The above pretty-printer does not print the doctype and adds spaces
62    # around texts, e.g.:
63    #  <string>
64    #    value of the string
65    #  </string>
66    # This is problematic both for the OSX Workgroup Manager (plist files) and
67    # the Windows Group Policy Editor (admx files). What they need instead:
68    #  <string>value of string</string>
69    # So we use a hacky pretty printer here. It assumes that there are no
70    # mixed-content nodes.
71    # Get all the XML content in a one-line string.
72    xml = doc.toxml(**kwargs)
73    # Determine where the line breaks will be. (They will only be between tags.)
74    lines = xml[1:len(xml) - 1].split('><')
75    indent = ''
76    res = ''
77    # Determine indent for each line.
78    for i, line in enumerate(lines):
79      if line[0] == '/':
80        # If the current line starts with a closing tag, decrease indent before
81        # printing.
82        indent = indent[2:]
83      lines[i] = indent + '<' + line + '>'
84      if (line[0] not in ['/', '?', '!'] and '</' not in line and
85          line[len(line) - 1] != '/'):
86        # If the current line starts with an opening tag and does not conatin a
87        # closing tag, increase indent after the line is printed.
88        indent += '  '
89    # Reconstruct XML text from the lines.
90    return '\n'.join(lines)
91