root/livinglogic.python.xist/src/ll/xist/scripts/dtd2xsc.py @ 4422:fe09ca906d4e

Revision 4422:fe09ca906d4e, 5.2 KB (checked in by Walter Doerwald <walter@…>, 9 years ago)

Bump copyright year. Change encoding of remaining files to UTF-8. Remove trailing whitespace.

Line 
1#! /usr/bin/env python
2# -*- coding: utf-8 -*-
3
4## Copyright 1999-2011 by LivingLogic AG, Bayreuth/Germany
5## Copyright 1999-2011 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, argparse, cStringIO
28
29try:
30    from xml.parsers.xmlproc import dtdparser
31except ImportError:
32    from xmlproc import dtdparser
33
34from ll import misc, url
35from ll.xist import xsc, parse, 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 adddtd2xnd(ns, dtd):
58    """
59    Append DTD information from :var:`dtd` to the :class:`xnd.Module` object
60    :var:`ns`.
61    """
62
63    dtd = dtdparser.load_dtd_string(dtd)
64
65    # try to guess the namespace name from the dtd
66    xmlns = getxmlns(dtd)
67    if len(xmlns) == 1:
68        xmlns = iter(xmlns).next()
69    else:
70        xmlns = None
71
72    # Add element info
73    elements = dtd.get_elements()
74    elements.sort()
75    for elemname in elements:
76        dtd_e = dtd.get_elem(elemname)
77        e = xnd.Element(elemname, xmlns=xmlns)
78
79        # Add attribute info for this element
80        attrs = dtd_e.get_attr_list()
81        if len(attrs):
82            attrs.sort()
83            for attrname in attrs:
84                dtd_a = dtd_e.get_attr(attrname)
85                if attrname=="xmlns" or u":" in attrname:
86                    continue # skip namespace declarations and global attributes
87                values = []
88                if dtd_a.type == "ID":
89                    type = "xsc.IDAttr"
90                else:
91                    type = "xsc.TextAttr"
92                    if isinstance(dtd_a.type, list):
93                        if len(dtd_a.type) > 1:
94                            values = dtd_a.type
95                        else:
96                            type = "xsc.BoolAttr"
97                default = dtd_a.default
98                if dtd_a.decl=="#REQUIRED":
99                    required = True
100                else:
101                    required = None
102                e += xnd.Attr(name=attrname, type=type, default=default, required=required, values=values)
103        ns += e
104
105    # Iterate through the elements a second time and add model information
106    for elemname in elements:
107        e = dtd.get_elem(elemname)
108        model = e.get_content_model()
109        if model is None:
110            modeltype = "sims.Any"
111            modelargs = None
112        elif model == ("", [], ""):
113            modeltype = "sims.Empty"
114            modelargs = None
115        else:
116            def extractcont(model):
117                if len(model) == 3:
118                    result = {}
119                    for cont in model[1]:
120                        result.update(extractcont(cont))
121                    return result
122                else:
123                    return {model[0]: None}
124            model = extractcont(model)
125            modeltype = "sims.Elements"
126            modelargs = []
127            for cont in model:
128                if cont == "#PCDATA":
129                    modeltype = "sims.ElementsOrText"
130                elif cont == "EMPTY":
131                    modeltype = "sims.Empty"
132                else:
133                    modelargs.append(ns.elements[(cont, xmlns)])
134            if not modelargs:
135                if modeltype == "sims.ElementsOrText":
136                    modeltype = "sims.NoElements"
137                else:
138                    modeltype = "sims.NoElementsOrText"
139        e = ns.elements[(elemname, xmlns)]
140        if ns.model == "simple":
141            modeltype = modeltype == "sims.Empty"
142            modelargs = None
143        e.modeltype = modeltype
144        e.modelargs = modelargs
145
146    # Add entities
147    ents = dtd.get_general_entities()
148    ents.sort()
149    for entname in ents:
150        if entname not in ("quot", "apos", "gt", "lt", "amp"):
151            try:
152                ent = parse.tree(dtd.resolve_ge(entname).value, parse.Encoder("utf-8"), parse.SGMLOP(encoding="utf-8"), parse.NS(), parse.Node())
153            except xsc.IllegalEntityError:
154                pass
155            else:
156                ns += xnd.CharRef(entname, codepoint=ord(unicode(ent[0])[0]))
157
158
159def urls2xnd(urls, shareattrs=None, **kwargs):
160    ns = xnd.Module(**kwargs)
161    with url.Context():
162        for u in urls:
163            if isinstance(u, url.URL):
164                u = u.openread()
165            elif isinstance(u, str):
166                u = cStringIO.StringIO(u)
167            adddtd2xnd(ns, u.read())
168
169    if shareattrs=="dupes":
170        ns.shareattrs(False)
171    elif shareattrs=="all":
172        ns.shareattrs(True)
173    return ns
174
175
176def main(args=None):
177    p = argparse.ArgumentParser(description="Convert DTDs to XIST namespace (on stdout)")
178    p.add_argument("urls", metavar="urls", type=url.URL, help="ULRs of DTDs to be parsed", nargs="+")
179    p.add_argument("-x", "--xmlns", dest="defaultxmlns", metavar="NAME", help="the namespace name for this module")
180    p.add_argument("-s", "--shareattrs", dest="shareattrs", help="Should identical attributes be shared among elements? (default: %(default)s)", choices=("none", "dupes", "all"), default="dupes")
181    p.add_argument("-m", "--model", dest="model", default="once", help="Add sims information to the namespace (default: %(default)s)", choices=("no", "simple", "fullall", "fullonce"))
182    p.add_argument("-d", "--defaults", dest="defaults", help="Output default values for attributes? (default: %(default)s)", action=misc.FlagAction, default=False)
183    p.add_argument(      "--duplicates", dest="duplicates", help="How to handle duplicate elements from multiple DTDs (default: %(default)s)", choices=("reject", "allow", "merge"), default="reject")
184
185    args = p.parse_args(args)
186    print urls2xnd(args.urls, **args.__dict__)
187
188
189if __name__ == "__main__":
190    sys.exit(main())
Note: See TracBrowser for help on using the browser.