# pylint: disable-msg=W0311,E1101,E1103,W0201,C0103,W0622,W0402,W0706,R0911,W0613,W0612,R0912,W0141,C0111,C0121

# qp_xml: Quick Parsing for XML
#
# Written by Greg Stein. Public Domain.
# No Copyright, no Rights Reserved, and no Warranties.
#
# This module is maintained by Greg and is available as part of the XML-SIG
# distribution. This module and its changelog can be fetched at:
#    http://www.lyra.org/cgi-bin/viewcvs.cgi/xml/xml/utils/qp_xml.py
#
# Additional information can be found on Greg's Python page at:
#    http://www.lyra.org/greg/python/
#
# This module was added to the XML-SIG distribution on February 14, 2000.
# As part of that distribution, it falls under the XML distribution license.
#

import string
from xml.parsers import expat


error = __name__ + '.error'


#
# The parsing class. Instantiate and pass a string/file to .parse()
#
class Parser:
  def __init__(self):
    self.reset()

  def reset(self):
    self.root = None
    self.cur_elem = None

  def find_prefix(self, prefix):
    elem = self.cur_elem
    while elem:
      if elem.ns_scope.has_key(prefix):
        return elem.ns_scope[prefix]
      elem = elem.parent

    if prefix == '':
      return ''        # empty URL for "no namespace"

    return None

  def process_prefix(self, name, use_default):
    idx = string.find(name, ':')
    if idx == -1:
      if use_default:
        return self.find_prefix(''), name
      return '', name    # no namespace

    if string.lower(name[:3]) == 'xml':
      return '', name    # name is reserved by XML. don't break out a NS.

    ns = self.find_prefix(name[:idx])
    if ns is None:
      raise error, 'namespace prefix ("%s") not found' % name[:idx]

    return ns, name[idx+1:]

  def start(self, name, attrs):
    elem = _element(name=name, lang=None, parent=None,
                    children=[], ns_scope={}, attrs={},
                    first_cdata='', following_cdata='')

    if self.cur_elem:
      elem.parent = self.cur_elem
      elem.parent.children.append(elem)
      self.cur_elem = elem
    else:
      self.cur_elem = self.root = elem

    work_attrs = [ ]

    # scan for namespace declarations (and xml:lang while we're at it)
    for name, value in attrs.items():
      if name == 'xmlns':
        elem.ns_scope[''] = value
      elif name[:6] == 'xmlns:':
        elem.ns_scope[name[6:]] = value
      elif name == 'xml:lang':
        elem.lang = value
      else:
        work_attrs.append((name, value))

    # inherit xml:lang from parent
    if elem.lang is None and elem.parent:
      elem.lang = elem.parent.lang

    # process prefix of the element name
    elem.ns, elem.name = self.process_prefix(elem.name, 1)

    # process attributes' namespace prefixes
    for name, value in work_attrs:
      elem.attrs[self.process_prefix(name, 0)] = value

  def end(self, name):
    parent = self.cur_elem.parent

    del self.cur_elem.ns_scope
    del self.cur_elem.parent

    self.cur_elem = parent

  def cdata(self, data):
    elem = self.cur_elem
    if elem.children:
      last = elem.children[-1]
      last.following_cdata = last.following_cdata + data
    else:
      elem.first_cdata = elem.first_cdata + data

  def parse(self, input):
    self.reset()

    p = expat.ParserCreate()
    p.StartElementHandler = self.start
    p.EndElementHandler = self.end
    p.CharacterDataHandler = self.cdata

    try: