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 |