root/livinglogic.python.xist/src/ll/xist/scripts/xml2xsc.py @ 4293:44a34f6adadc

Revision 4293:44a34f6adadc, 4.5 KB (checked in by Walter Doerwald <walter@…>, 9 years ago)

Add option --force-ns for forcing a namespace name on un-namespaced elements in xml2xsc.

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
12import sys, argparse, cStringIO
13
14from ll import misc, url
15from ll.xist import xsc, xnd, sims
16
17
18__docformat__ = "reStructuredText"
19
20
21def iterpath(node):
22    yield [node]
23    if hasattr(node, "text") and node.text:
24        yield [node, node.text]
25    if hasattr(node, "getchildren"):
26        for child in node:
27            for path in iterpath(child):
28                yield [node] + path
29    if hasattr(node, "tail") and node.tail:
30        yield [node, node.tail]
31
32
33def getelementname(node):
34    xmlns = None
35    name = node.tag
36    if name.startswith("{"):
37        (xmlns, sep, name) = name[1:].partition("}")
38    return (name, xmlns)
39
40
41def addetree2xnd(ns, node, elements):
42    # Iterate through the tree and collect which elements are encountered and how they are nested
43    for path in iterpath(node):
44        node = path[-1]
45        if "Element" in type(node).__name__:
46            (name, xmlns) = getelementname(node)
47            if (name, xmlns) in ns.elements:
48                xndnode = ns.elements[(name, xmlns)]
49            else:
50                xndnode = xnd.Element(name, xmlns=xmlns)
51                xndnode.add(ns)
52                elements[(name, xmlns)] = set()
53            for attrname in node.keys():
54                if not attrname.startswith("{") and attrname not in xndnode.attrs:
55                    xndnode(xnd.Attr(attrname, type=xsc.TextAttr))
56        elif "ProcessingInstruction" in type(node).__name__:
57            name = node.target
58            if name not in ns.procinsts:
59                xndnode = xnd.ProcInst(name)
60                xndnode.add(ns)
61        elif "Comment" in type(node).__name__:
62            xndnode = "#comment"
63        elif isinstance(node, basestring):
64            if node.isspace():
65                xndnode = "#whitespace"
66            else:
67                xndnode = "#text"
68        if len(path) >= 2:
69            parent = path[-2]
70            if "Element" in type(parent).__name__:
71                parententry = elements[getelementname(parent)]
72                parententry.add(xndnode)
73
74
75def makexnd(streams, parser="etree", shareattrs="dupes", model="simple"):
76    elements = {} # maps (name, xmlns) to content set
77    ns = xnd.Module()
78    with url.Context():
79        for stream in streams:
80            if isinstance(stream, url.URL):
81                stream = stream.openread()
82            elif isinstance(stream, str):
83                stream = cStringIO.StringIO(stream)
84            if parser == "etree":
85                from xml.etree import cElementTree
86                node = cElementTree.parse(stream).getroot()
87            elif parser == "lxml":
88                from lxml import etree
89                node = etree.parse(stream).getroot()
90            else:
91                raise ValueError("unknown parser {!r}".format(parser))
92            stream.close()
93            addetree2xnd(ns, node, elements)
94
95    # Put sims info into the element definitions
96    if model == "none":
97        pass
98    elif model == "simple":
99        for (fullname, modelset) in elements.iteritems():
100            ns.elements[fullname].modeltype = bool(modelset)
101    elif model in ("fullall", "fullonce"):
102        for (fullname, modelset) in elements.iteritems():
103            element = ns.elements[fullname]
104            if not modelset:
105                element.modeltype = "sims.Empty"
106            else:
107                elements = [el for el in modelset if isinstance(el, xnd.Element)]
108                if not elements:
109                    if "#text" in modelset:
110                        element.modeltype = "sims.NoElements"
111                    else:
112                        element.modeltype = "sims.NoElementsOrText"
113                else:
114                    if "#text" in modelset:
115                        element.modeltype = "sims.ElementsOrText"
116                    else:
117                        element.modeltype = "sims.Elements"
118                    element.modelargs = elements
119    else:
120        raise ValueError("unknown sims mode {!r}".format(model))
121
122    if shareattrs=="dupes":
123        ns.shareattrs(False)
124    elif shareattrs=="all":
125        ns.shareattrs(True)
126    return ns
127
128
129def main(args=None):
130    p = argparse.ArgumentParser(description="Convert XML files to XIST namespace (on stdout)")
131    p.add_argument("urls", metavar="urls", type=url.URL, help="ULRs of DTDs to be parsed", nargs="+")
132    p.add_argument("-p", "--parser", dest="parser", help="parser module to use for XML parsing (default: %(default)s)", choices=("etree", "lxml"), default="etree")
133    p.add_argument("-s", "--shareattrs", dest="shareattrs", help="Should identical attributes be shared among elements? (default: %(default)s)", choices=("none", "dupes", "all"), default="dupes")
134    p.add_argument("-m", "--model", dest="model", help="Create sims info? (default: %(default)s)", choices=("none", "simple", "fullall", "fullonce"), default="simple")
135    p.add_argument("-n", "--force-ns", dest="forcens", metavar="NAME", help="Force elements without a namespace into this namespace")
136
137    args = p.parse_args(args)
138    print makexnd(args.urls, parser=args.parser, shareattrs=args.shareattrs, model=args.model).aspy(model=args.model, forcens=args.forcens)
139
140
141if __name__ == "__main__":
142    sys.exit(main())
Note: See TracBrowser for help on using the browser.