Index: tools/metrics/common/models.py |
diff --git a/tools/metrics/common/models.py b/tools/metrics/common/models.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3ba301c4eec8bfd368f0d983c9c24c7cb47ef4aa |
--- /dev/null |
+++ b/tools/metrics/common/models.py |
@@ -0,0 +1,214 @@ |
+# Copyright 2015 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+"""Model types for describing description xml models.""" |
+ |
+from xml.dom import minidom |
+ |
+import sys |
+import os |
+ |
+import pretty_print_xml |
+ |
+ |
+def GetComments(node): |
+ """Extracts comments in the current node. |
+ |
+ Args: |
+ node: The DOM node to extract comments from. |
+ Returns: |
+ A list of comment DOM nodes. |
+ """ |
+ return [n for n in node.childNodes if n.nodeType == minidom.Node.COMMENT_NODE] |
+ |
+ |
+def PutComments(node, comments): |
+ """Append comments to the DOM node. |
+ |
+ Args: |
+ node: The DOM node to write comments to. |
+ comments: A list of comment DOM nodes. |
+ """ |
+ for n in comments: |
+ node.appendChild(n) |
+ |
+ |
+class NodeType(object): |
+ """Base type for a type of XML node. |
+ |
+ Args: |
+ dont_indent: True iff this node should not have it's children indented |
+ when pretty printing. |
+ extra_newlines: None or a triple of integers describing the number of |
+ newlines that should be printed (after_open, before_close, after_close) |
+ single_line: True iff this node may be squashed into a single line. |
+ """ |
+ def __init__(self, tag, |
+ dont_indent=False, |
+ extra_newlines=None, |
+ single_line=False): |
+ self.tag = tag |
+ self.dont_indent = dont_indent |
+ self.extra_newlines = extra_newlines |
+ self.single_line = single_line |
+ |
+ def Unmarshall(self, node): |
+ return None |
+ |
+ def Marshall(self, doc, obj): |
+ return None |
+ |
+ def GetAttributes(self): |
+ return [] |
+ |
+ def GetNodeTypes(self): |
+ return {self.tag: self} |
+ |
+ |
+class TextNodeType(NodeType): |
+ """A type for simple nodes that just have a tag and some text content. |
+ |
+ Unmarshalls nodes to strings. |
+ |
+ Args: |
+ tag: The name of XML tag for this type of node. |
+ """ |
+ def __init__(self, tag, **kwargs): |
+ NodeType.__init__(self, tag, **kwargs) |
+ |
+ def __str__(self): |
+ return 'TextNodeType("%s")' % self.tag |
+ |
+ def Unmarshall(self, node): |
+ return node.firstChild.nodeValue.strip() |
+ |
+ def Marshall(self, doc, obj): |
+ node = doc.createElement(self.tag) |
+ node.appendChild(doc.createTextNode(obj)) |
+ return node |
+ |
+ |
+class ChildType(object): |
+ """Metadata about a nodes children. |
+ |
+ Args: |
+ attr: The field name of the parents model object storing the child's model. |
+ node_type: The NodeType of the child. |
+ multiple: True if the child can be repeated. |
+ """ |
+ def __init__(self, attr, node_type, multiple): |
+ self.attr = attr |
+ self.node_type = node_type |
+ self.multiple = multiple |
+ |
+ |
+class ObjectNodeType(NodeType): |
+ """A complex node type that has attributes or other nodes as children. |
+ |
+ Unmarshalls nodes to objects. |
+ |
+ Args: |
+ tag: The name of XML tag for this type of node. |
+ int_attributes: A list of names of integer attributes. |
+ float_attributes: A list of names of float attributes. |
+ string_attributes: A list of names of string attributes. |
+ children: A list of ChildTypes describing the objects children. |
+ """ |
+ def __init__(self, tag, |
+ int_attributes=[], |
+ float_attributes=[], |
+ string_attributes=[], |
+ children=[], |
+ **kwargs): |
+ NodeType.__init__(self, tag, **kwargs) |
+ self.int_attributes = int_attributes |
+ self.float_attributes = float_attributes |
+ self.string_attributes = string_attributes |
+ self.children = children |
+ |
+ def __str__(self): |
+ return 'ObjectNodeType("%s")' % self.tag |
+ |
+ def Unmarshall(self, node): |
+ obj = {} |
+ |
+ obj['comments'] = GetComments(node) |
+ |
+ for attr in self.int_attributes: |
+ obj[attr] = int(node.getAttribute(attr)) |
+ |
+ for attr in self.float_attributes: |
+ obj[attr] = float(node.getAttribute(attr)) |
+ |
+ for attr in self.string_attributes: |
+ obj[attr] = node.getAttribute(attr) |
+ |
+ for child in self.children: |
+ nodes = node.getElementsByTagName(child.node_type.tag) |
+ if child.multiple: |
+ obj[child.attr] = [child.node_type.Unmarshall(n) for n in nodes] |
+ else: |
+ obj[child.attr] = child.node_type.Unmarshall(nodes[0]) |
+ return obj |
+ |
+ def Marshall(self, doc, obj): |
+ node = doc.createElement(self.tag) |
+ attributes = (self.int_attributes + |
+ self.float_attributes + |
+ self.string_attributes) |
+ for attr in attributes: |
+ node.setAttribute(attr, str(obj[attr])) |
+ |
+ PutComments(node, obj['comments']) |
+ |
+ for child in self.children: |
+ if child.multiple: |
+ for o in obj[child.attr]: |
+ node.appendChild(child.node_type.Marshall(doc, o)) |
+ else: |
+ node.appendChild(child.node_type.Marshall(doc, obj[child.attr])) |
+ return node |
+ |
+ def GetAttributes(self): |
+ return self.int_attributes + self.float_attributes + self.string_attributes |
+ |
+ def GetNodeTypes(self): |
+ types = {self.tag: self} |
+ for child in self.children: |
+ types.update(child.node_type.GetNodeTypes()) |
+ return types |
+ |
+ |
+class DocumentType(object): |
+ """Model for the root of an XML description file. |
+ |
+ Args: |
+ root_type: A NodeType describing the root tag of the document. |
+ """ |
+ def __init__(self, root_type): |
+ self.root_type = root_type |
+ |
+ def Parse(self, input_file): |
+ tree = minidom.parseString(input_file) |
+ comments = GetComments(tree) |
+ return comments, self.root_type.Unmarshall( |
+ tree.getElementsByTagName(self.root_type.tag)[0]) |
+ |
+ def GetPrintStyle(self): |
+ types = self.root_type.GetNodeTypes() |
+ return pretty_print_xml.XmlStyle( |
+ {t: types[t].GetAttributes() for t in types}, |
+ {t: types[t].extra_newlines for t in types if types[t].extra_newlines}, |
+ [t for t in types if types[t].dont_indent], |
+ [t for t in types if types[t].single_line]) |
+ |
+ def ToXML(self, comments, obj): |
+ doc = minidom.Document() |
+ for comment in comments: |
+ doc.appendChild(comment) |
+ doc.appendChild(self.root_type.Marshall(doc, obj)) |
+ return doc |
+ |
+ def PrettyPrint(self, comments, obj): |
+ return self.GetPrintStyle().PrettyPrintNode(self.ToXML(comments, obj)) |