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

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

00001 #! /usr/bin/env python
00002 # $Header$
00003 '''XML Schema support
00004 '''
00005 
00006 from ZSI import _copyright, _seqtypes, _find_type, _get_element_nsuri_name, EvaluateException
00007 from ZSI.wstools.Namespaces import SCHEMA, SOAP
00008 from ZSI.wstools.Utility import SplitQName
00009 
00010 
00011 def _get_type_definition(namespaceURI, name, **kw):
00012     return SchemaInstanceType.getTypeDefinition(namespaceURI, name, **kw)
00013 
00014 def _get_global_element_declaration(namespaceURI, name, **kw):
00015     return SchemaInstanceType.getElementDeclaration(namespaceURI, name, **kw)
00016 
00017 def _get_substitute_element(head, elt, ps):
00018     '''if elt matches a member of the head substitutionGroup, return 
00019     the GED typecode.
00020 
00021     head -- ElementDeclaration typecode, 
00022     elt -- the DOM element being parsed
00023     ps -- ParsedSoap Instance
00024     '''
00025     if not isinstance(head, ElementDeclaration):
00026         return None
00027 
00028     return ElementDeclaration.getSubstitutionElement(head, elt, ps)
00029 
00030 def _has_type_definition(namespaceURI, name):
00031     return SchemaInstanceType.getTypeDefinition(namespaceURI, name) is not None
00032 
00033 def _is_substitute_element(head, sub):
00034     '''if head and sub are both GEDs, and sub declares 
00035     head as its substitutionGroup then return True.
00036 
00037     head -- Typecode instance
00038     sub  -- Typecode instance
00039     '''
00040     if not isinstance(head, ElementDeclaration) or not isinstance(sub, ElementDeclaration):
00041         return False
00042 
00043     try:
00044         group = sub.substitutionGroup 
00045     except (AttributeError, TypeError):
00046         return False
00047 
00048     ged = GED(*group)
00049 
00050     # TODO: better way of representing element references.  Wrap them with
00051     # facets, and dereference when needed and delegate to..
00052     print (head.nspname == ged.nspname and head.pname == ged.pname)
00053     if head is ged or not (head.nspname == ged.nspname and head.pname == ged.pname):
00054         return False
00055 
00056     return True
00057 
00058 #
00059 # functions for retrieving schema items from 
00060 # the global schema instance.
00061 #
00062 GED = _get_global_element_declaration
00063 GTD = _get_type_definition
00064 
00065 
00066 def WrapImmutable(pyobj, what):
00067     '''Wrap immutable instance so a typecode can be
00068     set, making it self-describing ie. serializable.
00069     '''
00070     return _GetPyobjWrapper.WrapImmutable(pyobj, what)
00071 
00072 def RegisterBuiltin(arg):
00073     '''Add a builtin to be registered, and register it
00074     with the Any typecode.
00075     '''
00076     _GetPyobjWrapper.RegisterBuiltin(arg)
00077     _GetPyobjWrapper.RegisterAnyElement()
00078 
00079 def RegisterAnyElement():
00080     '''register all Wrapper classes with the Any typecode.
00081     This allows instances returned by Any to be self-describing.
00082     ie. serializable.  AnyElement falls back on Any to parse
00083     anything it doesn't understand.
00084     '''
00085     return _GetPyobjWrapper.RegisterAnyElement()
00086 
00087 
00088 class SchemaInstanceType(type):
00089     '''Register all types/elements, when hit already defined 
00090     class dont create a new one just give back reference.  Thus 
00091     import order determines which class is loaded.
00092 
00093     class variables:
00094         types -- dict of typecode classes definitions 
00095             representing global type definitions.
00096         elements -- dict of typecode classes representing 
00097             global element declarations.
00098         element_typecode_cache -- dict of typecode instances 
00099             representing global element declarations.
00100     '''
00101     types = {}
00102     elements = {}
00103     element_typecode_cache = {}
00104     #substitution_registry = {}
00105     
00106     def __new__(cls,classname,bases,classdict):
00107         '''If classdict has literal and schema register it as a
00108         element declaration, else if has type and schema register
00109         it as a type definition.
00110         '''
00111         if classname in ['ElementDeclaration', 'TypeDefinition', 'LocalElementDeclaration',]:
00112             return type.__new__(cls,classname,bases,classdict)
00113 
00114         if ElementDeclaration in bases:
00115             if classdict.has_key('schema') is False  or classdict.has_key('literal') is False: 
00116                 raise AttributeError, 'ElementDeclaration must define schema and literal attributes'
00117 
00118             key = (classdict['schema'],classdict['literal'])
00119             if SchemaInstanceType.elements.has_key(key):
00120                 return SchemaInstanceType.elements[key]
00121 
00122             # create global element declaration
00123             ged = SchemaInstanceType.elements[key] = type.__new__(cls,classname,bases,classdict)
00124 
00125             # TODO: Maybe I want access to all registrants??
00126             # 
00127             #if classdict.has_key('substitutionGroup'):
00128             #    sub = classdict.has_key('substitutionGroup')
00129             #    if not SchemaInstanceType.substitution_registry.has_key(sub):
00130             #        SchemaInstanceType.substitution_registry[sub] = [ged]
00131             #    else:
00132             #        SchemaInstanceType.substitution_registry[sub].append(ged)
00133 
00134             return ged
00135 
00136         if TypeDefinition in bases:
00137             if classdict.has_key('type') is None:
00138                 raise AttributeError, 'TypeDefinition must define type attribute'
00139 
00140             key = classdict['type']
00141             if SchemaInstanceType.types.has_key(key) is False:
00142                 SchemaInstanceType.types[key] = type.__new__(cls,classname,bases,classdict)
00143             return SchemaInstanceType.types[key]
00144 
00145         if LocalElementDeclaration in bases:
00146                 return type.__new__(cls,classname,bases,classdict)
00147 
00148         raise TypeError, 'SchemaInstanceType must be an ElementDeclaration or TypeDefinition '
00149 
00150     def getTypeDefinition(cls, namespaceURI, name, lazy=False):
00151         '''Grab a type definition, returns a typecode class definition
00152         because the facets (name, minOccurs, maxOccurs) must be provided.
00153  
00154         Parameters:
00155            namespaceURI -- 
00156            name -- 
00157         '''
00158         klass = cls.types.get((namespaceURI, name), None)
00159         if lazy and klass is not None:
00160             return _Mirage(klass)
00161         return klass
00162     getTypeDefinition = classmethod(getTypeDefinition)
00163 
00164     def getElementDeclaration(cls, namespaceURI, name, isref=False, lazy=False):
00165         '''Grab an element declaration, returns a typecode instance
00166         representation or a typecode class definition.  An element 
00167         reference has its own facets, and is local so it will not be
00168         cached.
00169 
00170         Parameters:
00171             namespaceURI -- 
00172             name -- 
00173             isref -- if element reference, return class definition.
00174         '''
00175         key = (namespaceURI, name)
00176         if isref:
00177             klass = cls.elements.get(key,None)
00178             if klass is not None and lazy is True:
00179                 return _Mirage(klass)
00180             return klass
00181  
00182         typecode = cls.element_typecode_cache.get(key, None)
00183         if typecode is None:
00184             tcls = cls.elements.get(key,None)
00185             if tcls is not None:
00186                 typecode = cls.element_typecode_cache[key] = tcls()
00187                 typecode.typed = False
00188             
00189         return typecode
00190     getElementDeclaration = classmethod(getElementDeclaration)
00191 
00192 
00193 class ElementDeclaration:
00194     '''Typecodes subclass to represent a Global Element Declaration by
00195     setting class variables schema and literal.
00196 
00197     schema = namespaceURI
00198     literal = NCName
00199     substitutionGroup -- GED reference of form, (namespaceURI,NCName)
00200     '''
00201     __metaclass__ = SchemaInstanceType
00202 
00203     def checkSubstitute(self, typecode):
00204         '''If this is True, allow typecode to be substituted
00205         for "self" typecode.
00206         '''
00207         if not isinstance(typecode, ElementDeclaration): 
00208             return False
00209 
00210         try:
00211             nsuri,ncname = typecode.substitutionGroup
00212         except (AttributeError, TypeError):
00213             return False
00214 
00215         if (nsuri,ncname) != (self.schema,self.literal):
00216             # allow slop with the empty namespace 
00217             if not nsuri and not self.schema and ncname == self.literal:
00218                  return True
00219 
00220             return False
00221 
00222         sub = GED(self.schema, self.literal)
00223         if sub is None or sub is not typecode:
00224             return False
00225 
00226         return True
00227 
00228     def getSubstitutionElement(self, elt, ps):
00229         '''if elt matches a member of the head substitutionGroup, return 
00230         the GED typecode representation of the member.
00231 
00232         head -- ElementDeclaration typecode, 
00233         elt -- the DOM element being parsed
00234         ps -- ParsedSoap instance
00235         '''
00236         nsuri,ncname = _get_element_nsuri_name(elt)
00237         typecode = GED(nsuri,ncname)
00238         if typecode is None:
00239             return
00240 
00241         try:
00242             nsuri,ncname = typecode.substitutionGroup
00243         except (AttributeError, TypeError):
00244             return
00245 
00246         if (ncname == self.pname) and (nsuri == self.nspname or 
00247            (not nsuri and not self.nspname)):
00248              return typecode
00249        
00250         return 
00251  
00252  
00253 class LocalElementDeclaration:
00254     '''Typecodes subclass to represent a Local Element Declaration.
00255     '''
00256     __metaclass__ = SchemaInstanceType
00257     
00258 
00259 class TypeDefinition:
00260     '''Typecodes subclass to represent a Global Type Definition by
00261     setting class variable type.
00262 
00263     type = (namespaceURI, NCName)
00264     '''
00265     __metaclass__ = SchemaInstanceType
00266     
00267     def getSubstituteType(self, elt, ps):
00268         '''if xsi:type does not match the instance type attr,
00269         check to see if it is a derived type substitution.
00270         
00271         DONT Return the element's type.
00272         
00273         Parameters:
00274             elt -- the DOM element being parsed
00275             ps -- the ParsedSoap object.
00276         '''
00277         pyclass = SchemaInstanceType.getTypeDefinition(*self.type)
00278         if pyclass is None:
00279             raise EvaluateException(
00280                     'No Type registed for xsi:type=(%s, %s)' %
00281                     (self.type[0], self.type[1]), ps.Backtrace(elt))
00282             
00283         typeName = _find_type(elt)
00284         prefix,typeName = SplitQName(typeName)
00285         uri = ps.GetElementNSdict(elt).get(prefix)
00286         subclass = SchemaInstanceType.getTypeDefinition(uri, typeName)
00287         if subclass is None:
00288             raise EvaluateException(
00289                     'No registered xsi:type=(%s, %s), substitute for xsi:type=(%s, %s)' %
00290                     (uri, typeName, self.type[0], self.type[1]), ps.Backtrace(elt))
00291                     
00292         if not issubclass(subclass, pyclass) and subclass(None) and not issubclass(subclass, pyclass):
00293             raise TypeError(
00294                     'Substitute Type (%s, %s) is not derived from %s' %
00295                     (self.type[0], self.type[1], pyclass), ps.Backtrace(elt))
00296 
00297         return subclass((self.nspname, self.pname))
00298     
00299     
00300 
00301 class _Mirage:
00302     '''Used with SchemaInstanceType for lazy evaluation, eval during serialize or 
00303     parse as needed.  Mirage is callable, TypeCodes are not.  When called it returns the
00304     typecode.  Tightly coupled with generated code.
00305     
00306     NOTE: **Must Use ClassType** for intended MRO of __call__ since setting it in
00307     an instance attribute rather than a class attribute (will not work for object).
00308     '''
00309     def __init__(self, klass):
00310         self.klass = klass
00311         self.__reveal = False
00312         self.__cache = None
00313         if issubclass(klass, ElementDeclaration):
00314             self.__call__ = self._hide_element
00315             
00316     def __str__(self):
00317         msg = "<Mirage id=%s, Local Element %s>"
00318         if issubclass(self.klass, ElementDeclaration):
00319             msg = "<Mirage id=%s, GED %s>"
00320         return  msg %(id(self), self.klass)
00321         
00322     def _hide_type(self, pname, aname, minOccurs=0, maxOccurs=1, nillable=False, 
00323                    **kw):
00324         self.__call__ = self._reveal_type
00325         self.__reveal = True
00326         
00327         # store all attributes, make some visable for pyclass_type
00328         self.__kw = kw
00329         self.minOccurs,self.maxOccurs,self.nillable = minOccurs,maxOccurs,nillable
00330         self.nspname,self.pname,self.aname = None,pname,aname
00331         if type(self.pname) in (tuple,list):
00332             self.nspname,self.pname = pname
00333         
00334         return self
00335         
00336     def _hide_element(self, minOccurs=0, maxOccurs=1, nillable=False, **kw):
00337         self.__call__ = self._reveal_element
00338         self.__reveal = True
00339         
00340         # store all attributes, make some visable for pyclass_type
00341         self.__kw = kw
00342         self.nspname = self.klass.schema
00343         self.pname = self.klass.literal
00344         #TODO: Fix hack
00345         #self.aname = '_%s' %self.pname
00346         self.minOccurs,self.maxOccurs,self.nillable = minOccurs,maxOccurs,nillable
00347         
00348         return self
00349     
00350     def _reveal_type(self):
00351         if self.__cache is None:
00352             self.__cache = self.klass(pname=self.pname, 
00353                             aname=self.aname, minOccurs=self.minOccurs, 
00354                             maxOccurs=self.maxOccurs, nillable=self.nillable, 
00355                             **self.__kw)
00356         return self.__cache
00357         
00358     def _reveal_element(self):
00359         if self.__cache is None:
00360             self.__cache = self.klass(minOccurs=self.minOccurs, 
00361                             maxOccurs=self.maxOccurs, nillable=self.nillable, 
00362                             **self.__kw)
00363         return self.__cache
00364     
00365     __call__ = _hide_type
00366 
00367 
00368 class _GetPyobjWrapper:
00369     '''Get a python object that wraps data and typecode.  Used by
00370     <any> parse routine, so that typecode information discovered
00371     during parsing is retained in the pyobj representation
00372     and thus can be serialized.
00373     '''
00374     types_dict = dict()
00375 
00376     def RegisterBuiltin(cls, arg):
00377         '''register a builtin, create a new wrapper.
00378         '''
00379         if arg in cls.types_dict:
00380             raise RuntimeError, '%s already registered' %arg
00381         class _Wrapper(arg):
00382             'Wrapper for builtin %s\n%s' %(arg, cls.__doc__)
00383         _Wrapper.__name__ = '_%sWrapper' %arg.__name__
00384         cls.types_dict[arg] = _Wrapper
00385     RegisterBuiltin = classmethod(RegisterBuiltin)
00386         
00387     def RegisterAnyElement(cls):
00388         '''If find registered TypeCode instance, add Wrapper class 
00389         to TypeCode class serialmap and Re-RegisterType.  Provides
00390         Any serialzation of any instances of the Wrapper.
00391         '''
00392         for k,v in cls.types_dict.items():
00393             what = Any.serialmap.get(k)
00394             if what is None: continue
00395             if v in what.__class__.seriallist: continue
00396             what.__class__.seriallist.append(v)
00397             RegisterType(what.__class__, clobber=1, **what.__dict__)
00398     RegisterAnyElement = classmethod(RegisterAnyElement)
00399 
00400     def WrapImmutable(cls, pyobj, what):
00401         '''return a wrapper for pyobj, with typecode attribute set.
00402         Parameters:
00403             pyobj -- instance of builtin type (immutable)
00404             what -- typecode describing the data
00405         '''
00406         d = cls.types_dict
00407         if type(pyobj) is bool:  
00408             pyclass = d[int]
00409         elif d.has_key(type(pyobj)) is True:
00410             pyclass = d[type(pyobj)]
00411         else:
00412             raise TypeError,\
00413                'Expecting a built-in type in %s (got %s).' %(
00414                 d.keys(),type(pyobj))
00415 
00416         newobj = pyclass(pyobj)
00417         newobj.typecode = what
00418         return newobj
00419     WrapImmutable = classmethod(WrapImmutable)
00420     
00421 
00422 from TC import Any, RegisterType
00423 
00424 if __name__ == '__main__': print _copyright
00425 

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