text: #! /usr/bin/env python
  # Copyright (c) 2008, PediaPress GmbH
  # See README.txt for additional licensing information.
  """
  incomplete implementation for reading relax ng content models
  
  rng tutorial:
  http://www.oasis-open.org/committees/relax-ng/tutorial-20011203.html
  
  implemented:
   allowed attributes
   allowed (direct) children
  
  not implemented:
    anything else
  
  basic usage:
  ./build_rng_grammar.py docbook-rng-4-4/docbook.rng > docbook44grammar.py
  """
  
  import os
  import sys
  from lxml import etree as ET
  
  ns = "{http://relaxng.org/ns/structure/1.0}"
  
  def clean(r):
      # clean the damn ns
      try:
          n = ET.Element(str(r.tag).replace(ns, ""))
      except ValueError:
          return 
      n.attrib.update(dict(r.attrib.items()))
      for c in r:
          cc = clean(c)
          if cc is not None:
              n.append(cc)
      return n
  
  
  def cached(method):
      def f(self, *args, **kargs):
          id = "%s%r%r" % (method.__name__, args, kargs)
          #print id
          if id in self.cache:
              return self.cache[id]
          e = method(self, *args, **kargs)
          self.cache[id] = e
          return e
      return f
  
  
  class ContentModel(object):
      debug = False
  
      def __init__(self, fn):
          self.tree = self._load(fn)
          self.cache = {}
  
      def _load(self, fn):
          t = ET.parse(open(fn))
          for e in t.iterfind(".//%sinclude" % ns):
              nfn =  os.path.join(os.path.dirname(os.path.abspath(fn)),e.get("href")) # FIXME
              e.getparent().replace(e, self._load(nfn))
          return clean(t.getroot())
  
  
      def _find_allowed(self, element_name, objective="element"):
          # searching for "object" and their "name" attribute
          # look for optional, zeroOrMore, oneOrMore, group and derferece ref
          valid = set()
          assert objective in ("element", "attribute")
  
          def collect(e):
              #print "collect", e, e.attrib
              if e.tag == objective:
                  valid.add(e.attrib.get("name"))
              elif e.tag == "ref":
                  if self.debug: print "ref", e, e.attrib
                  s = list(self.tree.iterfind(".//define[@name='%s']" % e.get("name")))
                  if self.debug: print "found", s
                  for x in s:
                      collect(x)
              elif e.tag in ("zeroOrMore", "oneOrMore", "optional", "choice", "define", "group"):
                  for c in e.getchildren():
                      collect(c)
              else:
                  if self.debug: print "breaking", e
                  pass # do nothing
                  
          for e in self.tree.iterfind(".//element[@name='%s']" % element_name):
              for c in e.getchildren():
                  collect(c)
          return list(valid)
  
  
      @cached
      def child_elements(self, element_name):
          return self._find_allowed(element_name, "element")
  
      @cached
      def attributes(self, element_name):
          return self._find_allowed(element_name, "attribute")
  
  
  def dump():
      fn = sys.argv[1]
  
      # dump static grammar for used elements in docbookwriter.py
      used_elements = ['article','articleinfo','blockquote',
                       'book', 'bookinfo','caption',
                       'chapter', 'chapterinfo','citation',
                       'em','emphasis', 'imagedata',
                       'imageobject','informaltable','inlinemediaobject',
                       'itemizedlist','listitem', 'literallayout',
                       'math', 'mediaobject','object',
                       'orderedlist','para','phrase',
                       'programlisting','quote','section',
                       'sectioninfo','span','subscript',
                       'superscript', 'td','term','title',
                       'tr','ulink','variablelist',           
                       'varlistentry','xref'] # extend this list if docbookwriter.py uses new elements
  
      cm = ContentModel(fn)
      grammar = {}
      for e in used_elements:
          grammar[e] = {"attributes": cm.attributes(e),
  #                      "children": [c for c in cm.child_elements(e) if c in used_elements],
                        "children": cm.child_elements(e)
                        }
  
      assert "url" in grammar["ulink"]["attributes"]
      print "# autogenerated grammar from file %s using: %s" % (fn, sys.argv[0])
      print "grammar = %r" % grammar
      
      
  def test(fn):
      cm = ContentModel(sys.argv[1])
      print cm.child_elements("para")
      print cm.attributes("para")
      print cm.child_elements("article")
      print cm.attributes("article")
      print cm.child_elements("article")
      print cm.attributes("article")
  
  
  def check(element):
      cm = ContentModel(sys.argv[1])
      cm.debug = True
      print "childelements"
      print cm.child_elements(element)
      print "attributes"
      print cm.attributes(element)
      
  if __name__ == "__main__":
      dump()
      #check("ulink")
  