| OLD | NEW |
| (Empty) |
| 1 # Copyright 2015 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 """Model types for describing description xml models.""" | |
| 6 | |
| 7 from xml.dom import minidom | |
| 8 | |
| 9 import sys | |
| 10 import os | |
| 11 | |
| 12 import pretty_print_xml | |
| 13 | |
| 14 | |
| 15 def GetComments(node): | |
| 16 """Extracts comments in the current node. | |
| 17 | |
| 18 Args: | |
| 19 node: The DOM node to extract comments from. | |
| 20 Returns: | |
| 21 A list of comment DOM nodes. | |
| 22 """ | |
| 23 return [n for n in node.childNodes if n.nodeType == minidom.Node.COMMENT_NODE] | |
| 24 | |
| 25 | |
| 26 def PutComments(node, comments): | |
| 27 """Append comments to the DOM node. | |
| 28 | |
| 29 Args: | |
| 30 node: The DOM node to write comments to. | |
| 31 comments: A list of comment DOM nodes. | |
| 32 """ | |
| 33 for n in comments: | |
| 34 node.appendChild(n) | |
| 35 | |
| 36 | |
| 37 class NodeType(object): | |
| 38 """Base type for a type of XML node. | |
| 39 | |
| 40 Args: | |
| 41 dont_indent: True iff this node should not have it's children indented | |
| 42 when pretty printing. | |
| 43 extra_newlines: None or a triple of integers describing the number of | |
| 44 newlines that should be printed (after_open, before_close, after_close) | |
| 45 single_line: True iff this node may be squashed into a single line. | |
| 46 """ | |
| 47 def __init__(self, tag, | |
| 48 dont_indent=False, | |
| 49 extra_newlines=None, | |
| 50 single_line=False): | |
| 51 self.tag = tag | |
| 52 self.dont_indent = dont_indent | |
| 53 self.extra_newlines = extra_newlines | |
| 54 self.single_line = single_line | |
| 55 | |
| 56 def Unmarshall(self, node): | |
| 57 return None | |
| 58 | |
| 59 def Marshall(self, doc, obj): | |
| 60 return None | |
| 61 | |
| 62 def GetAttributes(self): | |
| 63 return [] | |
| 64 | |
| 65 def GetNodeTypes(self): | |
| 66 return {self.tag: self} | |
| 67 | |
| 68 | |
| 69 class TextNodeType(NodeType): | |
| 70 """A type for simple nodes that just have a tag and some text content. | |
| 71 | |
| 72 Unmarshalls nodes to strings. | |
| 73 | |
| 74 Args: | |
| 75 tag: The name of XML tag for this type of node. | |
| 76 """ | |
| 77 def __init__(self, tag, **kwargs): | |
| 78 NodeType.__init__(self, tag, **kwargs) | |
| 79 | |
| 80 def __str__(self): | |
| 81 return 'TextNodeType("%s")' % self.tag | |
| 82 | |
| 83 def Unmarshall(self, node): | |
| 84 return node.firstChild.nodeValue.strip() | |
| 85 | |
| 86 def Marshall(self, doc, obj): | |
| 87 node = doc.createElement(self.tag) | |
| 88 node.appendChild(doc.createTextNode(obj)) | |
| 89 return node | |
| 90 | |
| 91 | |
| 92 class ChildType(object): | |
| 93 """Metadata about a nodes children. | |
| 94 | |
| 95 Args: | |
| 96 attr: The field name of the parents model object storing the child's model. | |
| 97 node_type: The NodeType of the child. | |
| 98 multiple: True if the child can be repeated. | |
| 99 """ | |
| 100 def __init__(self, attr, node_type, multiple): | |
| 101 self.attr = attr | |
| 102 self.node_type = node_type | |
| 103 self.multiple = multiple | |
| 104 | |
| 105 | |
| 106 class ObjectNodeType(NodeType): | |
| 107 """A complex node type that has attributes or other nodes as children. | |
| 108 | |
| 109 Unmarshalls nodes to objects. | |
| 110 | |
| 111 Args: | |
| 112 tag: The name of XML tag for this type of node. | |
| 113 int_attributes: A list of names of integer attributes. | |
| 114 float_attributes: A list of names of float attributes. | |
| 115 string_attributes: A list of names of string attributes. | |
| 116 children: A list of ChildTypes describing the objects children. | |
| 117 """ | |
| 118 def __init__(self, tag, | |
| 119 int_attributes=[], | |
| 120 float_attributes=[], | |
| 121 string_attributes=[], | |
| 122 children=[], | |
| 123 **kwargs): | |
| 124 NodeType.__init__(self, tag, **kwargs) | |
| 125 self.int_attributes = int_attributes | |
| 126 self.float_attributes = float_attributes | |
| 127 self.string_attributes = string_attributes | |
| 128 self.children = children | |
| 129 | |
| 130 def __str__(self): | |
| 131 return 'ObjectNodeType("%s")' % self.tag | |
| 132 | |
| 133 def Unmarshall(self, node): | |
| 134 obj = {} | |
| 135 | |
| 136 obj['comments'] = GetComments(node) | |
| 137 | |
| 138 for attr in self.int_attributes: | |
| 139 obj[attr] = int(node.getAttribute(attr)) | |
| 140 | |
| 141 for attr in self.float_attributes: | |
| 142 obj[attr] = float(node.getAttribute(attr)) | |
| 143 | |
| 144 for attr in self.string_attributes: | |
| 145 obj[attr] = node.getAttribute(attr) | |
| 146 | |
| 147 for child in self.children: | |
| 148 nodes = node.getElementsByTagName(child.node_type.tag) | |
| 149 if child.multiple: | |
| 150 obj[child.attr] = [child.node_type.Unmarshall(n) for n in nodes] | |
| 151 else: | |
| 152 obj[child.attr] = child.node_type.Unmarshall(nodes[0]) | |
| 153 return obj | |
| 154 | |
| 155 def Marshall(self, doc, obj): | |
| 156 node = doc.createElement(self.tag) | |
| 157 attributes = (self.int_attributes + | |
| 158 self.float_attributes + | |
| 159 self.string_attributes) | |
| 160 for attr in attributes: | |
| 161 node.setAttribute(attr, str(obj[attr])) | |
| 162 | |
| 163 PutComments(node, obj['comments']) | |
| 164 | |
| 165 for child in self.children: | |
| 166 if child.multiple: | |
| 167 for o in obj[child.attr]: | |
| 168 node.appendChild(child.node_type.Marshall(doc, o)) | |
| 169 else: | |
| 170 node.appendChild(child.node_type.Marshall(doc, obj[child.attr])) | |
| 171 return node | |
| 172 | |
| 173 def GetAttributes(self): | |
| 174 return self.int_attributes + self.float_attributes + self.string_attributes | |
| 175 | |
| 176 def GetNodeTypes(self): | |
| 177 types = {self.tag: self} | |
| 178 for child in self.children: | |
| 179 types.update(child.node_type.GetNodeTypes()) | |
| 180 return types | |
| 181 | |
| 182 | |
| 183 class DocumentType(object): | |
| 184 """Model for the root of an XML description file. | |
| 185 | |
| 186 Args: | |
| 187 root_type: A NodeType describing the root tag of the document. | |
| 188 """ | |
| 189 def __init__(self, root_type): | |
| 190 self.root_type = root_type | |
| 191 | |
| 192 def Parse(self, input_file): | |
| 193 tree = minidom.parseString(input_file) | |
| 194 comments = GetComments(tree) | |
| 195 return comments, self.root_type.Unmarshall( | |
| 196 tree.getElementsByTagName(self.root_type.tag)[0]) | |
| 197 | |
| 198 def GetPrintStyle(self): | |
| 199 types = self.root_type.GetNodeTypes() | |
| 200 return pretty_print_xml.XmlStyle( | |
| 201 {t: types[t].GetAttributes() for t in types}, | |
| 202 {t: types[t].extra_newlines for t in types if types[t].extra_newlines}, | |
| 203 [t for t in types if types[t].dont_indent], | |
| 204 [t for t in types if types[t].single_line]) | |
| 205 | |
| 206 def ToXML(self, comments, obj): | |
| 207 doc = minidom.Document() | |
| 208 for comment in comments: | |
| 209 doc.appendChild(comment) | |
| 210 doc.appendChild(self.root_type.Marshall(doc, obj)) | |
| 211 return doc | |
| 212 | |
| 213 def PrettyPrint(self, comments, obj): | |
| 214 return self.GetPrintStyle().PrettyPrintNode(self.ToXML(comments, obj)) | |
| OLD | NEW |