OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python2.4 |
| 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 '''Class for reading GRD files into memory, without processing them. |
| 7 ''' |
| 8 |
| 9 import os.path |
| 10 import types |
| 11 import xml.sax |
| 12 import xml.sax.handler |
| 13 |
| 14 from grit import exception |
| 15 from grit.node import base |
| 16 from grit.node import mapping |
| 17 from grit.node import misc |
| 18 from grit import util |
| 19 |
| 20 |
| 21 class StopParsingException(Exception): |
| 22 '''An exception used to stop parsing.''' |
| 23 pass |
| 24 |
| 25 |
| 26 class GrdContentHandler(xml.sax.handler.ContentHandler): |
| 27 def __init__(self, |
| 28 stop_after=None, debug=False, defines=None, tags_to_ignore=None): |
| 29 # Invariant of data: |
| 30 # 'root' is the root of the parse tree being created, or None if we haven't |
| 31 # parsed out any elements. |
| 32 # 'stack' is the a stack of elements that we push new nodes onto and |
| 33 # pop from when they finish parsing, or [] if we are not currently parsing. |
| 34 # 'stack[-1]' is the top of the stack. |
| 35 self.root = None |
| 36 self.stack = [] |
| 37 self.stop_after = stop_after |
| 38 self.debug = debug |
| 39 self.defines = defines |
| 40 self.tags_to_ignore = tags_to_ignore or set() |
| 41 self.ignore_depth = 0 |
| 42 |
| 43 def startElement(self, name, attrs): |
| 44 assert not self.root or len(self.stack) > 0 |
| 45 |
| 46 if name in self.tags_to_ignore: |
| 47 self.ignore_depth += 1 |
| 48 if self.debug: |
| 49 print "Ignoring element %s and its children" % name |
| 50 |
| 51 if self.ignore_depth != 0: |
| 52 return |
| 53 |
| 54 if self.debug: |
| 55 attr_list = [] |
| 56 for attr in attrs.getNames(): |
| 57 attr_list.append('%s="%s"' % (attr, attrs.getValue(attr))) |
| 58 if len(attr_list) == 0: attr_list = ['(none)'] |
| 59 attr_list = ' '.join(attr_list) |
| 60 print ("Starting parsing of element %s with attributes %r" % |
| 61 (name, attr_list)) |
| 62 |
| 63 typeattr = None |
| 64 if 'type' in attrs.getNames(): |
| 65 typeattr = attrs.getValue('type') |
| 66 |
| 67 node = mapping.ElementToClass(name, typeattr)() |
| 68 |
| 69 if not self.root: |
| 70 self.root = node |
| 71 if self.defines: |
| 72 self.root.SetDefines(self.defines) |
| 73 |
| 74 if len(self.stack) > 0: |
| 75 self.stack[-1].AddChild(node) |
| 76 node.StartParsing(name, self.stack[-1]) |
| 77 else: |
| 78 node.StartParsing(name, None) |
| 79 |
| 80 # Push |
| 81 self.stack.append(node) |
| 82 |
| 83 for attr in attrs.getNames(): |
| 84 node.HandleAttribute(attr, attrs.getValue(attr)) |
| 85 |
| 86 def endElement(self, name): |
| 87 if self.ignore_depth == 0: |
| 88 if self.debug: |
| 89 print "End parsing of element %s" % name |
| 90 # Pop |
| 91 self.stack[-1].EndParsing() |
| 92 assert len(self.stack) > 0 |
| 93 self.stack = self.stack[:-1] |
| 94 if self.stop_after and name == self.stop_after: |
| 95 raise StopParsingException() |
| 96 |
| 97 if name in self.tags_to_ignore: |
| 98 self.ignore_depth -= 1 |
| 99 |
| 100 def characters(self, content): |
| 101 if self.ignore_depth == 0: |
| 102 if self.stack[-1]: |
| 103 self.stack[-1].AppendContent(content) |
| 104 |
| 105 def ignorableWhitespace(self, whitespace): |
| 106 # TODO(joi) This is not supported by expat. Should use a different XML par
ser? |
| 107 pass |
| 108 |
| 109 |
| 110 def Parse(filename_or_stream, dir=None, flexible_root=False, |
| 111 stop_after=None, debug=False, first_id_filename=None, |
| 112 defines=None, tags_to_ignore=None): |
| 113 '''Parses a GRD file into a tree of nodes (from grit.node). |
| 114 |
| 115 If flexible_root is False, the root node must be a <grit> element. Otherwise |
| 116 it can be any element. The "own" directory of the file will only be fixed up |
| 117 if the root node is a <grit> element. |
| 118 |
| 119 'dir' should point to the directory of the input file, or be the full path |
| 120 to the input file (the filename will be stripped). |
| 121 |
| 122 If 'stop_after' is provided, the parsing will stop once the first node |
| 123 with this name has been fully parsed (including all its contents). |
| 124 |
| 125 If 'debug' is true, lots of information about the parsing events will be |
| 126 printed out during parsing of the file. |
| 127 |
| 128 If first_id_filename is provided, then we use the provided path instead of |
| 129 resources_id to gather the first id values for resources. |
| 130 |
| 131 Args: |
| 132 filename_or_stream: './bla.xml' (must be filename if dir is None) |
| 133 dir: '.' or None (only if filename_or_stream is a filename) |
| 134 flexible_root: True | False |
| 135 stop_after: 'inputs' |
| 136 debug: False |
| 137 first_id_filename: None |
| 138 defines: dictionary of defines, like {'chromeos': '1'} |
| 139 |
| 140 Return: |
| 141 Subclass of grit.node.base.Node |
| 142 |
| 143 Throws: |
| 144 grit.exception.Parsing |
| 145 ''' |
| 146 handler = GrdContentHandler(stop_after=stop_after, debug=debug, |
| 147 defines=defines, tags_to_ignore=tags_to_ignore) |
| 148 try: |
| 149 xml.sax.parse(filename_or_stream, handler) |
| 150 except StopParsingException: |
| 151 assert stop_after |
| 152 pass |
| 153 except: |
| 154 if not debug: |
| 155 print "parse exception: run GRIT with the -x flag to debug .grd problems" |
| 156 raise |
| 157 |
| 158 if not flexible_root or hasattr(handler.root, 'SetOwnDir'): |
| 159 assert isinstance(filename_or_stream, types.StringType) or dir != None |
| 160 if not dir: |
| 161 dir = util.dirname(filename_or_stream) |
| 162 if len(dir) == 0: |
| 163 dir = '.' |
| 164 # Fix up the base_dir so it is relative to the input file. |
| 165 handler.root.SetOwnDir(dir) |
| 166 |
| 167 # Assign first ids to the nodes that don't have them. |
| 168 if isinstance(handler.root, misc.GritNode) and first_id_filename != '': |
| 169 handler.root.AssignFirstIds(filename_or_stream, first_id_filename, defines) |
| 170 |
| 171 return handler.root |
| 172 |
| 173 |
| 174 if __name__ == '__main__': |
| 175 util.ChangeStdoutEncoding() |
| 176 print unicode(Parse(sys.argv[1])) |
OLD | NEW |