1# This program is free software; you can redistribute it and/or modify it under
2# the terms of the (LGPL) GNU Lesser General Public License as published by the
3# Free Software Foundation; either version 3 of the License, or (at your
4# option) any later version.
5#
6# This program is distributed in the hope that it will be useful, but WITHOUT
7# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
8# FOR A PARTICULAR PURPOSE. See the GNU Library Lesser General Public License
9# for more details at ( http://www.gnu.org/licenses/lgpl.html ).
10#
11# You should have received a copy of the GNU Lesser General Public License
12# along with this program; if not, write to the Free Software Foundation, Inc.,
13# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
14# written by: Jeff Ortel ( jortel@redhat.com )
15
16"""
17Provides XSD typing classes.
18
19"""
20
21from suds.sax import Namespace
22from suds.sax.text import Text
23from suds.sudsobject import Object
24
25
26class Typer:
27    """
28    Provides XML node typing as either automatic or manual.
29
30    @cvar types: Class to XSD type mapping.
31    @type types: dict
32
33    """
34
35    types = {
36        bool: ("boolean", Namespace.xsdns),
37        float: ("float", Namespace.xsdns),
38        int: ("int", Namespace.xsdns),
39        int: ("long", Namespace.xsdns),
40        str: ("string", Namespace.xsdns),
41        Text: ("string", Namespace.xsdns),
42        str: ("string", Namespace.xsdns)}
43
44    @classmethod
45    def auto(cls, node, value=None):
46        """
47        Automatically set the node's xsi:type attribute based on either
48        I{value}'s or the node text's class. When I{value} is an unmapped
49        class, the default type (xs:any) is set.
50
51        @param node: XML node.
52        @type node: L{sax.element.Element}
53        @param value: Object that is or would be the node's text.
54        @type value: I{any}
55        @return: Specified node.
56        @rtype: L{sax.element.Element}
57
58        """
59        if value is None:
60            value = node.getText()
61        if isinstance(value, Object):
62            known = cls.known(value)
63            if known.name is None:
64                return node
65            tm = known.name, known.namespace()
66        else:
67            tm = cls.types.get(value.__class__, cls.types.get(str))
68        cls.manual(node, *tm)
69        return node
70
71    @classmethod
72    def manual(cls, node, tval, ns=None):
73        """
74        Set the node's xsi:type attribute based on either I{value}'s or the
75        node text's class. Then adds the referenced prefix(s) to the node's
76        prefix mapping.
77
78        @param node: XML node.
79        @type node: L{sax.element.Element}
80        @param tval: XSD schema type name.
81        @type tval: str
82        @param ns: I{tval} XML namespace.
83        @type ns: (prefix, URI)
84        @return: Specified node.
85        @rtype: L{sax.element.Element}
86
87        """
88        xta = ":".join((Namespace.xsins[0], "type"))
89        node.addPrefix(Namespace.xsins[0], Namespace.xsins[1])
90        if ns is None:
91            node.set(xta, tval)
92        else:
93            ns = cls.genprefix(node, ns)
94            qname = ":".join((ns[0], tval))
95            node.set(xta, qname)
96            node.addPrefix(ns[0], ns[1])
97        return node
98
99    @classmethod
100    def genprefix(cls, node, ns):
101        """
102        Generate a prefix.
103
104        @param node: XML node on which the prefix will be used.
105        @type node: L{sax.element.Element}
106        @param ns: Namespace needing a unique prefix.
107        @type ns: (prefix, URI)
108        @return: I{ns} with a new prefix.
109        @rtype: (prefix, URI)
110
111        """
112        for i in range(1, 1024):
113            prefix = "ns%d" % (i,)
114            uri = node.resolvePrefix(prefix, default=None)
115            if uri in (None, ns[1]):
116                return prefix, ns[1]
117        raise Exception("auto prefix, exhausted")
118
119    @classmethod
120    def known(cls, object):
121        try:
122            md = object.__metadata__
123            known = md.sxtype
124            return known
125        except Exception:
126            pass
127