home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_gnome-python.idb / usr / freeware / lib / python1.5 / site-packages / pyglade / xmlparse.py.z / xmlparse.py
Encoding:
Python Source  |  1999-07-16  |  6.7 KB  |  237 lines

  1. """This file contains a simple parser for the XML output of GLADE.
  2.  
  3. There are two parsers in this file.  One uses xmllib, so will only be
  4. used if xmllib is in your python module library (it is included with
  5. python >= 1.5).  The second one is less complete, but will parse most
  6. GLADE XML files.
  7.  
  8. The second one is not a true XML parser, since it requires tags to be
  9. of the form:
  10.  
  11.   <tag>
  12.     <tag2>data</tag2>
  13.   </tag>
  14.  
  15. That is tags with embeded tags in them must have the opening tag on a single
  16. line, and the closing tag on another line by itself.  Tags with no embedded
  17. tags should have openning tag, data and closing tag all on one line.  Also
  18. tag attributes are not supported.  Yes I know this is a bit lame, but it is
  19. the minimum required for reading GLADE output.
  20.  
  21. This module is not really glade specific, except that it can read GLADE
  22. output (it can probably also read some other types of XML documents)
  23.  
  24. You should call one of read(fname), read_stream(fp) or read_string(str).
  25. The output is a tree of TagTree.  Tags of a node can be accessed either as
  26. attributes (eg node.tag) or as list items (eg node['tag']).  If there was
  27. more than one of that tag name at this level, they will be returned as a
  28. tuple by the previous two methods.
  29. """
  30.  
  31. import string
  32. import regex
  33.  
  34. # StringIO module for parsing an XML document stored in a string.  We pick
  35. # the faster cStringIO implementation if present.
  36. try:
  37.     from cStringIO import StringIO
  38. except ImportError:
  39.     from StringIO import StringIO
  40.  
  41. error = "pyglade.xmlparse.error"
  42.  
  43. comment_line = regex.compile('<\\?.*>')
  44. # data_tag must be checked first, since open_tag and close_tag will match
  45. # the same strings data_tag matches.
  46. data_tag     = regex.compile('<\([^>]+\)>\(.*\)</\([^>]+\)>')
  47. open_tag     = regex.compile('<\([^/>][^>]*\)>')
  48. close_tag    = regex.compile('</\([^>]+\)>')
  49.  
  50. class TagTree:
  51.     def __init__(self, parent, tag):
  52.         self.parent = parent
  53.         self.tag = tag
  54.         self.__tags = {}
  55.     def __getitem__(self, key):
  56.         return self.__tags[key]
  57.     __getattr__ = __getitem__
  58.     def __setitem__(self, key, value):
  59.         self.__tags[key] = value
  60.     def __len__(self):
  61.         return len(self.__tags)
  62.     def has_key(self, key):
  63.         return self.__tags.has_key(key)
  64.     def keys(self):
  65.         return self.__tags.keys()
  66.     def get(self, key, default):
  67.         if self.__tags.has_key(key):
  68.             return self.__tags[key]
  69.         else:
  70.             return default
  71.     def get_bool(self, key, default=0):
  72.         if self.__tags.has_key(key):
  73.             return string.lower(self.__tags[key]) == 'true'
  74.         else:
  75.             return default
  76.     def get_int(self, key, default=0):
  77.         if self.__tags.has_key(key):
  78.             return string.atoi(self.__tags[key])
  79.         else:
  80.             return default
  81.     def get_float(self, key, default=0.0):
  82.         if self.__tags.has_key(key):
  83.             return string.atof(self.__tags[key])
  84.         else:
  85.             return default
  86.     def destroy(self):
  87.         # This is required to break a dependency loop
  88.         del self.parent
  89.         for key in self.__tags.keys():
  90.             vals = self.__tags[key]
  91.             if type(vals) != type(()): vals = (vals,)
  92.             for val in vals:
  93.                 if hasattr(val, 'destroy'): val.destroy()
  94.             del self.__tags[key]
  95.  
  96. def read_stream(fp):
  97.     base = TagTree(parent=None, tag='XML-Base')
  98.     cstack = [base]
  99.     line = fp.readline()
  100.     while line:
  101.         if comment_line.search(line) >= 0 or line == '\n':
  102.             pass
  103.         elif data_tag.search(line) >= 0:
  104.             key = string.lower(data_tag.group(1))
  105.             data = data_tag.group(2)
  106.             end = string.lower(data_tag.group(3))
  107.             if key != end:
  108.                 raise error, "unmatching tags: %s and %s" % \
  109.                       (key, end)
  110.             if cstack[-1].has_key(key):
  111.                 oldval = cstack[-1][key]
  112.                 if type(oldval) == type(()):
  113.                     cstack[-1][key] = oldval + (data,)
  114.                 else:
  115.                     cstack[-1][key] = (oldval, data)
  116.             else:
  117.                 cstack[-1][key] = data
  118.         elif open_tag.search(line) >= 0:
  119.             key = string.lower(open_tag.group(1))
  120.             tree = TagTree(parent=cstack[-1], tag=key)
  121.             if cstack[-1].has_key(key):
  122.                 oldval = cstack[-1][key]
  123.                 if type(oldval) == type(()):
  124.                     cstack[-1][key] = oldval + (tree,)
  125.                 else:
  126.                     cstack[-1][key] = (oldval, tree)
  127.             else:
  128.                 cstack[-1][key] = tree
  129.             cstack.append(tree)
  130.         elif close_tag.search(line) >= 0:
  131.             key = string.lower(close_tag.group(1))
  132.             if not cstack:
  133.                 raise error, "no tags to match " + key
  134.             if key != cstack[-1].tag:
  135.                 raise error, \
  136.                       "unmatching container tags: %s and %s" %\
  137.                       (cstack[-1].type, key)
  138.             del cstack[-1]
  139.         else:
  140.             raise error, "unparseable line: " + line
  141.         line = fp.readline()
  142.     if len(cstack) != 1:
  143.         raise error, "some unclosed tags are present"
  144.     return base
  145.  
  146. def read(fname):
  147.     return read_stream(open(fname, "r"))
  148.  
  149. def read_string(string):
  150.     return read_stream(StringIO(string))
  151.  
  152. try:
  153.     # this is a better implementation that uses the xmllib library.
  154.     import xmllib
  155.     class myParser(xmllib.XMLParser):
  156.         def __init__(self):
  157.             xmllib.XMLParser.__init__(self)
  158.             self.base = TagTree(parent=None, tag='XML-Base')
  159.             self.cstack = [self.base]
  160.             self.curName = None
  161.             self.curData = ""
  162.         def unknown_starttag(self, tag, attrs):
  163.             tag = string.lower(tag)
  164.             if self.curName:
  165.                 cur = self.curName
  166.                 tree = TagTree(parent=self.cstack[-1],
  167.                            tag=cur)
  168.                 if self.cstack[-1].has_key(cur):
  169.                     oldval = self.cstack[-1][cur]
  170.                     if type(oldval) == type(()):
  171.                         self.cstack[-1][cur] = \
  172.                             oldval + (tree,)
  173.                     else:
  174.                         self.cstack[-1][cur] = \
  175.                             (oldval,tree)
  176.                 else:
  177.                     self.cstack[-1][cur] = tree
  178.                 self.cstack.append(tree)
  179.                 # for glade files, tags with child tags don't
  180.                 # have any data associated with them
  181.                 self.curData = ""
  182.             self.curName = tag
  183.         def unknown_endtag(self, tag):
  184.             tag = string.lower(tag)
  185.             self.curData = string.strip(self.curData)
  186.             if self.curName:
  187.                 if self.curName != tag:
  188.                     raise error, \
  189.                           "unmatching tags: %s and %s" % \
  190.                           (self.curName, tag)
  191.                 if self.cstack[-1].has_key(tag):
  192.                     oldval = self.cstack[-1][tag]
  193.                     if type(oldval) == type(()):
  194.                         self.cstack[-1][tag] = \
  195.                             oldval +(self.curData,)
  196.                     else:
  197.                         self.cstack[-1][tag] = \
  198.                             (oldval,self.curData)
  199.                 else:
  200.                     self.cstack[-1][tag] = self.curData
  201.             else:
  202.                 if not self.cstack:
  203.                     raise error, "no tags to match " + tag
  204.                 if self.cstack[-1].tag != tag:
  205.                     raise error, \
  206.                           "unmatching tags: %s and %s" % \
  207.                           (self.cstack[-1].tag, tag)
  208.                 del self.cstack[-1]
  209.             self.curName = None
  210.             self.curData = ""
  211.         def handle_data(self, data):
  212.             self.curData = self.curData + data
  213.  
  214.     def read_string(string):
  215.         parser = myParser()
  216.         parser.feed(string)
  217.         if len(parser.cstack) != 1:
  218.             raise error, "some unclosed tags are present"
  219.         return parser.base
  220.  
  221.     def read_stream(fp):
  222.         parser = myParser()
  223.         data = fp.read(8192)
  224.         while data:
  225.             parser.feed(data)
  226.             data = fp.read(8192)
  227.         if len(parser.cstack) != 1:
  228.             raise error, "some unclosed tags are present"
  229.         return parser.base
  230.  
  231.     def read(fname):
  232.         return read_stream(open(fname, "r"))
  233.  
  234. except ImportError:
  235.     pass
  236.     
  237.