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): | |
Alexei Svitkine (slow)
2015/02/13 14:36:37
I think we have some server-side code that mirrors
Steven Holte
2015/02/14 01:11:18
I haven't refactored any existing code to use the
| |
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 |