root/livinglogic.python.xist/src/ll/xist/scripts/dtd2xsc.py @ 3263:01981277eea0

Revision 3263:01981277eea0, 4.7 KB (checked in by Walter Doerwald <walter@…>, 11 years ago)

Put the license in the topmost init.py.

Line 
1#! /usr/bin/env python
2# -*- coding: utf-8 -*-
3
4## Copyright 1999-2008 by LivingLogic AG, Bayreuth/Germany
5## Copyright 1999-2008 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
29from xml.parsers.xmlproc import dtdparser
30
31from ll import url
32from ll.xist import xsc, parsers, xnd
33
34
35__docformat__ = "reStructuredText"
36
37
38def getxmlns(dtd):
39    """
40    Extract the value of all fixed ``xmlns`` attributes
41    """
42    found = set()
43    for elemname in dtd.get_elements():
44        element = dtd.get_elem(elemname)
45        for attrname in element.get_attr_list():
46            attr = element.get_attr(attrname)
47            if attrname=="xmlns" or u":" in attrname:
48                if attr.decl=="#FIXED":
49                    found.add(attr.default)
50                    continue # skip a namespace declaration
51    return found
52
53
54def dtd2xnd(dtd, xmlns=None):
55    """
56    Convert DTD information from the URL :var:`dtdurl` to an XIST DOM using the
57    :mod:`ll.xist.xnd` functionality.
58    """
59
60    dtd = dtdparser.load_dtd_string(dtd)
61
62    ns = xnd.Module()
63
64    if xmlns is None:
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                a = xnd.Attr(name=attrname, type=type, default=default, required=required)
103                for v in values:
104                    a.values.append(v)
105                e.attrs.append(a)
106        ns.content.append(e)
107
108    # Iterate through the elements a second time and add model information
109    for elemname in elements:
110        e = dtd.get_elem(elemname)
111        model = e.get_content_model()
112        if model is None:
113            modeltype = "sims.Any"
114            modelargs = None
115        elif model == ("", [], ""):
116            modeltype = "sims.Empty"
117            modelargs = None
118        else:
119            def extractcont(model):
120                if len(model) == 3:
121                    result = {}
122                    for cont in model[1]:
123                        result.update(extractcont(cont))
124                    return result
125                else:
126                    return {model[0]: None}
127            model = extractcont(model)
128            modeltype = "sims.Elements"
129            modelargs = []
130            for cont in model:
131                if cont == "#PCDATA":
132                    modeltype = "sims.ElementsOrText"
133                elif cont == "EMPTY":
134                    modeltype = "sims.Empty"
135                else:
136                    modelargs.append(ns.element(cont))
137            if not modelargs:
138                if modeltype == "sims.ElementsOrText":
139                    modeltype = "sims.NoElements"
140                else:
141                    modeltype = "sims.NoElementsOrText"
142        e = ns.element(elemname)
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            ent = parsers.parsestring(dtd.resolve_ge(entname).value)
152            ns.content.append(xnd.CharRef(entname, codepoint=ord(unicode(ent[0])[0])))
153
154    return ns
155
156
157def stream2xnd(stream, xmlns, shareattrs):
158    xnd = dtd2xnd(stream.read(), xmlns)
159
160    if shareattrs=="dupes":
161        xnd.shareattrs(False)
162    elif shareattrs=="all":
163        xnd.shareattrs(True)
164    return xnd
165
166
167def main(args=None):
168    p = optparse.OptionParser(usage="usage: %prog [options] <input.dtd >output_xmlns.py")
169    p.add_option("-x", "--xmlns", dest="xmlns", help="the namespace name for this module")
170    p.add_option("-s", "--shareattrs", dest="shareattrs", help="Should identical attributes be shared among elements?", choices=("none", "dupes", "all"), default="dupes")
171    p.add_option("-m", "--model", dest="model", default="once", help="Add sims information to the namespace", choices=("no", "all", "once"))
172    p.add_option("-d", "--defaults", action="store_true", dest="defaults", help="Output default values for attributes")
173
174    (options, args) = p.parse_args(args)
175    if len(args) != 0:
176        p.error("incorrect number of arguments")
177        return 1
178    print stream2xnd(sys.stdin, options.xmlns, options.shareattrs).aspy(model=options.model, defaults=options.defaults)
179
180
181if __name__ == "__main__":
182    sys.exit(main())
Note: See TracBrowser for help on using the browser.