root/livinglogic.python.xist/src/ll/xist/scripts/xml2xsc.py @ 4423:30bac4a06a01

Revision 4423:30bac4a06a01, 4.4 KB (checked in by Walter Doerwald <walter@…>, 9 years ago)

Start documentation for the XIST scripts.

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