#! /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")
