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