root/livinglogic.python.xist/src/ll/xist/scripts/dtd2xsc.py @ 3941:dac35eb5c802

Revision 3941:dac35eb5c802, 4.8 KB (checked in by Walter Doerwald <walter@…>, 10 years ago)

Revert back to the previous way of checking input/output types in parsing pipelines.

Line 
1#! /usr/bin/env python
2# -*- coding: utf-8 -*-
3
4## Copyright 1999-2010 by LivingLogic AG, Bayreuth/Germany
5## Copyright 1999-2010 by Walter Dörwald
6##
7## All Rights Reserved
8##
9## See ll/__init__.py for the license
10
11
12"""
13Module that helps to create XIST namespace modules from DTDs. Needs xmlproc__
14
15__http://www.garshol.priv.no/download/software/xmlproc/
16
17For usage information type::
18
19    $ dtd2xsc --help
20
21"""
22
23
24__docformat__ = "reStructuredText"
25
26
27import sys, os.path, optparse
28
29try:
30    from xml.parsers.xmlproc import dtdparser
31except ImportError:
32    from xmlproc import dtdparser
33
34from ll import url
35from ll.xist import xsc, parsers, xnd
36
37
38__docformat__ = "reStructuredText"
39
40
41def getxmlns(dtd):
42    """
43    Extract the value of all fixed ``xmlns`` attributes
44    """
45    found = set()
46    for elemname in dtd.get_elements():
47        element = dtd.get_elem(elemname)
48        for attrname in element.get_attr_list():
49            attr = element.get_attr(attrname)
50            if attrname=="xmlns" or u":" in attrname:
51                if attr.decl=="#FIXED":
52                    found.add(attr.default)
53                    continue # skip a namespace declaration
54    return found
55
56
57def dtd2xnd(dtd, xmlns=None):
58    """
59    Convert DTD information from the URL :var:`dtdurl` to an XIST DOM using the
60    :mod:`ll.xist.xnd` functionality.
61    """
62
63    dtd = dtdparser.load_dtd_string(dtd)
64
65    ns = xnd.Module()
66
67    if xmlns is None:
68        # try to guess the namespace name from the dtd
69        xmlns = getxmlns(dtd)
70        if len(xmlns) == 1:
71            xmlns = iter(xmlns).next()
72        else:
73            xmlns = None
74
75    # Add element info
76    elements = dtd.get_elements()
77    elements.sort()
78    for elemname in elements:
79        dtd_e = dtd.get_elem(elemname)
80        e = xnd.Element(elemname, xmlns=xmlns)
81
82        # Add attribute info for this element
83        attrs = dtd_e.get_attr_list()
84        if len(attrs):
85            attrs.sort()
86            for attrname in attrs:
87                dtd_a = dtd_e.get_attr(attrname)
88                if attrname=="xmlns" or u":" in attrname:
89                    continue # skip namespace declarations and global attributes
90                values = []
91                if dtd_a.type == "ID":
92                    type = "xsc.IDAttr"
93                else:
94                    type = "xsc.TextAttr"
95                    if isinstance(dtd_a.type, list):
96                        if len(dtd_a.type)>1:
97                            values = dtd_a.type
98                        else:
99                            type = "xsc.BoolAttr"
100                default = dtd_a.default
101                if dtd_a.decl=="#REQUIRED":
102                    required = True
103                else:
104                    required = None
105                a = xnd.Attr(name=attrname, type=type, default=default, required=required)
106                for v in values:
107                    a.values.append(v)
108                e.attrs.append(a)
109        ns.content.append(e)
110
111    # Iterate through the elements a second time and add model information
112    for elemname in elements:
113        e = dtd.get_elem(elemname)
114        model = e.get_content_model()
115        if model is None:
116            modeltype = "sims.Any"
117            modelargs = None
118        elif model == ("", [], ""):
119            modeltype = "sims.Empty"
120            modelargs = None
121        else:
122            def extractcont(model):
123                if len(model) == 3:
124                    result = {}
125                    for cont in model[1]:
126                        result.update(extractcont(cont))
127                    return result
128                else:
129                    return {model[0]: None}
130            model = extractcont(model)
131            modeltype = "sims.Elements"
132            modelargs = []
133            for cont in model:
134                if cont == "#PCDATA":
135                    modeltype = "sims.ElementsOrText"
136                elif cont == "EMPTY":
137                    modeltype = "sims.Empty"
138                else:
139                    modelargs.append(ns.element(cont))
140            if not modelargs:
141                if modeltype == "sims.ElementsOrText":
142                    modeltype = "sims.NoElements"
143                else:
144                    modeltype = "sims.NoElementsOrText"
145        e = ns.element(elemname)
146        e.modeltype = modeltype
147        e.modelargs = modelargs
148
149    # Add entities
150    ents = dtd.get_general_entities()
151    ents.sort()
152    for entname in ents:
153        if entname not in ("quot", "apos", "gt", "lt", "amp"):
154            ent = parsers.tree(dtd.resolve_ge(entname).value | parsers.Encoder("utf-8") | parsers.SGMLOP(encoding="utf-8") | parsers.NS() | parsers.Instantiate())
155            ns.content.append(xnd.CharRef(entname, codepoint=ord(unicode(ent[0])[0])))
156
157    return ns
158
159
160def stream2xnd(stream, xmlns, shareattrs):
161    xnd = dtd2xnd(stream.read(), xmlns)
162
163    if shareattrs=="dupes":
164        xnd.shareattrs(False)
165    elif shareattrs=="all":
166        xnd.shareattrs(True)
167    return xnd
168
169
170def main(args=None):
171    p = optparse.OptionParser(usage="usage: %prog [options] <input.dtd >output_xmlns.py")
172    p.add_option("-x", "--xmlns", dest="xmlns", help="the namespace name for this module")
173    p.add_option("-s", "--shareattrs", dest="shareattrs", help="Should identical attributes be shared among elements?", choices=("none", "dupes", "all"), default="dupes")
174    p.add_option("-m", "--model", dest="model", default="once", help="Add sims information to the namespace", choices=("no", "all", "once"))
175    p.add_option("-d", "--defaults", action="store_true", dest="defaults", help="Output default values for attributes")
176
177    (options, args) = p.parse_args(args)
178    if len(args) != 0:
179        p.error("incorrect number of arguments")
180        return 1
181    print stream2xnd(sys.stdin, options.xmlns, options.shareattrs).aspy(model=options.model, defaults=options.defaults)
182
183
184if __name__ == "__main__":
185    sys.exit(main())
Note: See TracBrowser for help on using the browser.