• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

contrib/opal/ZSI/build/lib/ZSI/TC.py

00001 #! /usr/bin/env python
00002 # $Header$
00003 '''General typecodes.
00004 '''
00005 
00006 from ZSI import _copyright, _children, _child_elements, \
00007     _floattypes, _stringtypes, _seqtypes, _find_attr, _find_attrNS, _find_attrNodeNS, \
00008     _find_arraytype, _find_default_namespace, _find_href, _find_encstyle, \
00009     _resolve_prefix, _find_xsi_attr, _find_type, \
00010     _find_xmlns_prefix, _get_element_nsuri_name, _get_idstr, \
00011     _Node, EvaluateException, UNICODE_ENCODING, \
00012     _valid_encoding, ParseException
00013     
00014 from ZSI.wstools.Namespaces import SCHEMA, SOAP
00015 from ZSI.wstools.Utility import SplitQName
00016 from ZSI.wstools.c14n import Canonicalize
00017 from ZSI.wstools.logging import getLogger as _GetLogger
00018 
00019 import re, types, time, copy
00020 
00021 from base64 import decodestring as b64decode, encodestring as b64encode
00022 from urllib import unquote as urldecode, quote as urlencode
00023 from binascii import unhexlify as hexdecode, hexlify as hexencode
00024 try:
00025     from cStringIO import StringIO
00026 except ImportError:
00027     from StringIO import StringIO
00028 
00029 
00030 _is_xsd_or_soap_ns = lambda ns: ns in [
00031                         SCHEMA.XSD3, SOAP.ENC, SCHEMA.XSD1, SCHEMA.XSD2, ]
00032 _find_nil = lambda E: _find_xsi_attr(E, "null") or _find_xsi_attr(E, "nil")
00033 
00034 def _get_xsitype(pyclass):
00035     '''returns the xsi:type as a tuple, coupled with ZSI.schema
00036     '''
00037     if hasattr(pyclass,'type') and type(pyclass.type) in _seqtypes:
00038         return pyclass.type
00039     elif hasattr(pyclass,'type') and hasattr(pyclass, 'schema'):
00040         return (pyclass.schema, pyclass.type)
00041 
00042     return (None,None)
00043 
00044 
00045 # value returned when xsi:nil="true"
00046 Nilled = None
00047 UNBOUNDED = 'unbounded'
00048 
00049 
00050 class TypeCode:
00051     '''The parent class for all parseable SOAP types.
00052     Class data:
00053         typechecks -- do init-time type checking if non-zero
00054     Class data subclasses may define:
00055         tag -- global element declaration
00056         type -- global type definition
00057         parselist -- list of valid SOAP types for this class, as
00058             (uri,name) tuples, where a uri of None means "all the XML
00059             Schema namespaces"
00060         errorlist -- parselist in a human-readable form; will be
00061             generated if/when needed
00062         seriallist -- list of Python types or user-defined classes
00063             that this typecode can serialize.
00064         logger -- logger instance for this class.
00065     '''
00066     tag = None
00067     type = (None,None)
00068     typechecks = True
00069     attribute_typecode_dict = None
00070     logger = _GetLogger('ZSI.TC.TypeCode')
00071 
00072     def __init__(self, pname=None, aname=None, minOccurs=1,
00073          maxOccurs=1, nillable=False, typed=True, unique=True, 
00074          pyclass=None, attrs_aname='_attrs', **kw):
00075         '''Baseclass initialization.
00076         Instance data (and usually keyword arg)
00077             pname -- the parameter name (localname).
00078             nspname -- the namespace for the parameter;
00079                 None to ignore the namespace
00080             typed -- output xsi:type attribute
00081             unique -- data item is not aliased (no href/id needed)
00082             minOccurs -- min occurances
00083             maxOccurs -- max occurances
00084             nillable -- is item nillable?
00085             attrs_aname -- This is variable name to dictionary of attributes
00086             encoded -- encoded namespaceURI (specify if use is encoded)
00087         '''
00088         if type(pname) in _seqtypes:
00089             self.nspname, self.pname = pname
00090         else:
00091             self.nspname, self.pname = None, pname
00092 
00093         if self.pname:
00094             self.pname = str(self.pname).split(':')[-1]
00095 
00096         self.aname = aname or self.pname
00097         self.minOccurs = minOccurs
00098         self.maxOccurs = maxOccurs
00099         self.nillable = nillable
00100         self.typed = typed
00101         self.unique = unique
00102         self.attrs_aname = attrs_aname
00103         self.pyclass = pyclass
00104 
00105         # Need this stuff for rpc/encoded.
00106         encoded = kw.get('encoded')
00107         if encoded is not None:
00108             self.nspname = kw['encoded']
00109 
00110     def parse(self, elt, ps):
00111         '''
00112         Parameters:
00113             elt -- the DOM element being parsed
00114             ps -- the ParsedSoap object.
00115         '''
00116         raise EvaluateException("Unimplemented evaluation", ps.Backtrace(elt))
00117 
00118     def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
00119         '''
00120         Parameters:
00121            elt -- the current DOMWrapper element 
00122            sw -- soapWriter object
00123            pyobj -- python object to serialize
00124 
00125         '''
00126         raise EvaluateException("Unimplemented evaluation", sw.Backtrace(elt))
00127 
00128     def text_to_data(self, text, elt, ps):
00129         '''convert text into typecode specific data.
00130         Parameters:
00131             text -- text content
00132             elt -- the DOM element being parsed
00133             ps -- the ParsedSoap object.
00134         '''
00135         raise EvaluateException("Unimplemented evaluation", ps.Backtrace(elt))
00136 
00137     def serialize_as_nil(self, elt):
00138         '''
00139         Parameters:
00140             elt -- the current DOMWrapper element 
00141         '''
00142         elt.setAttributeNS(SCHEMA.XSI3, 'nil', '1')
00143 
00144     def SimpleHREF(self, elt, ps, tag):
00145         '''Simple HREF for non-string and non-struct and non-array.
00146         Parameters:
00147             elt -- the DOM element being parsed
00148             ps -- the ParsedSoap object.
00149             tag -- 
00150         '''
00151         if len(_children(elt)): return elt
00152         href = _find_href(elt)
00153         if not href:
00154             if self.minOccurs is 0: return None
00155             raise EvaluateException('Required' + tag + ' missing',
00156                     ps.Backtrace(elt))
00157         return ps.FindLocalHREF(href, elt, 0)
00158 
00159     def get_parse_and_errorlist(self):
00160         """Get the parselist and human-readable version, errorlist is returned,
00161         because it is used in error messages.
00162         """
00163         d = self.__class__.__dict__
00164         parselist = d.get('parselist')
00165         errorlist = d.get('errorlist')
00166         if parselist and not errorlist:
00167             errorlist = []
00168             for t in parselist:
00169                 if t[1] not in errorlist: errorlist.append(t[1])
00170             errorlist = ' or '.join(errorlist)
00171             d['errorlist'] = errorlist
00172         return (parselist, errorlist)
00173 
00174     def checkname(self, elt, ps):
00175         '''See if the name and type of the "elt" element is what we're
00176         looking for.   Return the element's type.
00177         Parameters:
00178             elt -- the DOM element being parsed
00179             ps -- the ParsedSoap object.
00180         '''
00181 
00182         parselist,errorlist = self.get_parse_and_errorlist()
00183         ns, name = _get_element_nsuri_name(elt)
00184         if ns == SOAP.ENC:
00185             # Element is in SOAP namespace, so the name is a type.
00186             if parselist and \
00187             (None, name) not in parselist and (ns, name) not in parselist:
00188                 raise EvaluateException(
00189                 'Element mismatch (got %s wanted %s) (SOAP encoding namespace)' % \
00190                         (name, errorlist), ps.Backtrace(elt))
00191             return (ns, name)
00192 
00193         # Not a type, check name matches.
00194         if self.nspname and ns != self.nspname:
00195             raise EvaluateException('Element NS mismatch (got %s wanted %s)' % \
00196                 (ns, self.nspname), ps.Backtrace(elt))
00197 
00198         if self.pname and name != self.pname:
00199             raise EvaluateException('Element Name mismatch (got %s wanted %s)' % \
00200                 (name, self.pname), ps.Backtrace(elt))
00201         return self.checktype(elt, ps)
00202 
00203     def checktype(self, elt, ps):
00204         '''See if the type of the "elt" element is what we're looking for.
00205         Return the element's type.
00206         Parameters:
00207             elt -- the DOM element being parsed
00208             ps -- the ParsedSoap object.
00209         '''
00210         typeName = _find_type(elt)
00211         if typeName is None or typeName == "":
00212             return (None,None)
00213 
00214         # Parse the QNAME.
00215         prefix,typeName = SplitQName(typeName)
00216         uri = ps.GetElementNSdict(elt).get(prefix)
00217         if uri is None:
00218             raise EvaluateException('Malformed type attribute (bad NS)',
00219                     ps.Backtrace(elt))
00220 
00221         #typeName = list[1]
00222         parselist,errorlist = self.get_parse_and_errorlist()
00223         if not parselist or \
00224         (uri,typeName) in parselist or \
00225         (_is_xsd_or_soap_ns(uri) and (None,typeName) in parselist):
00226             return (uri,typeName)
00227         raise EvaluateException(
00228                 'Type mismatch (%s namespace) (got %s wanted %s)' % \
00229                 (uri, typeName, errorlist), ps.Backtrace(elt))
00230 
00231     def name_match(self, elt):
00232         '''Simple boolean test to see if we match the element name.
00233         Parameters:
00234             elt -- the DOM element being parsed
00235         '''
00236         return self.pname == elt.localName and \
00237                     self.nspname in [None, '', elt.namespaceURI]
00238 
00239     def nilled(self, elt, ps):
00240         '''Is the element NIL, and is that okay?
00241         Parameters:
00242             elt -- the DOM element being parsed
00243             ps -- the ParsedSoap object.
00244         '''
00245         if _find_nil(elt) not in [ "true",  "1"]: return False
00246         if self.nillable is False:
00247             raise EvaluateException('Non-nillable element is NIL',
00248                     ps.Backtrace(elt))
00249         return True
00250 
00251     def simple_value(self, elt, ps, mixed=False):
00252         '''Get the value of the simple content of this element.
00253         Parameters:
00254             elt -- the DOM element being parsed
00255             ps -- the ParsedSoap object.
00256             mixed -- ignore element content, optional text node
00257         '''
00258         if not _valid_encoding(elt):
00259             raise EvaluateException('Invalid encoding', ps.Backtrace(elt))
00260         c = _children(elt)
00261         if mixed is False:
00262             if len(c) == 0:
00263                 raise EvaluateException('Value missing', ps.Backtrace(elt))
00264             for c_elt in c:
00265                 if c_elt.nodeType == _Node.ELEMENT_NODE:
00266                     raise EvaluateException('Sub-elements in value',
00267                         ps.Backtrace(c_elt))
00268 
00269         # It *seems* to be consensus that ignoring comments and
00270         # concatenating the text nodes is the right thing to do.
00271         return ''.join([E.nodeValue for E in c
00272                 if E.nodeType 
00273                 in [ _Node.TEXT_NODE, _Node.CDATA_SECTION_NODE ]])
00274 
00275     def parse_attributes(self, elt, ps):
00276         '''find all attributes specified in the attribute_typecode_dict in
00277         current element tag, if an attribute is found set it in the 
00278         self.attributes dictionary.  Default to putting in String.
00279         Parameters:
00280             elt -- the DOM element being parsed
00281             ps -- the ParsedSoap object.
00282         '''
00283         if self.attribute_typecode_dict is None: 
00284             return
00285         
00286         attributes = {}
00287         for attr,what in self.attribute_typecode_dict.items():
00288             namespaceURI,localName = None,attr
00289             if type(attr) in _seqtypes: 
00290                 namespaceURI,localName = attr
00291             value = _find_attrNodeNS(elt, namespaceURI, localName)
00292             self.logger.debug("Parsed Attribute (%s,%s) -- %s", 
00293                                namespaceURI, localName, value)
00294 
00295             # For Now just set it w/o any type interpretation.
00296             if value is None: continue
00297             attributes[attr] = what.text_to_data(value, elt, ps)
00298 
00299         return attributes
00300 
00301     def set_attributes(self, el, pyobj):
00302         '''Instance data attributes contains a dictionary 
00303         of keys (namespaceURI,localName) and attribute values.
00304         These values can be self-describing (typecode), or use
00305         attribute_typecode_dict to determine serialization.
00306         Paramters:
00307             el -- MessageInterface representing the element
00308             pyobj -- 
00309         '''
00310         if not hasattr(pyobj, self.attrs_aname):
00311             return
00312 
00313         if not isinstance(getattr(pyobj, self.attrs_aname), dict):
00314             raise TypeError,\
00315                 'pyobj.%s must be a dictionary of names and values'\
00316                 % self.attrs_aname
00317 
00318         for attr, value in getattr(pyobj, self.attrs_aname).items():
00319             namespaceURI,localName = None, attr
00320             if type(attr) in _seqtypes:
00321                 namespaceURI, localName = attr
00322 
00323             what = None
00324             if getattr(self, 'attribute_typecode_dict', None) is not None:
00325                 what = self.attribute_typecode_dict.get(attr)
00326                 if what is None and namespaceURI is None:
00327                     what = self.attribute_typecode_dict.get(localName)
00328 
00329             # allow derived type
00330             if hasattr(value, 'typecode') and not isinstance(what, AnyType):
00331                 if what is not None and not isinstance(value.typecode, what):
00332                     raise EvaluateException, \
00333                         'self-describing attribute must subclass %s'\
00334                         %what.__class__
00335 
00336                 what = value.typecode
00337                 
00338             self.logger.debug("attribute create -- %s", value)
00339             if isinstance(what, QName):
00340                 what.set_prefix(el, value)
00341             
00342             #format the data
00343             if what is None:
00344                 value = str(value)
00345             else:
00346                 value = what.get_formatted_content(value)
00347 
00348             el.setAttributeNS(namespaceURI, localName, value)
00349 
00350     def set_attribute_xsi_type(self, el, **kw):
00351         '''if typed, set the xsi:type attribute 
00352         Paramters:
00353             el -- MessageInterface representing the element
00354         '''
00355         if kw.get('typed', self.typed):
00356             namespaceURI,typeName = kw.get('type', _get_xsitype(self))
00357             if namespaceURI and typeName:
00358                 self.logger.debug("attribute: (%s, %s)", namespaceURI, typeName)
00359                 el.setAttributeType(namespaceURI, typeName)
00360 
00361     def set_attribute_href(self, el, objid):
00362         '''set href attribute
00363         Paramters:
00364             el -- MessageInterface representing the element
00365             objid -- ID type, unique id
00366         '''
00367         el.setAttributeNS(None, 'href', "#%s" %objid)
00368 
00369     def set_attribute_id(self, el, objid):
00370         '''set id attribute
00371         Paramters:
00372             el -- MessageInterface representing the element
00373             objid -- ID type, unique id
00374         '''
00375         if self.unique is False:
00376             el.setAttributeNS(None, 'id', "%s" %objid)
00377 
00378     def get_name(self, name, objid):
00379         '''
00380         Paramters:
00381             name -- element tag
00382             objid -- ID type, unique id
00383         '''
00384         if type(name) is tuple:
00385             return name
00386 
00387         ns = self.nspname
00388         n = name or self.pname or ('E' + objid)
00389         return ns,n
00390 
00391     def has_attributes(self):
00392         '''Return True if Attributes are declared outside
00393         the scope of SOAP ('root', 'id', 'href'), and some
00394         attributes automatically handled (xmlns, xsi:type).
00395         '''
00396         if self.attribute_typecode_dict is None: return False
00397         return len(self.attribute_typecode_dict) > 0
00398 
00399 
00400 class SimpleType(TypeCode):
00401     '''SimpleType -- consist exclusively of a tag, attributes, and a value
00402     class attributes:
00403         empty_content -- value representing an empty element.
00404     '''
00405     empty_content = None
00406     logger = _GetLogger('ZSI.TC.SimpleType')
00407     
00408     def parse(self, elt, ps):
00409         self.checkname(elt, ps)
00410         if len(_children(elt)) == 0:
00411             href = _find_href(elt)
00412             if not href:
00413                 if self.nilled(elt, ps) is False:
00414                     # No content, no HREF, not NIL:  empty string
00415                     return self.text_to_data(self.empty_content, elt, ps)
00416                     
00417                 # No content, no HREF, and is NIL...
00418                 if self.nillable is True: 
00419                     return Nilled
00420                 raise EvaluateException('Requiredstring missing',
00421                         ps.Backtrace(elt))
00422                         
00423             if href[0] != '#':
00424                 return ps.ResolveHREF(href, self)
00425             
00426             elt = ps.FindLocalHREF(href, elt)
00427             self.checktype(elt, ps)
00428             if self.nilled(elt, ps): return Nilled
00429             if len(_children(elt)) == 0: 
00430                 v = self.empty_content
00431             else:
00432                 v = self.simple_value(elt, ps)
00433         else:
00434             v = self.simple_value(elt, ps)
00435             
00436         pyobj = self.text_to_data(v, elt, ps)
00437         
00438         # parse all attributes contained in attribute_typecode_dict 
00439         # (user-defined attributes), the values (if not None) will 
00440         # be keyed in self.attributes dictionary.
00441         if self.attribute_typecode_dict is not None:
00442             attributes = self.parse_attributes(elt, ps)
00443             if attributes:
00444                 setattr(pyobj, self.attrs_aname, attributes)
00445         
00446         return pyobj
00447 
00448     def get_formatted_content(self, pyobj):
00449         raise NotImplementedError, 'method get_formatted_content is not implemented'
00450 
00451     def serialize_text_node(self, elt, sw, pyobj):
00452         '''Serialize without an element node.
00453         '''
00454         textNode = None
00455         if pyobj is not None:
00456             text = self.get_formatted_content(pyobj)
00457             if type(text) not in _stringtypes:
00458                 raise TypeError, 'pyobj must be a formatted string'
00459 
00460             textNode = elt.createAppendTextNode(text)
00461 
00462         return textNode
00463 
00464     def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
00465         '''Handles the start and end tags, and attributes.  callout
00466         to get_formatted_content to get the textNode value.
00467         Parameters:
00468             elt -- ElementProxy/DOM element 
00469             sw -- SoapWriter instance
00470             pyobj -- processed content
00471             
00472         KeyWord Parameters:
00473             name -- substitute name, (nspname,name) or name
00474             orig --
00475             
00476         '''
00477         objid = _get_idstr(pyobj)
00478         ns,n = self.get_name(name, objid)
00479 
00480         # nillable
00481         el = elt.createAppendElement(ns, n)
00482         if self.nillable is True and pyobj is Nilled:
00483             self.serialize_as_nil(el)
00484             return None
00485 
00486         # other attributes
00487         self.set_attributes(el, pyobj)
00488 
00489         # soap href attribute
00490         unique = self.unique or kw.get('unique', False)
00491         if unique is False and sw.Known(orig or pyobj):
00492             self.set_attribute_href(el, objid)
00493             return None
00494 
00495         # xsi:type attribute 
00496         if kw.get('typed', self.typed) is True:
00497             self.set_attribute_xsi_type(el, **kw)
00498 
00499         # soap id attribute
00500         if self.unique is False:
00501             self.set_attribute_id(el, objid)
00502 
00503         #Content, <empty tag/>c
00504         self.serialize_text_node(el, sw, pyobj)
00505 
00506         return el
00507 
00508 
00509 class Any(TypeCode):
00510     '''When the type isn't defined in the schema, but must be specified
00511     in the incoming operation.
00512         parsemap -- a type to class mapping (updated by descendants), for
00513                 parsing
00514         serialmap -- same, for (outgoing) serialization
00515     '''
00516     logger = _GetLogger('ZSI.TC.Any')
00517     parsemap, serialmap = {}, {}
00518 
00519     def __init__(self, pname=None, aslist=False, minOccurs=0, unique=False, **kw):
00520         TypeCode.__init__(self, pname, minOccurs=minOccurs, unique=unique, **kw)
00521         self.aslist = aslist
00522         self.kwargs = dict(aslist=aslist, unique=unique)
00523         self.kwargs.update(kw)
00524 
00525     # input arg v should be a list of tuples (name, value).
00526     def listify(self, v):
00527         if self.aslist: return [ k for j,k in v ]
00528         return dict(v)
00529 
00530     def parse_into_dict_or_list(self, elt, ps):
00531         c = _child_elements(elt)
00532         count = len(c)
00533         v = []
00534         if count == 0:
00535             href = _find_href(elt)
00536             if not href: return v
00537             elt = ps.FindLocalHREF(href, elt)
00538             self.checktype(elt, ps)
00539             c = _child_elements(elt)
00540             count = len(c)
00541             if count == 0: return self.listify(v)
00542         if self.nilled(elt, ps): return Nilled
00543 
00544         for c_elt in c:
00545             v.append((str(c_elt.localName), self.__class__(**self.kwargs).parse(c_elt, ps)))
00546 
00547         return self.listify(v)
00548 
00549     def parse(self, elt, ps):
00550         (ns,type) = self.checkname(elt, ps)
00551         if not type and self.nilled(elt, ps): return Nilled
00552         if len(_children(elt)) == 0:
00553             href = _find_href(elt)
00554             if not href:
00555                 if self.minOccurs < 1:
00556                     if _is_xsd_or_soap_ns(ns):
00557                         parser = Any.parsemap.get((None,type))
00558                         if parser: return parser.parse(elt, ps)
00559                     if ((ns,type) == (SOAP.ENC,'Array') or 
00560                         (_find_arraytype(elt) or '').endswith('[0]')):
00561                         return []
00562                     return None
00563                 raise EvaluateException('Required Any missing',
00564                         ps.Backtrace(elt))
00565             elt = ps.FindLocalHREF(href, elt)
00566             (ns,type) = self.checktype(elt, ps)
00567         if not type and elt.namespaceURI == SOAP.ENC:
00568             ns,type = SOAP.ENC, elt.localName
00569         if not type or (ns,type) == (SOAP.ENC,'Array'):
00570             if self.aslist or _find_arraytype(elt):
00571                 return [ self.__class__(**self.kwargs).parse(e, ps)
00572                             for e in _child_elements(elt) ]
00573             if len(_child_elements(elt)) == 0:
00574                 #raise EvaluateException("Any cannot parse untyped element",
00575                 #        ps.Backtrace(elt))
00576                 return self.simple_value(elt, ps)
00577             return self.parse_into_dict_or_list(elt, ps)
00578         parser = Any.parsemap.get((ns,type))
00579         if not parser and _is_xsd_or_soap_ns(ns):
00580             parser = Any.parsemap.get((None,type))
00581         if not parser:
00582             raise EvaluateException('''Any can't parse element''',
00583                     ps.Backtrace(elt))
00584         return parser.parse(elt, ps)
00585 
00586     def get_formatted_content(self, pyobj):
00587         tc = type(pyobj)
00588         if tc == types.InstanceType:
00589             tc = pyobj.__class__
00590             if hasattr(pyobj, 'typecode'):
00591                 #serializer = pyobj.typecode.serialmap.get(tc)
00592                 serializer = pyobj.typecode
00593             else:
00594                 serializer = Any.serialmap.get(tc)
00595             if not serializer:
00596                 tc = (types.ClassType, pyobj.__class__.__name__)
00597                 serializer = Any.serialmap.get(tc)
00598         else:
00599             serializer = Any.serialmap.get(tc)
00600             if not serializer and isinstance(pyobj, time.struct_time):
00601                 from ZSI.TCtimes import gDateTime
00602                 serializer = gDateTime()
00603         if serializer:
00604             return serializer.get_formatted_content(pyobj)
00605         raise EvaluateException, 'Failed to find serializer for pyobj %s' %pyobj
00606 
00607     def serialize(self, elt, sw, pyobj, name=None, **kw):
00608         if hasattr(pyobj, 'typecode') and pyobj.typecode is not self:
00609             pyobj.typecode.serialize(elt, sw, pyobj, **kw)
00610             return
00611 
00612         objid = _get_idstr(pyobj)
00613         ns,n = self.get_name(name, objid)
00614         kw.setdefault('typed', self.typed)
00615         tc = type(pyobj)
00616         self.logger.debug('Any serialize -- %s', tc)
00617         if tc in _seqtypes:
00618             if self.aslist:
00619                 array = elt.createAppendElement(ns, n)
00620                 array.setAttributeType(SOAP.ENC, "Array")
00621                 array.setAttributeNS(self.nspname, 'SOAP-ENC:arrayType', 
00622                     "xsd:anyType[" + str(len(pyobj)) + "]" )
00623                 for o in pyobj:
00624                     #TODO maybe this should take **self.kwargs...
00625                     serializer = getattr(o, 'typecode', Any(**self.kwargs))
00626                     serializer.serialize(array, sw, o, name='element', **kw)
00627             else:
00628                 struct = elt.createAppendElement(ns, n)
00629                 for o in pyobj:
00630                     #TODO maybe this should take **self.kwargs...
00631                     serializer = getattr(o, 'typecode', Any(**self.kwargs))
00632                     serializer.serialize(struct, sw, o, **kw)
00633             return
00634 
00635         kw['name'] = (ns,n)
00636         if tc == types.DictType:
00637             el = elt.createAppendElement(ns, n)
00638             parentNspname = self.nspname # temporarily clear nspname for dict elements
00639             self.nspname = None
00640             for o,m in pyobj.items():
00641                 if type(o) != types.StringType and type(o) != types.UnicodeType:
00642                     raise Exception, 'Dictionary implementation requires keys to be of type string (or unicode).' %pyobj
00643                 kw['name'] = o
00644                 kw.setdefault('typed', True)
00645                 self.serialize(el, sw, m, **kw)
00646             # restore nspname
00647             self.nspname = parentNspname
00648             return
00649                 
00650         if tc == types.InstanceType:
00651             tc = pyobj.__class__
00652             if hasattr(pyobj, 'typecode'):
00653                 #serializer = pyobj.typecode.serialmap.get(tc)
00654                 serializer = pyobj.typecode
00655             else:
00656                 serializer = Any.serialmap.get(tc)
00657             if not serializer:
00658                 tc = (types.ClassType, pyobj.__class__.__name__)
00659                 serializer = Any.serialmap.get(tc)
00660         else:
00661             serializer = Any.serialmap.get(tc)
00662             if not serializer and isinstance(pyobj, time.struct_time):
00663                 from ZSI.TCtimes import gDateTime
00664                 serializer = gDateTime()
00665 
00666         if not serializer:
00667             # Last-chance; serialize instances as dictionary
00668             if pyobj is None:
00669                 self.serialize_as_nil(elt.createAppendElement(ns, n))
00670             elif type(pyobj) != types.InstanceType:
00671                 raise EvaluateException('''Any can't serialize ''' + \
00672                         repr(pyobj))
00673             else:
00674                 self.serialize(elt, sw, pyobj.__dict__, **kw)
00675         else:
00676             # Try to make the element name self-describing
00677             tag = getattr(serializer, 'tag', None)
00678             if self.pname is not None:
00679                 #serializer.nspname = self.nspname
00680                 #serializer.pname = self.pname
00681                 if "typed" not in kw:
00682                     kw['typed'] = False
00683             elif tag:
00684                 if tag.find(':') == -1: tag = 'SOAP-ENC:' + tag
00685                 kw['name'] = tag
00686                 kw['typed'] = False
00687 
00688             serializer.unique = self.unique
00689             serializer.serialize(elt, sw, pyobj, **kw)
00690             # Reset TypeCode
00691             #serializer.nspname = None
00692             #serializer.pname = None
00693 
00694 
00695 class String(SimpleType):
00696     '''A string type.
00697     '''
00698     empty_content = ''
00699     parselist = [ (None,'string') ]
00700     seriallist = [ types.StringType, types.UnicodeType ]
00701     type = (SCHEMA.XSD3, 'string')
00702     logger = _GetLogger('ZSI.TC.String')
00703 
00704     def __init__(self, pname=None, strip=True, **kw):
00705         TypeCode.__init__(self, pname, **kw)
00706         if kw.has_key('resolver'): self.resolver = kw['resolver']
00707         self.strip = strip
00708 
00709     def text_to_data(self, text, elt, ps):
00710         '''convert text into typecode specific data.
00711         Encode all strings as UTF-8, which will be type 'str'
00712         not 'unicode'
00713         '''
00714         if self.strip: text = text.strip()
00715         if self.pyclass is not None:
00716             return self.pyclass(text.encode(UNICODE_ENCODING))
00717         return text.encode(UNICODE_ENCODING)
00718 
00719     def get_formatted_content(self, pyobj):
00720         if type(pyobj) not in _stringtypes:
00721             pyobj = str(pyobj)
00722         if type(pyobj) == unicode: 
00723             return pyobj.encode(UNICODE_ENCODING)
00724         return pyobj
00725 
00726 
00727 class URI(String):
00728     '''A URI.
00729     Class data:
00730         reserved -- urllib.quote will escape all reserved characters
00731              regardless of whether they are used for the reserved purpose.
00732 
00733     '''
00734     parselist = [ (None,'anyURI'),(SCHEMA.XSD3, 'anyURI')]
00735     type = (SCHEMA.XSD3, 'anyURI')
00736     logger = _GetLogger('ZSI.TC.URI')
00737     reserved = ";/?:@&=+$,"
00738 
00739     def text_to_data(self, text, elt, ps):
00740         '''text --> typecode specific data.
00741         '''
00742         return String.text_to_data(self, urldecode(text), elt, ps)   
00743 
00744     def get_formatted_content(self, pyobj):
00745         '''typecode data --> text
00746         '''
00747         u = urlencode(pyobj, self.reserved)
00748         return String.get_formatted_content(self, 
00749             u)
00750 
00751 
00752 class QName(String):
00753     '''A QName type
00754     '''
00755     parselist = [ (None,'QName') ]
00756     type = (SCHEMA.XSD3, 'QName')
00757     logger = _GetLogger('ZSI.TC.QName')
00758 
00759     def __init__(self, pname=None, strip=1, **kw):
00760         String.__init__(self, pname, strip, **kw)
00761         self.prefix = None
00762 
00763     def get_formatted_content(self, pyobj):
00764         value = pyobj
00765         if isinstance(pyobj, tuple):
00766             namespaceURI,localName = pyobj
00767             if self.prefix is not None:
00768                 value = "%s:%s" %(self.prefix,localName)
00769         return String.get_formatted_content(self, value)
00770 
00771     def set_prefix(self, elt, pyobj):
00772         '''use this method to set the prefix of the QName,
00773         method looks in DOM to find prefix or set new prefix.
00774         This method must be called before get_formatted_content.
00775         '''
00776         if isinstance(pyobj, tuple):
00777             namespaceURI,localName = pyobj
00778             self.prefix = elt.getPrefix(namespaceURI)
00779 
00780     def text_to_data(self, text, elt, ps):
00781         '''convert text into typecode specific data.
00782         '''
00783         prefix,localName = SplitQName(text)
00784         nsdict = ps.GetElementNSdict(elt)
00785         prefix = prefix or ''
00786         try:
00787             namespaceURI = nsdict[prefix]
00788         except KeyError, ex:
00789             raise EvaluateException('cannot resolve prefix(%s)'%prefix,
00790                 ps.Backtrace(elt))
00791                 
00792         v = (namespaceURI,localName)
00793         if self.pyclass is not None:
00794             return self.pyclass(v)    
00795         return v
00796 
00797     def serialize_text_node(self, elt, sw, pyobj):
00798         '''Serialize without an element node.
00799         '''
00800         self.set_prefix(elt, pyobj)
00801         return String.serialize_text_node(self, elt, sw, pyobj)
00802 
00803 
00804 class Token(String):
00805     '''an xsd:token type
00806     '''
00807     parselist = [ (None, 'token') ]
00808     type = (SCHEMA.XSD3, 'token')
00809     logger = _GetLogger('ZSI.TC.Token')
00810 
00811 
00812 class Base64String(String):
00813     '''A Base64 encoded string.
00814     '''
00815     parselist = [ (None,'base64Binary'), (SOAP.ENC, 'base64') ]
00816     type = (SOAP.ENC, 'base64')
00817     logger = _GetLogger('ZSI.TC.Base64String')
00818 
00819     def text_to_data(self, text, elt, ps):
00820         '''convert text into typecode specific data.
00821         '''
00822         val = b64decode(text.replace(' ', '').replace('\n','').replace('\r',''))
00823         if self.pyclass is not None:
00824             return self.pyclass(val)
00825         return val
00826 
00827     def get_formatted_content(self, pyobj):
00828         pyobj = '\n' + b64encode(pyobj)
00829         return String.get_formatted_content(self, pyobj)
00830 
00831 
00832 class Base64Binary(String):
00833     parselist = [ (None,'base64Binary'), ]
00834     type = (SCHEMA.XSD3, 'base64Binary')
00835     logger = _GetLogger('ZSI.TC.Base64Binary')
00836 
00837     def text_to_data(self, text, elt, ps):
00838         '''convert text into typecode specific data.
00839         '''
00840         val = b64decode(text)
00841         if self.pyclass is not None:
00842             return self.pyclass(val) 
00843         return val
00844 
00845     def get_formatted_content(self, pyobj):
00846         pyobj = b64encode(pyobj).strip()
00847         return pyobj
00848 
00849 
00850 class HexBinaryString(String):
00851     '''Hex-encoded binary (yuk).
00852     '''
00853     parselist = [ (None,'hexBinary') ]
00854     type = (SCHEMA.XSD3, 'hexBinary')
00855     logger = _GetLogger('ZSI.TC.HexBinaryString')
00856 
00857     def text_to_data(self, text, elt, ps):
00858         '''convert text into typecode specific data.
00859         '''
00860         val = hexdecode(text)
00861         if self.pyclass is not None:
00862             return self.pyclass(val) 
00863         return val
00864 
00865     def get_formatted_content(self, pyobj):
00866         pyobj = hexencode(pyobj).upper()
00867         return String.get_formatted_content(self, pyobj)
00868 
00869 
00870 class XMLString(String):
00871     '''A string that represents an XML document
00872     '''
00873     logger = _GetLogger('ZSI.TC.XMLString')
00874     
00875     def __init__(self, pname=None, readerclass=None, **kw):
00876         String.__init__(self, pname, **kw)
00877         self.readerclass = readerclass
00878 
00879     def parse(self, elt, ps):
00880         if not self.readerclass:
00881             from xml.dom.ext.reader import PyExpat
00882             self.readerclass = PyExpat.Reader
00883         v = String.parse(self, elt, ps)
00884         return self.readerclass().fromString(v)
00885 
00886     def get_formatted_content(self, pyobj):
00887         #pyobj = Canonicalize(pyobj)
00888         return String.get_formatted_content(self, pyobj)
00889 
00890 
00891 class Enumeration(String):
00892     '''A string type, limited to a set of choices.
00893     '''
00894     logger = _GetLogger('ZSI.TC.Enumeration')
00895     
00896     def __init__(self, choices, pname=None, **kw):
00897         String.__init__(self, pname, **kw)
00898         t = type(choices)
00899         if t in _seqtypes:
00900             self.choices = tuple(choices)
00901         elif TypeCode.typechecks:
00902             raise TypeError(
00903                 'Enumeration choices must be list or sequence, not ' + str(t))
00904         if TypeCode.typechecks:
00905             for c in self.choices:
00906                 if type(c) not in _stringtypes:
00907                     raise TypeError(
00908                         'Enumeration choice ' + str(c) + ' is not a string')
00909 
00910     def parse(self, elt, ps):
00911         val = String.parse(self, elt, ps)
00912         if val not in self.choices:
00913             raise EvaluateException('Value not in enumeration list',
00914                     ps.Backtrace(elt))
00915         return val
00916 
00917     def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
00918         if pyobj not in self.choices:
00919             raise EvaluateException('Value not in enumeration list',
00920                     sw.Backtrace(elt))
00921         String.serialize(self, elt, sw, pyobj, name=name, orig=orig, **kw)
00922 
00923 
00924 
00925 # This is outside the Integer class purely for code esthetics.
00926 _ignored = []
00927 
00928 class Integer(SimpleType):
00929     '''Common handling for all integers.
00930     '''
00931 
00932     ranges = {
00933         'unsignedByte':         (0, 255),
00934         'unsignedShort':        (0, 65535),
00935         'unsignedInt':          (0, 4294967295L),
00936         'unsignedLong':         (0, 18446744073709551615L),
00937 
00938         'byte':                 (-128, 127),
00939         'short':                (-32768, 32767),
00940         'int':                  (-2147483648L, 2147483647),
00941         'long':                 (-9223372036854775808L, 9223372036854775807L),
00942 
00943         'negativeInteger':      (_ignored, -1),
00944         'nonPositiveInteger':   (_ignored, 0),
00945         'nonNegativeInteger':   (0, _ignored),
00946         'positiveInteger':      (1, _ignored),
00947 
00948         'integer':              (_ignored, _ignored)
00949     }
00950     parselist = [ (None,k) for k in ranges.keys() ]
00951     seriallist = [ types.IntType, types.LongType ]
00952     logger = _GetLogger('ZSI.TC.Integer')
00953 
00954     def __init__(self, pname=None, format='%d', **kw):
00955         TypeCode.__init__(self, pname, **kw)
00956         self.format = format
00957 
00958     def text_to_data(self, text, elt, ps):
00959         '''convert text into typecode specific data.
00960         '''
00961         if self.pyclass is not None:
00962             v = self.pyclass(text) 
00963         else:
00964             try:
00965                 v = int(text)
00966             except:
00967                 try:
00968                     v = long(text)
00969                 except:
00970                     raise EvaluateException('Unparseable integer', 
00971                         ps.Backtrace(elt))
00972         return v
00973 
00974     def parse(self, elt, ps):
00975         (ns,type) = self.checkname(elt, ps)
00976         if self.nilled(elt, ps): return Nilled
00977         elt = self.SimpleHREF(elt, ps, 'integer')
00978         if not elt: return None
00979 
00980         if type is None:
00981            type = self.type[1] 
00982         elif self.type[1] is not None and type != self.type[1]:
00983             raise EvaluateException('Integer type mismatch; ' \
00984                 'got %s wanted %s' % (type,self.type[1]), ps.Backtrace(elt))
00985         
00986         v = self.simple_value(elt, ps)
00987         v = self.text_to_data(v, elt, ps)
00988 
00989         (rmin, rmax) = Integer.ranges.get(type, (_ignored, _ignored))
00990         if rmin != _ignored and v < rmin:
00991             raise EvaluateException('Underflow, less than ' + repr(rmin),
00992                     ps.Backtrace(elt))
00993         if rmax != _ignored and v > rmax:
00994             raise EvaluateException('Overflow, greater than ' + repr(rmax),
00995                     ps.Backtrace(elt))
00996         return v
00997 
00998     def get_formatted_content(self, pyobj):
00999         return self.format %pyobj
01000 
01001 
01002 
01003 # See credits, below.
01004 def _make_inf():
01005     x = 2.0
01006     x2 = x * x
01007     i = 0
01008     while i < 100 and x != x2:
01009         x = x2
01010         x2 = x * x
01011         i = i + 1
01012     if x != x2:
01013         raise ValueError("This machine's floats go on forever!")
01014     return x
01015 
01016 # This is outside the Decimal class purely for code esthetics.
01017 _magicnums = { }
01018 try:
01019     _magicnums['INF'] = float('INF')
01020     _magicnums['-INF'] = float('-INF')
01021 except:
01022     _magicnums['INF'] = _make_inf()
01023     _magicnums['-INF'] = -_magicnums['INF']
01024 
01025 # The following comment and code was written by Tim Peters in
01026 # article <001401be92d2$09dcb800$5fa02299@tim> in comp.lang.python,
01027 # also available at the following URL:
01028 # http://groups.google.com/groups?selm=001401be92d2%2409dcb800%245fa02299%40tim
01029 # Thanks, Tim!
01030 
01031 # NaN-testing.
01032 #
01033 # The usual method (x != x) doesn't work.
01034 # Python forces all comparisons thru a 3-outcome cmp protocol; unordered
01035 # isn't a possible outcome.  The float cmp outcome is essentially defined
01036 # by this C expression (combining some cross-module implementation
01037 # details, and where px and py are pointers to C double):
01038 #   px == py ? 0 : *px < *py ? -1 : *px > *py ? 1 : 0
01039 # Comparing x to itself thus always yields 0 by the first clause, and so
01040 # x != x is never true.
01041 # If px and py point to distinct NaN objects, a strange thing happens:
01042 # 1. On scrupulous 754 implementations, *px < *py returns false, and so
01043 #    does *px > *py.  Python therefore returns 0, i.e. "equal"!
01044 # 2. On Pentium HW, an unordered outcome sets an otherwise-impossible
01045 #    combination of condition codes, including both the "less than" and
01046 #    "equal to" flags.  Microsoft C generates naive code that accepts
01047 #    the "less than" flag at face value, and so the *px < *py clause
01048 #    returns true, and Python returns -1, i.e. "not equal".
01049 # So with a proper C 754 implementation Python returns the wrong result,
01050 # and under MS's improper 754 implementation Python yields the right
01051 # result -- both by accident.  It's unclear who should be shot <wink>.
01052 #
01053 # Anyway, the point of all that was to convince you it's tricky getting
01054 # the right answer in a portable way!
01055 def isnan(x):
01056     """x -> true iff x is a NaN."""
01057     # multiply by 1.0 to create a distinct object (x < x *always*
01058     # false in Python, due to object identity forcing equality)
01059     if x * 1.0 < x:
01060         # it's a NaN and this is MS C on a Pentium
01061         return 1
01062     # Else it's non-NaN, or NaN on a non-MS+Pentium combo.
01063     # If it's non-NaN, then x == 1.0 and x == 2.0 can't both be true,
01064     # so we return false.  If it is NaN, then assuming a good 754 C
01065     # implementation Python maps both unordered outcomes to true.
01066     return 1.0 == x and x == 2.0
01067 
01068 
01069 class Decimal(SimpleType):
01070     '''Parent class for floating-point numbers.
01071     '''
01072 
01073     parselist = [ (None,'decimal'), (None,'float'), (None,'double') ]
01074     seriallist = _floattypes
01075     type = None
01076     ranges =  {
01077         'float': ( 7.0064923216240861E-46,
01078                         -3.4028234663852886E+38, 3.4028234663852886E+38 ),
01079         'double': ( 2.4703282292062327E-324,
01080                         -1.7976931348623158E+308, 1.7976931348623157E+308),
01081     }
01082     zeropat = re.compile('[1-9]')
01083     logger = _GetLogger('ZSI.TC.Decimal')
01084 
01085     def __init__(self, pname=None, format='%f', **kw):
01086         TypeCode.__init__(self, pname, **kw)
01087         self.format = format
01088 
01089 
01090     def text_to_data(self, text, elt, ps):
01091         '''convert text into typecode specific data.
01092         '''
01093         v = text
01094         if self.pyclass is not None:
01095             return self.pyclass(v)
01096 
01097         m = _magicnums.get(v)
01098         if m: return m
01099 
01100         try:
01101             return float(v)
01102         except:
01103             raise EvaluateException('Unparseable floating point number',
01104                     ps.Backtrace(elt))
01105 
01106     def parse(self, elt, ps):
01107         (ns,type) = self.checkname(elt, ps)
01108         elt = self.SimpleHREF(elt, ps, 'floating-point')
01109         if not elt: return None
01110         tag = getattr(self.__class__, 'type')
01111         if tag:
01112             if type is None:
01113                 type = tag
01114             elif tag != (ns,type):
01115                 raise EvaluateException('Floating point type mismatch; ' \
01116                         'got (%s,%s) wanted %s' % (ns,type,tag), ps.Backtrace(elt))
01117         # Special value?
01118         if self.nilled(elt, ps): return Nilled
01119         v = self.simple_value(elt, ps)
01120         try:
01121             fp = self.text_to_data(v, elt, ps)
01122         except EvaluateException, ex:
01123             ex.args.append(ps.Backtrace(elt))
01124             raise ex
01125    
01126         m = _magicnums.get(v)
01127         if m: 
01128             return m
01129 
01130         if str(fp).lower() in [ 'inf', '-inf', 'nan', '-nan' ]:
01131             raise EvaluateException('Floating point number parsed as "' + \
01132                     str(fp) + '"', ps.Backtrace(elt))
01133         if fp == 0 and Decimal.zeropat.search(v):
01134             raise EvaluateException('Floating point number parsed as zero',
01135                     ps.Backtrace(elt))
01136         (rtiny, rneg, rpos) = Decimal.ranges.get(type, (None, None, None))
01137         if rneg and fp < 0 and fp < rneg:
01138             raise EvaluateException('Negative underflow', ps.Backtrace(elt))
01139         if rtiny and fp > 0 and fp < rtiny:
01140             raise EvaluateException('Positive underflow', ps.Backtrace(elt))
01141         if rpos and fp > 0 and fp > rpos:
01142             raise EvaluateException('Overflow', ps.Backtrace(elt))
01143         return fp
01144 
01145     def get_formatted_content(self, pyobj):
01146         if pyobj == _magicnums['INF']:
01147             return 'INF'
01148         elif pyobj == _magicnums['-INF']:
01149             return '-INF'
01150         elif isnan(pyobj):
01151             return 'NaN'
01152         else:
01153             return self.format %pyobj
01154 
01155 
01156 class Boolean(SimpleType):
01157     '''A boolean.
01158     '''
01159 
01160     parselist = [ (None,'boolean') ]
01161     seriallist = [ bool ]
01162     type = (SCHEMA.XSD3, 'boolean')
01163     logger = _GetLogger('ZSI.TC.Boolean')
01164     
01165     def text_to_data(self, text, elt, ps):
01166         '''convert text into typecode specific data.
01167         '''
01168         v = text
01169         if v == 'false': 
01170             if self.pyclass is None:
01171                 return False
01172             return self.pyclass(False)
01173 
01174         if v == 'true': 
01175             if self.pyclass is None:
01176                 return True
01177             return self.pyclass(True)
01178 
01179         try:
01180             v = int(v)
01181         except:
01182             try:
01183                 v = long(v)
01184             except:
01185                 raise EvaluateException('Unparseable boolean', 
01186                         ps.Backtrace(elt))
01187 
01188         if v:
01189             if self.pyclass is None:
01190                 return True
01191             return self.pyclass(True)
01192 
01193         if self.pyclass is None:
01194              return False
01195         return self.pyclass(False)
01196 
01197     def parse(self, elt, ps):
01198         self.checkname(elt, ps)
01199         elt = self.SimpleHREF(elt, ps, 'boolean')
01200         if not elt: return None
01201         if self.nilled(elt, ps): return Nilled
01202 
01203         v = self.simple_value(elt, ps).lower()
01204         return self.text_to_data(v, elt, ps)
01205 
01206     def get_formatted_content(self, pyobj):
01207         if pyobj: return 'true'
01208         return 'false'
01209 
01210 
01211 #XXX NOT FIXED YET
01212 class XML(TypeCode):
01213     '''Opaque XML which shouldn't be parsed.
01214         comments -- preserve comments
01215         inline -- don't href/id when serializing
01216         resolver -- object to resolve href's
01217         wrapped -- put a wrapper element around it
01218     '''
01219 
01220     # Clone returned data?
01221     copyit = 0
01222     logger = _GetLogger('ZSI.TC.XML')
01223     
01224     def __init__(self, pname=None, comments=0, inline=0, wrapped=True, **kw):
01225         TypeCode.__init__(self, pname, **kw)
01226         self.comments = comments
01227         self.inline = inline
01228         if kw.has_key('resolver'): self.resolver = kw['resolver']
01229         self.wrapped = wrapped
01230         self.copyit = kw.get('copyit', XML.copyit)
01231 
01232     def parse(self, elt, ps):
01233         if self.wrapped is False:
01234             return elt
01235         c = _child_elements(elt)
01236         if not c:
01237             href = _find_href(elt)
01238             if not href:
01239                 if self.minOccurs == 0: return None
01240                 raise EvaluateException('Embedded XML document missing',
01241                         ps.Backtrace(elt))
01242             if href[0] != '#':
01243                 return ps.ResolveHREF(href, self)
01244             elt = ps.FindLocalHREF(href, elt)
01245             c = _child_elements(elt)
01246         if _find_encstyle(elt) != "":
01247             #raise EvaluateException('Embedded XML has unknown encodingStyle',
01248             #       ps.Backtrace(elt)
01249             pass
01250         if len(c) != 1:
01251             raise EvaluateException('Embedded XML has more than one child',
01252                     ps.Backtrace(elt))
01253         if self.copyit: return c[0].cloneNode(1)
01254         return c[0]
01255 
01256     def serialize(self, elt, sw, pyobj, name=None, unsuppressedPrefixes=[], **kw):
01257         objid = _get_idstr(pyobj)
01258         ns,n = self.get_name(name, objid)
01259 
01260         xmlelt = elt
01261         if self.wrapped:
01262             xmlelt = elt.createAppendElement(ns, n)
01263 
01264         #if type(pyobj) in _stringtypes:
01265         #    self.set_attributes(xmlelt, pyobj)
01266         #    self.set_attribute_href(xmlelt, objid)
01267         #elif kw.get('inline', self.inline):
01268         #    self.cb(xmlelt, sw, pyobj, unsuppressedPrefixes)
01269         #else:
01270         #    self.set_attributes(xmlelt, pyobj)
01271         #    self.set_attribute_href(xmlelt, objid)
01272         #    sw.AddCallback(self.cb, elt, sw, pyobj, unsuppressedPrefixes)
01273 
01274         self.cb(xmlelt, sw, pyobj, unsuppressedPrefixes)
01275 
01276     def cb(self, elt, sw, pyobj, unsuppressedPrefixes=[]):
01277         """pyobj -- xml.dom.Node.ELEMENT_NODE
01278         """
01279         #if sw.Known(pyobj): 
01280         #    return
01281 
01282         if type(pyobj) in _stringtypes:
01283             elt.createAppendTextNode(pyobj)
01284             return
01285 
01286         ## grab document and import node, and append it
01287         doc = elt.getDocument()
01288         node = doc.importNode(pyobj, deep=1)
01289         child = elt.node.appendChild(node)
01290 
01291         ## copy xmlns: attributes into appended node
01292         parent = pyobj.parentNode
01293         while parent.nodeType == _Node.ELEMENT_NODE:
01294             for attr in filter(lambda a: a.name.startswith('xmlns:') and a.name not in child.attributes.keys(), parent.attributes): 
01295                 child.setAttributeNode(attr.cloneNode(1))
01296 
01297             parent = parent.parentNode
01298 
01299 
01300 class AnyType(TypeCode):
01301     """XML Schema xsi:anyType type definition wildCard.
01302        class variables: 
01303           all -- specifies use of all namespaces.
01304           other -- specifies use of other namespaces
01305           type --
01306     """
01307     all = '#all'
01308     other = '#other'
01309     type = (SCHEMA.XSD3, 'anyType')
01310     logger = _GetLogger('ZSI.TC.AnyType')
01311     
01312     def __init__(self, pname=None, namespaces=['#all'],
01313     minOccurs=1, maxOccurs=1, strip=1, **kw):
01314         TypeCode.__init__(self, pname=pname, minOccurs=minOccurs, 
01315               maxOccurs=maxOccurs, **kw)
01316         self.namespaces = namespaces
01317 
01318     def get_formatted_content(self, pyobj):
01319         # TODO: not sure this makes sense, 
01320         # parse side will be clueless, but oh well..
01321         what = getattr(pyobj, 'typecode', Any())
01322         return what.get_formatted_content(pyobj)
01323 
01324     def text_to_data(self, text, elt, ps):
01325         '''convert text into typecode specific data.  Used only with
01326         attributes so will not know anything about this content so
01327         why guess?
01328         Parameters:
01329             text -- text content
01330             elt -- the DOM element being parsed
01331             ps -- the ParsedSoap object.
01332         '''
01333         return text
01334 
01335     def serialize(self, elt, sw, pyobj, **kw):
01336         nsuri,typeName = _get_xsitype(pyobj)
01337         if self.all not in self.namespaces and nsuri not in self.namespaces:
01338             raise EvaluateException(
01339                 '<anyType> unsupported use of namespaces "%s"' %self.namespaces)
01340         
01341         what = getattr(pyobj, 'typecode', None)
01342         if what is None:
01343             # TODO: resolve this, "strict" processing but no 
01344             # concrete schema makes little sense.
01345             #what = _AnyStrict(pname=(self.nspname,self.pname))
01346             what = Any(pname=(self.nspname,self.pname), unique=True, 
01347                        aslist=False)
01348             kw['typed'] = True
01349             what.serialize(elt, sw, pyobj, **kw)
01350             return
01351 
01352         # Namespace if element AnyType was namespaced.
01353         what.serialize(elt, sw, pyobj, 
01354            name=(self.nspname or what.nspname, self.pname or what.pname), **kw)
01355 
01356     def parse(self, elt, ps):
01357         #element name must be declared ..
01358         nspname,pname = _get_element_nsuri_name(elt)
01359         if nspname != self.nspname or pname != self.pname:
01360             raise EvaluateException('<anyType> instance is (%s,%s) found (%s,%s)' %(
01361                     self.nspname,self.pname,nspname,pname), ps.Backtrace(elt))
01362 
01363         #locate xsi:type
01364         prefix, typeName = SplitQName(_find_type(elt))
01365         namespaceURI = _resolve_prefix(elt, prefix)
01366         pyclass = GTD(namespaceURI, typeName)
01367         if not pyclass:
01368             if _is_xsd_or_soap_ns(namespaceURI):
01369                 pyclass = Any
01370             elif (str(namespaceURI).lower()==str(Apache.Map.type[0]).lower())\
01371                 and (str(typeName).lower() ==str(Apache.Map.type[1]).lower()):
01372                 pyclass = Apache.Map
01373             else:
01374                 # Unknown type, so parse into a dictionary
01375                 pyobj = Any().parse_into_dict_or_list(elt, ps)
01376                 return pyobj
01377                     
01378         what = pyclass(pname=(self.nspname,self.pname))
01379         pyobj = what.parse(elt, ps)
01380         return pyobj
01381 
01382 
01383 class AnyElement(AnyType):
01384     """XML Schema xsi:any element declaration wildCard.
01385        class variables: 
01386             tag -- global element declaration
01387     """
01388     tag = (SCHEMA.XSD3, 'any')
01389     logger = _GetLogger('ZSI.TC.AnyElement')
01390     
01391     def __init__(self, namespaces=['#all'],pname=None, 
01392         minOccurs=1, maxOccurs=1, strip=1, processContents='strict',
01393         **kw):
01394         
01395         if processContents not in ('lax', 'skip', 'strict'):
01396             raise ValueError('processContents(%s) must be lax, skip, or strict')
01397             
01398         self.processContents = processContents
01399         AnyType.__init__(self, namespaces=namespaces,pname=pname,
01400             minOccurs=minOccurs, maxOccurs=maxOccurs, strip=strip, **kw)
01401        
01402     def serialize(self, elt, sw, pyobj, **kw):
01403         '''Must provice typecode to AnyElement for serialization, else
01404         try to use TC.Any to serialize instance which will serialize 
01405         based on the data type of pyobj w/o reference to XML schema 
01406         instance.
01407         '''
01408         if isinstance(pyobj, TypeCode):
01409             raise TypeError, 'pyobj is a typecode instance.'
01410         
01411         what = getattr(pyobj, 'typecode', None)
01412         if what is not None and type(pyobj) is types.InstanceType:
01413             tc = pyobj.__class__
01414             what = Any.serialmap.get(tc)
01415             if not what:
01416                 tc = (types.ClassType, pyobj.__class__.__name__)
01417                 what = Any.serialmap.get(tc)
01418         
01419         self.logger.debug('processContents: %s', self.processContents)
01420 
01421         # failed to find a registered type for class
01422         if what is None:
01423             #TODO: seems incomplete.  what about facets.
01424             #if self.processContents == 'strict':
01425             what = Any(pname=(self.nspname,self.pname))
01426                 
01427         self.logger.debug('serialize with %s', what.__class__.__name__)
01428         what.serialize(elt, sw, pyobj, **kw)
01429 
01430     def parse(self, elt, ps):
01431         '''
01432         processContents -- 'lax' | 'skip' | 'strict', 'strict'
01433         1) if 'skip' check namespaces, and return the DOM node.
01434         2) if 'lax' look for declaration, or definition.  If
01435            not found return DOM node.
01436         3) if 'strict' get declaration, or raise.
01437         '''
01438         skip = self.processContents == 'skip'
01439         nspname,pname = _get_element_nsuri_name(elt)
01440         what = GED(nspname, pname)
01441         if not skip and what is not None:
01442             pyobj = what.parse(elt, ps)
01443             try:
01444                 pyobj.typecode = what
01445             except AttributeError, ex:
01446                 # Assume this means builtin type.
01447                 pyobj = WrapImmutable(pyobj, what)
01448             return pyobj
01449         
01450         # Allow use of "<any>" element declarations w/ local
01451         # element declarations
01452         prefix, typeName = SplitQName(_find_type(elt))
01453         if not skip and typeName:
01454             namespaceURI = _resolve_prefix(elt, prefix or 'xmlns')
01455             # First look thru user defined namespaces, if don't find
01456             # look for 'primitives'.
01457             pyclass = GTD(namespaceURI, typeName) or Any
01458             what = pyclass(pname=(nspname,pname))
01459             pyobj = what.parse(elt, ps)
01460             try:
01461                 pyobj.typecode = what
01462             except AttributeError, ex:
01463                 # Assume this means builtin type.
01464                 pyobj = WrapImmutable(pyobj, what)
01465                 
01466             what.typed = True
01467             return pyobj
01468 
01469         if skip:
01470             what = XML(pname=(nspname,pname), wrapped=False)
01471         elif self.processContents == 'lax':
01472             what = Any(pname=(nspname,pname), unique=True)
01473         else:
01474             what = Any(pname=(nspname,pname), unique=True)
01475 
01476         try:
01477             pyobj = what.parse(elt, ps)
01478         except EvaluateException, ex:
01479             self.logger.debug("error parsing:  %s" %str(ex))
01480 
01481             if len(_children(elt)) != 0:
01482                 self.logger.debug('parse <any>, return as dict')
01483                 return Any(aslist=False).parse_into_dict_or_list(elt, ps)
01484 
01485             self.logger.debug("Give up, parse (%s,%s) as a String", 
01486                   what.nspname, what.pname)
01487             what = String(pname=(nspname,pname), typed=False)
01488             return WrapImmutable(what.parse(elt, ps), what)
01489 
01490         if pyobj is None: 
01491             return
01492 
01493         # dict is elementName:value pairs
01494         if type(pyobj) is dict:
01495             return pyobj
01496 
01497         try:
01498             pyobj.typecode = what
01499         except AttributeError:
01500             pyobj = WrapImmutable(pyobj, what)
01501 
01502         return pyobj  
01503 
01504 
01505 
01506 class Union(SimpleType):
01507     '''simpleType Union
01508 
01509     class variables:
01510         memberTypes -- list [(namespace,name),] tuples, each representing a type defintion.
01511     '''
01512     memberTypes = None
01513     logger = _GetLogger('ZSI.TC.Union')
01514     
01515     def __init__(self, pname=None, minOccurs=1, maxOccurs=1, **kw):
01516         SimpleType.__init__(self, pname=pname, minOccurs=minOccurs, maxOccurs=maxOccurs, **kw)
01517         self.memberTypeCodes = []
01518 
01519     def setMemberTypeCodes(self):
01520         if len(self.memberTypeCodes) > 0: 
01521             return
01522         if self.__class__.memberTypes is None:
01523             raise EvaluateException, 'uninitialized class variable memberTypes [(namespace,name),]'
01524         for nsuri,name in self.__class__.memberTypes:
01525             tcclass = GTD(nsuri,name)
01526             if tcclass is None:
01527                 tc = Any.parsemap.get((nsuri,name)) or Any.parsemap.get((None, name))
01528                 typecode = tc.__class__(pname=(self.nspname,self.pname))
01529             else:
01530                 typecode = tcclass(pname=(self.nspname,self.pname))
01531 
01532             if typecode is None:
01533                 raise EvaluateException, \
01534                     'Typecode class for Union memberType (%s,%s) is missing' %(nsuri,name)
01535             if isinstance(typecode, Struct):
01536                 raise EvaluateException, \
01537                     'Illegal: Union memberType (%s,%s) is complexType' %(nsuri,name)
01538             self.memberTypeCodes.append(typecode)
01539 
01540     def parse(self, elt, ps, **kw):
01541         '''attempt to parse sequentially.  No way to know ahead of time
01542         what this instance represents.  Must be simple type so it can
01543         not have attributes nor children, so this isn't too bad.
01544         '''
01545         self.setMemberTypeCodes()
01546         (nsuri,typeName) = self.checkname(elt, ps)
01547 
01548         #if (nsuri,typeName) not in self.memberTypes:
01549         #    raise EvaluateException(
01550         #            'Union Type mismatch got (%s,%s) not in %s' % \
01551         #            (nsuri, typeName, self.memberTypes), ps.Backtrace(elt))
01552 
01553         for indx in range(len(self.memberTypeCodes)):
01554             typecode = self.memberTypeCodes[indx]
01555             try:
01556                 pyobj = typecode.parse(elt, ps)
01557             except ParseException, ex:
01558                 continue
01559             except Exception, ex:
01560                 continue
01561 
01562             if indx > 0:
01563                 self.memberTypeCodes.remove(typecode)
01564                 self.memberTypeCodes.insert(0, typecode)
01565             break
01566 
01567         else:
01568             raise
01569 
01570         return pyobj
01571 
01572     def get_formatted_content(self, pyobj, **kw): 
01573         self.setMemberTypeCodes()
01574         for indx in range(len(self.memberTypeCodes)):
01575             typecode = self.memberTypeCodes[indx]
01576             try:
01577                 content = typecode.get_formatted_content(copy.copy(pyobj))
01578                 break
01579             except (ParseException, TypeError):
01580                 pass
01581 
01582             if indx > 0:
01583                 self.memberTypeCodes.remove(typecode)
01584                 self.memberTypeCodes.insert(0, typecode)
01585 
01586         else:
01587             raise
01588 
01589         return content
01590 
01591 
01592 class List(SimpleType):
01593     '''simpleType List
01594     Class data:
01595         itemType -- sequence (namespaceURI,name) or a TypeCode instance
01596             representing the type definition
01597     '''
01598     itemType = None
01599     logger = _GetLogger('ZSI.TC.List')
01600     
01601     def __init__(self, pname=None, itemType=None, **kw):
01602         '''Currently need to require maxOccurs=1, so list
01603         is interpreted as a single unit of data.
01604         '''
01605         assert kw.get('maxOccurs',1) == 1, \
01606             'Currently only supporting SimpleType Lists with  maxOccurs=1'
01607 
01608         SimpleType.__init__(self, pname=pname, **kw)
01609         self.itemType = itemType or self.itemType
01610         self.itemTypeCode = self.itemType
01611 
01612         itemTypeCode = None
01613         if type(self.itemTypeCode) in _seqtypes:
01614             namespaceURI,name = self.itemTypeCode
01615             try:
01616                 itemTypeCode = GTD(*self.itemType)(None)
01617             except:
01618                 if _is_xsd_or_soap_ns(namespaceURI) is False:
01619                     raise
01620                 for pyclass in TYPES:
01621                     if pyclass.type == self.itemTypeCode:
01622                         itemTypeCode =  pyclass(None)
01623                         break
01624                     elif pyclass.type[1] == name:
01625                         itemTypeCode =  pyclass(None)
01626 
01627                 if itemTypeCode is None:
01628                     raise EvaluateException('Failed to locate %s' %str(self.itemTypeCode))
01629 
01630             if hasattr(itemTypeCode, 'text_to_data') is False:
01631                 raise EvaluateException('TypeCode class %s missing text_to_data method' %itemTypeCode)
01632 
01633             self.itemTypeCode = itemTypeCode
01634 
01635 
01636     def text_to_data(self, text, elt, ps):
01637         '''convert text into typecode specific data.  items in
01638         list are space separated.
01639         '''
01640         v = []
01641         items = text.split()
01642         for item in items:
01643             v.append(self.itemTypeCode.text_to_data(item, elt, ps))
01644 
01645         if self.pyclass is not None:
01646             return self.pyclass(v)
01647         return v
01648 
01649     def parse(self, elt, ps):
01650         '''elt -- the DOM element being parsed
01651         ps -- the ParsedSoap object.
01652         '''
01653         self.checkname(elt, ps)
01654         if len(_children(elt)) == 0:
01655             href = _find_href(elt)
01656             if not href:
01657                 if self.nilled(elt, ps) is False:
01658                     return []
01659                 if self.nillable is True: 
01660                     return Nilled
01661                 raise EvaluateException('Required string missing',
01662                         ps.Backtrace(elt))
01663             if href[0] != '#':
01664                 return ps.ResolveHREF(href, self)
01665             elt = ps.FindLocalHREF(href, elt)
01666             self.checktype(elt, ps)
01667 
01668         if self.nilled(elt, ps): return Nilled
01669         if len(_children(elt)) == 0: return []
01670 
01671         v = self.simple_value(elt, ps)
01672         return self.text_to_data(v, elt, ps)
01673 
01674 
01675     def serialize(self, elt, sw, pyobj, name=None, orig=None, **kw):
01676         '''elt -- the current DOMWrapper element 
01677            sw -- soapWriter object
01678            pyobj -- python object to serialize
01679         '''
01680         if pyobj is not None and type(pyobj) not in _seqtypes:
01681             raise EvaluateException, 'expecting a list or None'
01682 
01683         objid = _get_idstr(pyobj)
01684         ns,n = self.get_name(name, objid)
01685         el = elt.createAppendElement(ns, n)
01686         if self.nillable is True and pyobj is None:
01687             self.serialize_as_nil(el)
01688             return None
01689         
01690         tc = self.itemTypeCode
01691         s = StringIO(); sep = ' '
01692         for item in pyobj:
01693             s.write(tc.get_formatted_content(item))
01694             s.write(sep)
01695 
01696         el.createAppendTextNode(s.getvalue())
01697 
01698 
01699 def RegisterType(C, clobber=0, *args, **keywords):
01700     instance = apply(C, args, keywords)
01701     for t in C.__dict__.get('parselist', []):
01702         prev = Any.parsemap.get(t)
01703         if prev:
01704             if prev.__class__ == C: continue
01705             if not clobber:
01706                 raise TypeError(
01707                     str(C) + ' duplicating parse registration for ' + str(t))
01708         Any.parsemap[t] = instance
01709     for t in C.__dict__.get('seriallist', []):
01710         ti = type(t)
01711         if ti in [ types.TypeType, types.ClassType]:
01712             key = t
01713         elif ti in _stringtypes:
01714             key = (types.ClassType, t)
01715         else:
01716             raise TypeError(str(t) + ' is not a class name')
01717         prev = Any.serialmap.get(key)
01718         if prev:
01719             if prev.__class__ == C: continue
01720             if not clobber:
01721                 raise TypeError(
01722                     str(C) + ' duplicating serial registration for ' + str(t))
01723         Any.serialmap[key] = instance
01724 
01725 
01726 #def _DynamicImport(moduleName, className):
01727 #    '''
01728 #    Utility function for RegisterTypeWithSchemaAndClass
01729 #    '''
01730 #    mod = __import__(moduleName)
01731 #    components = moduleName.split('.')
01732 #    for comp in components[1:]:
01733 #        mod = getattr(mod, comp)
01734 #    return getattr(mod, className)
01735 #
01736 #def _RegisterTypeWithSchemaAndClass(importedSchemaTypes, schemaTypeName, classModuleName, className, generatedClassSuffix="_"):
01737 #    '''
01738 #    Used by RegisterGeneratedTypesWithMapping.
01739 #    Helps register classes so they can be serialized and parsed as "any".
01740 #    Register a type by providing its schema and class.  This allows
01741 #       Any and AnyType to reconstruct objects made up of your own classes.
01742 #       Note: The class module should be able to be imported (by being in your
01743 #       pythonpath).  Your classes __init__ functions shoud have default
01744 #       arguments for all extra parameters.
01745 #    Example of use:
01746 #        import SchemaToPyTypeMap # Mapping written by you.  Also used with wsdl2py -m
01747 #             # mapping = {"SomeDescription":("Descriptions", "SomeDescription"),
01748 #             #             schemaTypeName  :  moduleName   ,  className 
01749 #        # The module on the next line is generated by wsdl2py
01750 #        from EchoServer_services_types import urn_ZSI_examples as ExampleTypes
01751 #
01752 #        for key,value in SchemaToPyTypeMap.mapping.items():
01753 #        ZSI.TC.RegisterTypeWithSchemaAndClass(importedSchemaTypes = ExampleTypes, schemaTypeName=key, classModuleName=value[0], className=value[1])
01754 #
01755 #    '''
01756 #    # Doing this: (schemaTypeName="ExampleTypes", classModuleName="Description",
01757 #    #               className="SomeDescription")
01758 #    # sd_instance = ExampleTypes.SomeDescription_(pname="SomeDescription")
01759 #    # Any.serialmap[Descriptions.SomeDescription] = sd_instance
01760 #    # Any.parsemap[(None,'SomeDescription')] = sd_instance
01761 #    classDef = _DynamicImport(classModuleName, className)
01762 #    interfaceDef = getattr(importedSchemaTypes, schemaTypeName + generatedClassSuffix)
01763 #
01764 #    instance = interfaceDef(pname=className)
01765 #    Any.serialmap[classDef] = instance
01766 #    Any.parsemap[(None,schemaTypeName)] = instance
01767 #
01768 #def RegisterGeneratedTypesWithMapping(generatedTypes, mapping, generatedClassSuffix="_"):
01769 #    '''
01770 #    Registers python classes so they can be serialized and parsed as "any".
01771 #        generatedTypes is a class containing typecode classes generated by zsi.
01772 #        mapping is a dictionary that maps
01773 #        {schemaTypeName : moduleName, className}
01774 #        and is also used with wsdl2py -m
01775 #
01776 #    Example of use:
01777 #        import SchemaToPyTypeMap      # See RegisterTypeWithSchemaAndClass for description
01778 #        # The module on the next line is generated by wsdl2py and
01779 #        #    contains generated typecodes.
01780 #        from EchoServer_services_types import urn_ZSI_examples as ExampleTypes
01781 #        RegisterGeneratedTypesWithMapping(generatedTypes = ExampleTypes, mapping=SchemaToPyTypeMap.mapping)
01782 #    '''
01783 #    for key,value in mapping.items():
01784 #        _RegisterTypeWithSchemaAndClass(importedSchemaTypes = generatedTypes, schemaTypeName=key, classModuleName=value[0], className=value[1], generatedClassSuffix=generatedClassSuffix)
01785 
01786 
01787 from TCnumbers import *
01788 from TCtimes import *
01789 from schema import GTD, GED, WrapImmutable
01790 from TCcompound import *
01791 from TCapache import *
01792 
01793 # aliases backwards compatiblity
01794 _get_type_definition, _get_global_element_declaration, Wrap  = GTD, GED, WrapImmutable
01795 
01796 f = lambda x: type(x) == types.ClassType and issubclass(x, TypeCode) and getattr(x, 'type', None) is not None
01797 TYPES = filter(f, map(lambda y:eval(y),dir()))
01798 
01799 
01800 if __name__ == '__main__': print _copyright
01801 

Generated on Wed Oct 20 2010 11:12:16 for APBS by  doxygen 1.7.2