| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 | # Copyright (c) 2011 Google Inc. All rights reserved.# Use of this source code is governed by a BSD-style license that can be# found in the LICENSE file.import reimport osimport localedef XmlToString(content, encoding='utf-8', pretty=False):  """ Writes the XML content to disk, touching the file only if it has changed.  Visual Studio files have a lot of pre-defined structures.  This function makes  it easy to represent these structures as Python data structures, instead of  having to create a lot of function calls.  Each XML element of the content is represented as a list composed of:  1. The name of the element, a string,  2. The attributes of the element, a dictionary (optional), and  3+. The content of the element, if any.  Strings are simple text nodes and      lists are child elements.  Example 1:      <test/>  becomes      ['test']  Example 2:      <myelement a='value1' b='value2'>         <childtype>This is</childtype>         <childtype>it!</childtype>      </myelement>  becomes      ['myelement', {'a':'value1', 'b':'value2'},         ['childtype', 'This is'],         ['childtype', 'it!'],      ]  Args:    content:  The structured content to be converted.    encoding: The encoding to report on the first XML line.    pretty: True if we want pretty printing with indents and new lines.  Returns:    The XML content as a string.  """  # We create a huge list of all the elements of the file.  xml_parts = ['<?xml version="1.0" encoding="%s"?>' % encoding]  if pretty:    xml_parts.append('\n')  _ConstructContentList(xml_parts, content, pretty)  # Convert it to a string  return ''.join(xml_parts)def _ConstructContentList(xml_parts, specification, pretty, level=0):  """ Appends the XML parts corresponding to the specification.  Args:    xml_parts: A list of XML parts to be appended to.    specification:  The specification of the element.  See EasyXml docs.    pretty: True if we want pretty printing with indents and new lines.    level: Indentation level.  """  # The first item in a specification is the name of the element.  if pretty:    indentation = '  ' * level    new_line = '\n'  else:    indentation = ''    new_line = ''  name = specification[0]  if not isinstance(name, str):    raise Exception('The first item of an EasyXml specification should be '                    'a string.  Specification was ' + str(specification))  xml_parts.append(indentation + '<' + name)  # Optionally in second position is a dictionary of the attributes.  rest = specification[1:]  if rest and isinstance(rest[0], dict):    for at, val in sorted(rest[0].iteritems()):      xml_parts.append(' %s="%s"' % (at, _XmlEscape(val, attr=True)))    rest = rest[1:]  if rest:    xml_parts.append('>')    all_strings = reduce(lambda x, y: x and isinstance(y, str), rest, True)    multi_line = not all_strings    if multi_line and new_line:      xml_parts.append(new_line)    for child_spec in rest:      # If it's a string, append a text node.      # Otherwise recurse over that child definition      if isinstance(child_spec, str):       xml_parts.append(_XmlEscape(child_spec))      else:        _ConstructContentList(xml_parts, child_spec, pretty, level + 1)    if multi_line and indentation:      xml_parts.append(indentation)    xml_parts.append('</%s>%s' % (name, new_line))  else:    xml_parts.append('/>%s' % new_line)def WriteXmlIfChanged(content, path, encoding='utf-8', pretty=False,                      win32=False):  """ Writes the XML content to disk, touching the file only if it has changed.  Args:    content:  The structured content to be written.    path: Location of the file.    encoding: The encoding to report on the first line of the XML file.    pretty: True if we want pretty printing with indents and new lines.  """  xml_string = XmlToString(content, encoding, pretty)  if win32 and os.linesep != '\r\n':    xml_string = xml_string.replace('\n', '\r\n')  default_encoding = locale.getdefaultlocale()[1]  if default_encoding.upper() != encoding.upper():    xml_string = xml_string.decode(default_encoding).encode(encoding)  # Get the old content  try:    f = open(path, 'r')    existing = f.read()    f.close()  except:    existing = None  # It has changed, write it  if existing != xml_string:    f = open(path, 'w')    f.write(xml_string)    f.close()_xml_escape_map = {    '"': '"',    "'": ''',    '<': '<',    '>': '>',    '&': '&',    '\n': '
',    '\r': '
',}_xml_escape_re = re.compile(    "(%s)" % "|".join(map(re.escape, _xml_escape_map.keys())))def _XmlEscape(value, attr=False):  """ Escape a string for inclusion in XML."""  def replace(match):    m = match.string[match.start() : match.end()]    # don't replace single quotes in attrs    if attr and m == "'":      return m    return _xml_escape_map[m]  return _xml_escape_re.sub(replace, value)
 |