Chromium Code Reviews| 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): |
|
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
|
| + """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)) |