# Copyright (c) 2003, The Regents of the University of California, # through Lawrence Berkeley National Laboratory (subject to receipt of # any required approvals from the U.S. Dept. of Energy). All rights # reserved. # # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. ident = "$Id$" import types, weakref, urllib, sys from threading import RLock from Namespaces import XMLNS from Utility import DOM, DOMException, Collection, SplitQName from StringIO import StringIO def GetSchema(component): """convience function for finding the parent XMLSchema instance. """ parent = component while not isinstance(parent, XMLSchema): parent = parent._parent() return parent class SchemaReader: """A SchemaReader creates XMLSchema objects from urls and xml data. """ def __init__(self, domReader=None, base_url=None): """domReader -- class must implement DOMAdapterInterface base_url -- base url string """ self.__base_url = base_url self.__readerClass = domReader if not self.__readerClass: self.__readerClass = DOMAdapter self._includes = {} self._imports = {} def __setImports(self, schema): """Add dictionary of imports to schema instance. schema -- XMLSchema instance """ for ns,val in schema.imports.items(): if self._imports.has_key(ns): schema.addImportSchema(self._imports[ns]) def __setIncludes(self, schema): """Add dictionary of includes to schema instance. schema -- XMLSchema instance """ for schemaLocation, val in schema.includes.items(): if self._includes.has_key(schemaLocation): schema.addIncludeSchema(self._imports[schemaLocation]) def addSchemaByLocation(self, location, schema): """provide reader with schema document for a location. """ self._includes[location] = schema def addSchemaByNamespace(self, schema): """provide reader with schema document for a targetNamespace. """ self._imports[schema.targetNamespace] = schema def loadFromNode(self, parent, element): """element -- DOM node or document parent -- WSDLAdapter instance """ reader = self.__readerClass(element) schema = XMLSchema(parent) #HACK to keep a reference schema.wsdl = parent schema.setBaseUrl(self.__base_url) schema.load(reader) return schema def loadFromStream(self, file): """Return an XMLSchema instance loaded from a file object. file -- file object """ reader = self.__readerClass() reader.loadDocument(file) schema = XMLSchema() schema.setBaseUrl(self.__base_url) schema.load(reader) self.__setIncludes(schema) self.__setImports(schema) return schema def loadFromString(self, data): """Return an XMLSchema instance loaded from an XML string. data -- XML string """ return self.loadFromStream(StringIO(data)) def loadFromURL(self, url): """Return an XMLSchema instance loaded from the given url. url -- URL to dereference """ if not url.endswith('xsd'): raise SchemaError, 'unknown file type %s' %url reader = self.__readerClass() if self.__base_url: url = urllib.basejoin(self.__base_url,url) reader.loadFromURL(url) schema = XMLSchema() schema.setBaseUrl(self.__base_url) schema.load(reader) self.__setIncludes(schema) self.__setImports(schema) return schema def loadFromFile(self, filename): """Return an XMLSchema instance loaded from the given file. filename -- name of file to open """ file = open(filename, 'rb') try: schema = self.loadFromStream(file) finally: file.close() return schema class SchemaError(Exception): pass ########################### # DOM Utility Adapters ########################## class DOMAdapterInterface: def hasattr(self, attr, ns=None): """return true if node has attribute attr -- attribute to check for ns -- namespace of attribute, by default None """ raise NotImplementedError, 'adapter method not implemented' def getContentList(self, *contents): """returns an ordered list of child nodes *contents -- list of node names to return """ raise NotImplementedError, 'adapter method not implemented' def setAttributeDictionary(self, attributes): """set attribute dictionary """ raise NotImplementedError, 'adapter method not implemented' def getAttributeDictionary(self): """returns a dict of node's attributes """ raise NotImplementedError, 'adapter method not implemented' def getNamespace(self, prefix): """returns namespace referenced by prefix. """ raise NotImplementedError, 'adapter method not implemented' def getTagName(self): """returns tagName of node """ raise NotImplementedError, 'adapter method not implemented' def getParentNode(self): """returns parent element in DOMAdapter or None """ raise NotImplementedError, 'adapter method not implemented' def loadDocument(self, file): """load a Document from a file object file -- """ raise NotImplementedError, 'adapter method not implemented' def loadFromURL(self, url): """load a Document from an url url -- URL to dereference """ raise NotImplementedError, 'adapter method not implemented' class DOMAdapter(DOMAdapterInterface): """Adapter for ZSI.Utility.DOM """ def __init__(self, node=None): """Reset all instance variables. element -- DOM document, node, or None """ if hasattr(node, 'documentElement'): self.__node = node.documentElement else: self.__node = node self.__attributes = None def hasattr(self, attr, ns=None): """attr -- attribute ns -- optional namespace, None means unprefixed attribute. """ if not self.__attributes: self.setAttributeDictionary() if ns: return self.__attributes.get(ns,{}).has_key(attr) return self.__attributes.has_key(attr) def getContentList(self, *contents): nodes = [] ELEMENT_NODE = self.__node.ELEMENT_NODE for child in DOM.getElements(self.__node, None): if child.nodeType == ELEMENT_NODE and\ SplitQName(child.tagName)[1] in contents: nodes.append(child) return map(self.__class__, nodes) def setAttributeDictionary(self): self.__attributes = {} for v in self.__node._attrs.values(): self.__attributes[v.nodeName] = v.nodeValue def getAttributeDictionary(self): if not self.__attributes: self.setAttributeDictionary() return self.__attributes def getTagName(self): return self.__node.tagName def getParentNode(self): if self.__node.parentNode.nodeType == self.__node.ELEMENT_NODE: return DOMAdapter(self.__node.parentNode) return None def getNamespace(self, prefix): """prefix -- deference namespace prefix in node's context. Ascends parent nodes until found. """ namespace = None if prefix == 'xmlns': namespace = DOM.findDefaultNS(prefix, self.__node) else: try: namespace = DOM.findNamespaceURI(prefix, self.__node) except DOMException, ex: if prefix != 'xml': raise SchemaError, '%s namespace not declared for %s'\ %(prefix, self.__node._get_tagName()) namespace = XMLNS.XML return namespace def loadDocument(self, file): self.__node = DOM.loadDocument(file) if hasattr(self.__node, 'documentElement'): self.__node = self.__node.documentElement def loadFromURL(self, url): self.__node = DOM.loadFromURL(url) if hasattr(self.__node, 'documentElement'): self.__node = self.__node.documentElement class XMLBase: """ These class variables are for string indentation. """ __indent = 0 __rlock = RLock() def __str__(self): XMLBase.__rlock.acquire() XMLBase.__indent += 1 tmp = "<" + str(self.__class__) + '>\n' for k,v in self.__dict__.items(): tmp += "%s* %s = %s\n" %(XMLBase.__indent*' ', k, v) XMLBase.__indent -= 1 XMLBase.__rlock.release() return tmp """Marker Interface: can determine something about an instances properties by using the provided convenience functions. """ class DefinitionMarker: """marker for definitions """ pass class DeclarationMarker: """marker for declarations """ pass class AttributeMarker: """marker for attributes """ pass class AttributeGroupMarker: """marker for attribute groups """ pass class WildCardMarker: """marker for wildcards """ pass class ElementMarker: """marker for wildcards """ pass class ReferenceMarker: """marker for references """ pass class ModelGroupMarker: """marker for model groups """ pass class ExtensionMarker: """marker for extensions """ pass class RestrictionMarker: """marker for restrictions """ facets = ['enumeration', 'length', 'maxExclusive', 'maxInclusive',\ 'maxLength', 'minExclusive', 'minInclusive', 'minLength',\ 'pattern', 'fractionDigits', 'totalDigits', 'whiteSpace'] class SimpleMarker: """marker for simple type information """ pass class ComplexMarker: """marker for complex type information """ pass class LocalMarker: """marker for complex type information """ pass class MarkerInterface: def isDefinition(self): return isinstance(self, DefinitionMarker) def isDeclaration(self): return isinstance(self, DeclarationMarker) def isAttribute(self): return isinstance(self, AttributeMarker) def isAttributeGroup(self): return isinstance(self, AttributeGroupMarker) def isElement(self): return isinstance(self, ElementMarker) def isReference(self): return isinstance(self, ReferenceMarker) def isWildCard(self): return isinstance(self, WildCardMarker) def isModelGroup(self): return isinstance(self, ModelGroupMarker) def isExtension(self): return isinstance(self, ExtensionMarker) def isRestriction(self): return isinstance(self, RestrictionMarker) def isSimple(self): return isinstance(self, SimpleMarker) def isComplex(self): return isinstance(self, ComplexMarker) def isLocal(self): return isinstance(self, LocalMarker) ########################################################## # Schema Components ######################################################### class XMLSchemaComponent(XMLBase, MarkerInterface): """ class variables: required -- list of required attributes attributes -- dict of default attribute values, including None. Value can be a function for runtime dependencies. contents -- dict of namespace keyed content lists. 'xsd' content of xsd namespace. xmlns_key -- key for declared xmlns namespace. xmlns -- xmlns is special prefix for namespace dictionary xml -- special xml prefix for xml namespace. """ required = [] attributes = {} contents = {} xmlns_key = '' xmlns = 'xmlns' xml = 'xml' def __init__(self, parent=None): """parent -- parent instance instance variables: attributes -- dictionary of node's attributes """ self.attributes = None self._parent = parent if self._parent: self._parent = weakref.ref(parent) if not self.__class__ == XMLSchemaComponent\ and not (type(self.__class__.required) == type(XMLSchemaComponent.required)\ and type(self.__class__.attributes) == type(XMLSchemaComponent.attributes)\ and type(self.__class__.contents) == type(XMLSchemaComponent.contents)): raise RuntimeError, 'Bad type for a class variable in %s' %self.__class__ def getTargetNamespace(self): """return targetNamespace """ parent = self targetNamespace = 'targetNamespace' tns = self.attributes.get(targetNamespace) while not tns: parent = parent._parent() tns = parent.attributes.get(targetNamespace) return tns def getTypeDefinition(self, attribute): """attribute -- attribute with a QName value (eg. type). collection -- check types collection in parent Schema instance """ return self.getQNameAttribute('types', attribute) def getElementDeclaration(self, attribute): """attribute -- attribute with a QName value (eg. element). collection -- check elements collection in parent Schema instance. """ return self.getQNameAttribute('elements', attribute) def getQNameAttribute(self, collection, attribute): """returns object instance representing QName --> (namespace,name), or if does not exist return None. attribute -- an information item attribute, with a QName value. collection -- collection in parent Schema instance to search. """ obj = None tdc = self.attributes.get(attribute) if tdc: parent = GetSchema(self) targetNamespace = tdc.getTargetNamespace() if parent.targetNamespace == targetNamespace: item = tdc.getName() try: obj = getattr(parent, collection)[item] except KeyError, ex: raise KeyError, "targetNamespace(%s) collection(%s) has no item(%s)"\ %(targetNamespace, collection, item) elif parent.imports.has_key(targetNamespace): schema = parent.imports[targetNamespace].getSchema() item = tdc.getName() try: obj = getattr(schema, collection)[item] except KeyError, ex: raise KeyError, "targetNamespace(%s) collection(%s) has no item(%s)"\ %(targetNamespace, collection, item) return obj def getXMLNS(self, prefix=None): """deference prefix or by default xmlns, returns namespace. """ if prefix == XMLSchemaComponent.xml: return XMLNS.XML parent = self ns = self.attributes[XMLSchemaComponent.xmlns].get(prefix or\ XMLSchemaComponent.xmlns_key) while not ns: parent = parent._parent() ns = parent.attributes[XMLSchemaComponent.xmlns].get(prefix or\ XMLSchemaComponent.xmlns_key) if not ns and isinstance(parent, WSDLToolsAdapter): raise SchemaError, 'unknown prefix %s' %prefix return ns def getAttribute(self, attribute): """return requested attribute or None """ return self.attributes.get(attribute) def setAttributes(self, node): """Sets up attribute dictionary, checks for required attributes and sets default attribute values. attr is for default attribute values determined at runtime. structure of attributes dictionary ['xmlns'][xmlns_key] -- xmlns namespace ['xmlns'][prefix] -- declared namespace prefix [namespace][prefix] -- attributes declared in a namespace [attribute] -- attributes w/o prefix, default namespaces do not directly apply to attributes, ie Name can't collide with QName. """ self.attributes = {XMLSchemaComponent.xmlns:{}} for k,v in node.getAttributeDictionary().items(): prefix,value = SplitQName(k) if value == XMLSchemaComponent.xmlns: self.attributes[value][prefix or XMLSchemaComponent.xmlns_key] = v elif prefix: ns = node.getNamespace(prefix) if not ns: raise SchemaError, 'no namespace for attribute prefix %s'\ %prefix if not self.attributes.has_key(ns): self.attributes[ns] = {} elif self.attributes[ns].has_key(value): raise SchemaError, 'attribute %s declared multiple times in %s'\ %(value, ns) self.attributes[ns][value] = v elif not self.attributes.has_key(value): self.attributes[value] = v else: raise SchemaError, 'attribute %s declared multiple times' %value self.__checkAttributes() self.__setAttributeDefaults() #set QNames for k in ['type', 'element', 'base', 'ref', 'substitutionGroup', 'itemType']: if self.attributes.has_key(k): prefix, value = SplitQName(self.attributes.get(k)) self.attributes[k] = \ TypeDescriptionComponent((self.getXMLNS(prefix), value)) #Union, memberTypes is a whitespace separated list of QNames for k in ['memberTypes']: if self.attributes.has_key(k): qnames = self.attributes[k] self.attributes[k] = [] for qname in qnames.split(): prefix, value = SplitQName(qname) self.attributes['memberTypes'].append(\ TypeDescriptionComponent(\ (self.getXMLNS(prefix), value))) def getContents(self, node): """retrieve xsd contents """ return node.getContentList(*self.__class__.contents['xsd']) def __setAttributeDefaults(self): """Looks for default values for unset attributes. If class variable representing attribute is None, then it must be defined as an instance variable. """ for k,v in self.__class__.attributes.items(): if v and not self.attributes.has_key(k): if isinstance(v, types.FunctionType): self.attributes[k] = v(self) else: self.attributes[k] = v def __checkAttributes(self): """Checks that required attributes have been defined, attributes w/default cannot be required. Checks all defined attributes are legal, attribute references are not subject to this test. """ for a in self.__class__.required: if not self.attributes.has_key(a): raise SchemaError,\ 'class instance %s, missing required attribute %s'\ %(self.__class__, a) for a in self.attributes.keys(): if (a not in (XMLSchemaComponent.xmlns, XMLNS.XML)) and\ (a not in self.__class__.attributes.keys()) and not\ (self.isAttribute() and self.isReference()): raise SchemaError, '%s, unknown attribute' %a class WSDLToolsAdapter(XMLSchemaComponent): """WSDL Adapter to grab the attributes from the wsdl document node. """ attributes = {'name':None, 'targetNamespace':None} def __init__(self, wsdl): #XMLSchemaComponent.__init__(self, None) XMLSchemaComponent.__init__(self, parent=wsdl) self.setAttributes(DOMAdapter(wsdl.document)) def getImportSchemas(self): """returns WSDLTools.WSDL types Collection """ return self._parent().types class Notation(XMLSchemaComponent): """ parent: schema attributes: id -- ID name -- NCName, Required public -- token, Required system -- anyURI contents: annotation? """ required = ['name', 'public'] attributes = {'id':None, 'name':None, 'public':None, 'system':None} contents = {'xsd':('annotation')} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) for i in contents: component = SplitQName(i.getTagName())[1] if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) class Annotation(XMLSchemaComponent): """ parent: all,any,anyAttribute,attribute,attributeGroup,choice,complexContent, complexType,element,extension,field,group,import,include,key,keyref, list,notation,redefine,restriction,schema,selector,simpleContent, simpleType,union,unique attributes: id -- ID contents: (documentation | appinfo)* """ attributes = {'id':None} contents = {'xsd':('documentation', 'appinfo')} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) content = [] for i in contents: component = SplitQName(i.getTagName())[1] if component == 'documentation': #print_debug('class %s, documentation skipped' %self.__class__, 5) continue elif component == 'appinfo': #print_debug('class %s, appinfo skipped' %self.__class__, 5) continue else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) self.content = tuple(content) class Documentation(XMLSchemaComponent): """ parent: annotation attributes: source, anyURI xml:lang, language contents: mixed, any """ attributes = {'source':None, 'xml:lang':None} contents = {'xsd':('mixed', 'any')} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) content = [] for i in contents: component = SplitQName(i.getTagName())[1] if component == 'mixed': #print_debug('class %s, mixed skipped' %self.__class__, 5) continue elif component == 'any': #print_debug('class %s, any skipped' %self.__class__, 5) continue else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) self.content = tuple(content) class Appinfo(XMLSchemaComponent): """ parent: annotation attributes: source, anyURI contents: mixed, any """ attributes = {'source':None, 'anyURI':None} contents = {'xsd':('mixed', 'any')} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) content = [] for i in contents: component = SplitQName(i.getTagName())[1] if component == 'mixed': #print_debug('class %s, mixed skipped' %self.__class__, 5) continue elif component == 'any': #print_debug('class %s, any skipped' %self.__class__, 5) continue else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) self.content = tuple(content) class XMLSchemaFake: # This is temporary, for the benefit of WSDL until the real thing works. def __init__(self, element): self.targetNamespace = DOM.getAttr(element, 'targetNamespace') self.element = element class XMLSchema(XMLSchemaComponent): """A schema is a collection of schema components derived from one or more schema documents, that is, one or more element information items. It represents the abstract notion of a schema rather than a single schema document (or other representation). parent: ROOT attributes: id -- ID version -- token xml:lang -- language targetNamespace -- anyURI attributeFormDefault -- 'qualified' | 'unqualified', 'unqualified' elementFormDefault -- 'qualified' | 'unqualified', 'unqualified' blockDefault -- '#all' | list of ('substitution | 'extension' | 'restriction') finalDefault -- '#all' | list of ('extension' | 'restriction' | 'list' | 'union') contents: ((include | import | redefine | annotation)*, (attribute, attributeGroup, complexType, element, group, notation, simpleType)*, annotation*)* attributes -- schema attributes imports -- import statements includes -- include statements redefines -- types -- global simpleType, complexType definitions elements -- global element declarations attr_decl -- global attribute declarations attr_groups -- attribute Groups model_groups -- model Groups notations -- global notations """ attributes = {'id':None, 'version':None, 'xml:lang':None, 'targetNamespace':None, 'attributeFormDefault':'unqualified', 'elementFormDefault':'unqualified', 'blockDefault':None, 'finalDefault':None} contents = {'xsd':('include', 'import', 'redefine', 'annotation', 'attribute',\ 'attributeGroup', 'complexType', 'element', 'group',\ 'notation', 'simpleType', 'annotation')} empty_namespace = '' def __init__(self, parent=None): """parent -- instance variables: targetNamespace -- schema's declared targetNamespace, or empty string. _imported_schemas -- namespace keyed dict of schema dependencies, if a schema is provided instance will not resolve import statement. _included_schemas -- schemaLocation keyed dict of component schemas, if schema is provided instance will not resolve include statement. _base_url -- needed for relative URLs support, only works with URLs relative to initial document. includes -- collection of include statements imports -- collection of import statements elements -- collection of global element declarations types -- collection of global type definitions attr_decl -- collection of global attribute declarations attr_groups -- collection of global attribute group definitions model_groups -- collection of model group definitions notations -- collection of notations """ self.targetNamespace = None XMLSchemaComponent.__init__(self, parent) f = lambda k: k.attributes['name'] ns = lambda k: k.attributes['namespace'] sl = lambda k: k.attributes['schemaLocation'] self.includes = Collection(self, key=sl) self.imports = Collection(self, key=ns) self.elements = Collection(self, key=f) self.types = Collection(self, key=f) self.attr_decl = Collection(self, key=f) self.attr_groups = Collection(self, key=f) self.model_groups = Collection(self, key=f) self.notations = Collection(self, key=f) self._imported_schemas = {} self._included_schemas = {} self._base_url = None def addImportSchema(self, schema): """for resolving import statements in Schema instance schema -- schema instance _imported_schemas """ if not isinstance(schema, XMLSchema): raise TypeError, 'expecting a Schema instance' if schema.targetNamespace != self.targetNamespace: self._imported_schemas[schema.targetNamespace] = schema else: raise SchemaError, 'import schema bad targetNamespace' def addIncludeSchema(self, schemaLocation, schema): """for resolving include statements in Schema instance schemaLocation -- schema location schema -- schema instance _included_schemas """ if not isinstance(schema, XMLSchema): raise TypeError, 'expecting a Schema instance' if not schema.targetNamespace or\ schema.targetNamespace == self.targetNamespace: self._included_schemas[schemaLocation] = schema else: raise SchemaError, 'include schema bad targetNamespace' def setImportSchemas(self, schema_dict): """set the import schema dictionary, which is used to reference depedent schemas. """ self._imported_schemas = schema_dict def getImportSchemas(self): """get the import schema dictionary, which is used to reference depedent schemas. """ return self._imported_schemas def getSchemaNamespacesToImport(self): """returns tuple of namespaces the schema instance has declared itself to be depedent upon. """ return tuple(self.includes.keys()) def setIncludeSchemas(self, schema_dict): """set the include schema dictionary, which is keyed with schemaLocation (uri). This is a means of providing schemas to the current schema for content inclusion. """ self._included_schemas = schema_dict def getIncludeSchemas(self): """get the include schema dictionary, which is keyed with schemaLocation (uri). """ return self._included_schemas def getBaseUrl(self): """get base url, used for normalizing all relative uri's """ return self._base_url def setBaseUrl(self, url): """set base url, used for normalizing all relative uri's """ self._base_url = url def getElementFormDefault(self): """return elementFormDefault attribute """ return self.attributes.get('elementFormDefault') def getAttributeFormDefault(self): """return attributeFormDefault attribute """ return self.attributes.get('attributeFormDefault') def getBlockDefault(self): """return blockDefault attribute """ return self.attributes.get('blockDefault') def getFinalDefault(self): """return finalDefault attribute """ return self.attributes.get('finalDefault') def load(self, node): pnode = node.getParentNode() if pnode: pname = SplitQName(pnode.getTagName())[1] if pname == 'types': attributes = {} self.setAttributes(pnode) attributes.update(self.attributes) self.setAttributes(node) for k,v in attributes['xmlns'].items(): if not self.attributes['xmlns'].has_key(k): self.attributes['xmlns'][k] = v else: self.setAttributes(node) else: self.setAttributes(node) self.targetNamespace = self.getTargetNamespace() contents = self.getContents(node) indx = 0 num = len(contents) while indx < num: while indx < num: node = contents[indx] component = SplitQName(node.getTagName())[1] if component == 'include': tp = self.__class__.Include(self) tp.fromDom(node) self.includes[tp.attributes['schemaLocation']] = tp schema = tp.getSchema() if schema.targetNamespace and \ schema.targetNamespace != self.targetNamespace: raise SchemaError, 'included schema bad targetNamespace' for collection in ['imports','elements','types',\ 'attr_decl','attr_groups','model_groups','notations']: for k,v in getattr(schema,collection).items(): if not getattr(self,collection).has_key(k): v._parent = weakref.ref(self) getattr(self,collection)[k] = v elif component == 'import': tp = self.__class__.Import(self) tp.fromDom(node) import_ns = tp.getAttribute('namespace') if import_ns: if import_ns == self.targetNamespace: raise SchemaError,\ 'import and schema have same targetNamespace' self.imports[import_ns] = tp else: self.imports[self.__class__.empty_namespace] = tp if not self.getImportSchemas().has_key(import_ns) and\ tp.getAttribute('schemaLocation'): self.addImportSchema(tp.getSchema()) elif component == 'redefine': #print_debug('class %s, redefine skipped' %self.__class__, 5) pass elif component == 'annotation': #print_debug('class %s, annotation skipped' %self.__class__, 5) pass else: break indx += 1 # (attribute, attributeGroup, complexType, element, group, # notation, simpleType)*, annotation*)* while indx < num: node = contents[indx] component = SplitQName(node.getTagName())[1] if component == 'attribute': tp = AttributeDeclaration(self) tp.fromDom(node) self.attr_decl[tp.getAttribute('name')] = tp elif component == 'attributeGroup': tp = AttributeGroupDefinition(self) tp.fromDom(node) self.attr_groups[tp.getAttribute('name')] = tp elif component == 'complexType': tp = ComplexType(self) tp.fromDom(node) self.types[tp.getAttribute('name')] = tp elif component == 'element': tp = ElementDeclaration(self) tp.fromDom(node) self.elements[tp.getAttribute('name')] = tp elif component == 'group': tp = ModelGroupDefinition(self) tp.fromDom(node) self.model_groups[tp.getAttribute('name')] = tp elif component == 'notation': tp = Notation(self) tp.fromDom(node) self.notations[tp.getAttribute('name')] = tp elif component == 'simpleType': tp = SimpleType(self) tp.fromDom(node) self.types[tp.getAttribute('name')] = tp else: break indx += 1 while indx < num: node = contents[indx] component = SplitQName(node.getTagName())[1] if component == 'annotation': #print_debug('class %s, annotation 2 skipped' %self.__class__, 5) pass else: break indx += 1 class Import(XMLSchemaComponent): """ parent: schema attributes: id -- ID namespace -- anyURI schemaLocation -- anyURI contents: annotation? """ attributes = {'id':None, 'namespace':None, 'schemaLocation':None} contents = {'xsd':['annotation']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self._schema = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) if self.attributes['namespace'] == self._parent().attributes['targetNamespace']: raise SchemaError, 'namespace of schema and import match' for i in contents: component = SplitQName(i.getTagName())[1] if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) def getSchema(self): """if schema is not defined, first look for a Schema class instance in parent Schema. Else if not defined resolve schemaLocation and create a new Schema class instance, and keep a hard reference. """ if not self._schema: ns = self.attributes['namespace'] schema = self._parent().getImportSchemas().get(ns) if not schema and self._parent()._parent: schema = self._parent()._parent().getImportSchemas().get(ns) if not schema: url = self.attributes.get('schemaLocation') if not url: raise SchemaError, 'namespace(%s) is unknown' %ns base_url = self._parent().getBaseUrl() reader = SchemaReader(base_url=base_url) reader._imports = self._parent().getImportSchemas() reader._includes = self._parent().getIncludeSchemas() self._schema = reader.loadFromURL(url) return self._schema or schema class Include(XMLSchemaComponent): """ parent: schema attributes: id -- ID schemaLocation -- anyURI, required contents: annotation? """ required = ['schemaLocation'] attributes = {'id':None, 'schemaLocation':None} contents = {'xsd':['annotation']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self._schema = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) for i in contents: component = SplitQName(i.getTagName())[1] if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) def getSchema(self): """if schema is not defined, first look for a Schema class instance in parent Schema. Else if not defined resolve schemaLocation and create a new Schema class instance. """ if not self._schema: #schema = self._parent()._parent() schema = self._parent() #self._schema = schema.getIncludeSchemas(\ # self.attributes['schemaLocation']) self._schema = schema.getIncludeSchemas().get(\ self.attributes['schemaLocation'] ) if not self._schema: url = self.attributes['schemaLocation'] reader = SchemaReader(base_url=schema.getBaseUrl()) reader._imports = schema.getImportSchemas() reader._includes = schema.getIncludeSchemas() self._schema = reader.loadFromURL(url) return self._schema class AttributeDeclaration(XMLSchemaComponent,\ AttributeMarker,\ DeclarationMarker): """ parent: schema attributes: id -- ID name -- NCName, required type -- QName default -- string fixed -- string contents: annotation?, simpleType? """ required = ['name'] attributes = {'id':None, 'name':None, 'type':None, 'default':None, 'fixed':None} contents = {'xsd':['annotation','simpleType']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None def fromDom(self, node): """ No list or union support """ self.setAttributes(node) contents = self.getContents(node) for i in contents: component = SplitQName(i.getTagName())[1] if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) elif component == 'simpleType': self.content = AnonymousSimpleType(self) self.content.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) class LocalAttributeDeclaration(AttributeDeclaration,\ AttributeMarker,\ LocalMarker,\ DeclarationMarker): """ parent: complexType, restriction, extension, attributeGroup attributes: id -- ID name -- NCName, required type -- QName form -- ('qualified' | 'unqualified'), schema.attributeFormDefault use -- ('optional' | 'prohibited' | 'required'), optional default -- string fixed -- string contents: annotation?, simpleType? """ required = ['name'] attributes = {'id':None, 'name':None, 'type':None, 'form':lambda self: GetSchema(self).getAttributeFormDefault(), 'use':'optional', 'default':None, 'fixed':None} contents = {'xsd':['annotation','simpleType']} def __init__(self, parent): AttributeDeclaration.__init__(self, parent) self.annotation = None self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) for i in contents: component = SplitQName(i.getTagName())[1] if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) elif component == 'simpleType': self.content = AnonymousSimpleType(self) self.content.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) class AttributeWildCard(XMLSchemaComponent,\ AttributeMarker,\ DeclarationMarker,\ WildCardMarker): """ parents: complexType, restriction, extension, attributeGroup attributes: id -- ID namespace -- '##any' | '##other' | (anyURI* | '##targetNamespace' | '##local'), ##any processContents -- 'lax' | 'skip' | 'strict', strict contents: annotation? """ attributes = {'id':None, 'namespace':'##any', 'processContents':'strict'} contents = {'xsd':['annotation']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) for i in contents: component = SplitQName(i.getTagName())[1] if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) class AttributeReference(XMLSchemaComponent,\ AttributeMarker,\ ReferenceMarker): """ parents: complexType, restriction, extension, attributeGroup attributes: id -- ID ref -- QName, required use -- ('optional' | 'prohibited' | 'required'), optional default -- string fixed -- string contents: annotation? """ required = ['ref'] attributes = {'id':None, 'ref':None, 'use':'optional', 'default':None, 'fixed':None} contents = {'xsd':['annotation']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) for i in contents: component = SplitQName(i.getTagName())[1] if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) class AttributeGroupDefinition(XMLSchemaComponent,\ AttributeGroupMarker,\ DefinitionMarker): """ parents: schema, redefine attributes: id -- ID name -- NCName, required contents: annotation?, (attribute | attributeGroup)*, anyAttribute? """ required = ['name'] attributes = {'id':None, 'name':None} contents = {'xsd':['annotation']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.attr_content = None def getAttributeContent(self): return self.attr_content def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) content = [] for indx in range(len(contents)): component = SplitQName(contents[indx].getTagName())[1] if (component == 'annotation') and (not indx): self.annotation = Annotation(self) self.annotation.fromDom(contents[indx]) elif (component == 'attribute'): if contents[indx].hasattr('name'): content.append(AttributeDeclaration()) elif contents[indx].hasattr('ref'): content.append(AttributeReference()) else: raise SchemaError, 'Unknown attribute type' content[-1].fromDom(contents[indx]) elif (component == 'attributeGroup'): content.append(AttributeGroupReference()) content[-1].fromDom(contents[indx]) elif (component == 'anyAttribute') and (len(contents) == x+1): content.append(AttributeWildCard()) content[-1].fromDom(contents[indx]) else: raise SchemaError, 'Unknown component (%s)' %(contents[indx].getTagName()) self.attr_content = tuple(content) class AttributeGroupReference(XMLSchemaComponent,\ AttributeGroupMarker,\ ReferenceMarker): """ parents: complexType, restriction, extension, attributeGroup attributes: id -- ID ref -- QName, required contents: annotation? """ required = ['ref'] attributes = {'id':None, 'ref':None} contents = {'xsd':['annotation']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) for i in contents: component = SplitQName(i.getTagName())[1] if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) ###################################################### # Elements ##################################################### class IdentityConstrants(XMLSchemaComponent): """Allow one to uniquely identify nodes in a document and ensure the integrity of references between them. attributes -- dictionary of attributes selector -- XPath to selected nodes fields -- list of XPath to key field """ def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.selector = None self.fields = None self.annotation = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) fields = [] for i in contents: component = SplitQName(i.getTagName())[1] if component in self.__class__.contents['xsd']: if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) elif component == 'selector': self.selector = self.Selector(self) self.selector.fromDom(i) continue elif component == 'field': fields.append(self.Field(self)) fields[-1].fromDom(i) continue else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) self.fields = tuple(fields) class Constraint(XMLSchemaComponent): def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) for i in contents: component = SplitQName(i.getTagName())[1] if component in self.__class__.contents['xsd']: if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) class Selector(Constraint): """ parent: unique, key, keyref attributes: id -- ID xpath -- XPath subset, required contents: annotation? """ required = ['xpath'] attributes = {'id':None, 'xpath':None} contents = {'xsd':['annotation']} class Field(Constraint): """ parent: unique, key, keyref attributes: id -- ID xpath -- XPath subset, required contents: annotation? """ required = ['xpath'] attributes = {'id':None, 'xpath':None} contents = {'xsd':['annotation']} class Unique(IdentityConstrants): """ Enforce fields are unique w/i a specified scope. parent: element attributes: id -- ID name -- NCName, required contents: annotation?, selector, field+ """ required = ['name'] attributes = {'id':None, 'name':None} contents = {'xsd':['annotation', 'selector', 'field']} class Key(IdentityConstrants): """ Enforce fields are unique w/i a specified scope, and all field values are present w/i document. Fields cannot be nillable. parent: element attributes: id -- ID name -- NCName, required contents: annotation?, selector, field+ """ required = ['name'] attributes = {'id':None, 'name':None} contents = {'xsd':['annotation', 'selector', 'field']} class KeyRef(IdentityConstrants): """ Ensure a match between two sets of values in an instance. parent: element attributes: id -- ID name -- NCName, required refer -- QName, required contents: annotation?, selector, field+ """ required = ['name', 'refer'] attributes = {'id':None, 'name':None, 'refer':None} contents = {'xsd':['annotation', 'selector', 'field']} class ElementDeclaration(XMLSchemaComponent,\ ElementMarker,\ DeclarationMarker): """ parents: schema attributes: id -- ID name -- NCName, required type -- QName default -- string fixed -- string nillable -- boolean, false abstract -- boolean, false substitutionGroup -- QName block -- ('#all' | ('substition' | 'extension' | 'restriction')*), schema.blockDefault final -- ('#all' | ('extension' | 'restriction')*), schema.finalDefault contents: annotation?, (simpleType,complexType)?, (key | keyref | unique)* """ required = ['name'] attributes = {'id':None, 'name':None, 'type':None, 'default':None, 'fixed':None, 'nillable':0, 'abstract':0, 'substitutionGroup':None, 'block':lambda self: self._parent().getBlockDefault(), 'final':lambda self: self._parent().getFinalDefault()} contents = {'xsd':['annotation', 'simpleType', 'complexType', 'key',\ 'keyref', 'unique']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None self.constraints = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) constraints = [] for i in contents: component = SplitQName(i.getTagName())[1] if component in self.__class__.contents['xsd']: if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) elif component == 'simpleType' and not self.content: self.content = AnonymousSimpleType(self) self.content.fromDom(i) elif component == 'complexType' and not self.content: self.content = LocalComplexType(self) self.content.fromDom(i) elif component == 'key': constraints.append(Key(self)) constraints[-1].fromDom(i) elif component == 'keyref': constraints.append(KeyRef(self)) constraints[-1].fromDom(i) elif component == 'unique': constraints.append(Unique(self)) constraints[-1].fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) self.constraints = tuple(constraints) class LocalElementDeclaration(ElementDeclaration,\ LocalMarker): """ parents: all, choice, sequence attributes: id -- ID name -- NCName, required form -- ('qualified' | 'unqualified'), schema.elementFormDefault type -- QName minOccurs -- Whole Number, 1 maxOccurs -- (Whole Number | 'unbounded'), 1 default -- string fixed -- string nillable -- boolean, false block -- ('#all' | ('extension' | 'restriction')*), schema.blockDefault contents: annotation?, (simpleType,complexType)?, (key | keyref | unique)* """ required = ['name'] attributes = {'id':None, 'name':None, 'form':lambda self: GetSchema(self).getElementFormDefault(), 'type':None, 'minOccurs':'1', 'maxOccurs':'1', 'default':None, 'fixed':None, 'nillable':0, 'abstract':0, 'block':lambda self: GetSchema(self).getBlockDefault()} contents = {'xsd':['annotation', 'simpleType', 'complexType', 'key',\ 'keyref', 'unique']} class ElementReference(XMLSchemaComponent,\ ElementMarker,\ ReferenceMarker): """ parents: all, choice, sequence attributes: id -- ID ref -- QName, required minOccurs -- Whole Number, 1 maxOccurs -- (Whole Number | 'unbounded'), 1 contents: annotation? """ required = ['ref'] attributes = {'id':None, 'ref':None, 'minOccurs':'1', 'maxOccurs':'1'} contents = {'xsd':['annotation']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None def fromDom(self, node): self.annotation = None self.setAttributes(node) for i in self.getContents(node): component = SplitQName(i.getTagName())[1] if component in self.__class__.contents['xsd']: if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) class ElementWildCard(LocalElementDeclaration,\ WildCardMarker): """ parents: choice, sequence attributes: id -- ID minOccurs -- Whole Number, 1 maxOccurs -- (Whole Number | 'unbounded'), 1 namespace -- '##any' | '##other' | (anyURI* | '##targetNamespace' | '##local'), ##any processContents -- 'lax' | 'skip' | 'strict', strict contents: annotation? """ required = [] attributes = {'id':None, 'minOccurs':'1', 'maxOccurs':'1', 'namespace':'##any', 'processContents':'strict'} contents = {'xsd':['annotation']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None def fromDom(self, node): self.annotation = None self.setAttributes(node) for i in self.getContents(node): component = SplitQName(i.getTagName())[1] if component in self.__class__.contents['xsd']: if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) ###################################################### # Model Groups ##################################################### class Sequence(XMLSchemaComponent,\ ModelGroupMarker): """ parents: complexType, extension, restriction, group, choice, sequence attributes: id -- ID minOccurs -- Whole Number, 1 maxOccurs -- (Whole Number | 'unbounded'), 1 contents: annotation?, (element | group | choice | sequence | any)* """ attributes = {'id':None, 'minOccurs':'1', 'maxOccurs':'1'} contents = {'xsd':['annotation', 'element', 'group', 'choice', 'sequence',\ 'any']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) content = [] for i in contents: component = SplitQName(i.getTagName())[1] if component in self.__class__.contents['xsd']: if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) continue elif component == 'element': if i.hasattr('ref'): content.append(ElementReference(self)) else: content.append(LocalElementDeclaration(self)) elif component == 'group': content.append(ModelGroupReference(self)) elif component == 'choice': content.append(Choice(self)) elif component == 'sequence': content.append(Sequence(self)) elif component == 'any': content.append(ElementWildCard(self)) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) content[-1].fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) self.content = tuple(content) class All(XMLSchemaComponent,\ ModelGroupMarker): """ parents: complexType, extension, restriction, group attributes: id -- ID minOccurs -- '0' | '1', 1 maxOccurs -- '1', 1 contents: annotation?, element* """ attributes = {'id':None, 'minOccurs':'1', 'maxOccurs':'1'} contents = {'xsd':['annotation', 'element']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) content = [] for i in contents: component = SplitQName(i.getTagName())[1] if component in self.__class__.contents['xsd']: if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) continue elif component == 'element': if i.hasattr('ref'): content.append(ElementReference(self)) else: content.append(LocalElementDeclaration(self)) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) content[-1].fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) self.content = tuple(content) class Choice(XMLSchemaComponent,\ ModelGroupMarker): """ parents: complexType, extension, restriction, group, choice, sequence attributes: id -- ID minOccurs -- Whole Number, 1 maxOccurs -- (Whole Number | 'unbounded'), 1 contents: annotation?, (element | group | choice | sequence | any)* """ attributes = {'id':None, 'minOccurs':'1', 'maxOccurs':'1'} contents = {'xsd':['annotation', 'element', 'group', 'choice', 'sequence',\ 'any']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) content = [] for i in contents: component = SplitQName(i.getTagName())[1] if component in self.__class__.contents['xsd']: if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) continue elif component == 'element': if i.hasattr('ref'): content.append(ElementReference(self)) else: content.append(LocalElementDeclaration(self)) elif component == 'group': content.append(ModelGroupReference(self)) elif component == 'choice': content.append(Choice(self)) elif component == 'sequence': content.append(Sequence(self)) elif component == 'any': content.append(ElementWildCard(self)) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) content[-1].fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) self.content = tuple(content) class ModelGroupDefinition(XMLSchemaComponent,\ ModelGroupMarker,\ DefinitionMarker): """ parents: redefine, schema attributes: id -- ID name -- NCName, required contents: annotation?, (all | choice | sequence)? """ required = ['name'] attributes = {'id':None, 'name':None} contents = {'xsd':['annotation', 'all', 'choice', 'sequence']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) for i in contents: component = SplitQName(i.getTagName())[1] if component in self.__class__.contents['xsd']: if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) continue elif component == 'all' and not self.content: self.content = All(self) elif component == 'choice' and not self.content: self.content = Choice(self) elif component == 'sequence' and not self.content: self.content = Sequence(self) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) self.content.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) class ModelGroupReference(XMLSchemaComponent,\ ModelGroupMarker,\ ReferenceMarker): """ parents: choice, complexType, extension, restriction, sequence attributes: id -- ID ref -- NCName, required minOccurs -- Whole Number, 1 maxOccurs -- (Whole Number | 'unbounded'), 1 contents: annotation? """ required = ['ref'] attributes = {'id':None, 'ref':None, 'minOccurs':'1', 'maxOccurs':'1'} contents = {'xsd':['annotation']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) for i in contents: component = SplitQName(i.getTagName())[1] if component in self.__class__.contents['xsd']: if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) class ComplexType(XMLSchemaComponent,\ DefinitionMarker,\ ComplexMarker): """ parents: redefine, schema attributes: id -- ID name -- NCName, required mixed -- boolean, false abstract -- boolean, false block -- ('#all' | ('extension' | 'restriction')*), schema.blockDefault final -- ('#all' | ('extension' | 'restriction')*), schema.finalDefault contents: annotation?, (simpleContent | complexContent | ((group | all | choice | sequence)?, (attribute | attributeGroup)*, anyAttribute?)) """ required = ['name'] attributes = {'id':None, 'name':None, 'mixed':0, 'abstract':0, 'block':lambda self: self._parent().getBlockDefault(), 'final':lambda self: self._parent().getFinalDefault()} contents = {'xsd':['annotation', 'simpleContent', 'complexContent',\ 'group', 'all', 'choice', 'sequence', 'attribute', 'attributeGroup',\ 'anyAttribute', 'any']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None self.attr_content = None def getAttributeContent(self): return self.attr_content def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) indx = 0 num = len(contents) #XXX ugly if not num: return component = SplitQName(contents[indx].getTagName())[1] if component == 'annotation': self.annotation = Annotation(self) self.annotation.fromDom(contents[indx]) indx += 1 component = SplitQName(contents[indx].getTagName())[1] self.content = None if component == 'simpleContent': self.content = self.__class__.SimpleContent(self) self.content.fromDom(contents[indx]) elif component == 'complexContent': self.content = self.__class__.ComplexContent(self) self.content.fromDom(contents[indx]) else: if component == 'all': self.content = All(self) elif component == 'choice': self.content = Choice(self) elif component == 'sequence': self.content = Sequence(self) elif component == 'group': self.content = ModelGroupReference(self) if self.content: self.content.fromDom(contents[indx]) indx += 1 self.attr_content = [] while indx < num: component = SplitQName(contents[indx].getTagName())[1] if component == 'attribute': if contents[indx].hasattr('ref'): self.attr_content.append(AttributeReference(self)) else: self.attr_content.append(LocalAttributeDeclaration(self)) elif component == 'attributeGroup': self.attr_content.append(AttributeGroupReference(self)) elif component == 'anyAttribute': self.attr_content.append(AttributeWildCard(self)) else: raise SchemaError, 'Unknown component (%s)' %(contents[indx].getTagName()) self.attr_content[-1].fromDom(contents[indx]) indx += 1 class _DerivedType(XMLSchemaComponent): def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.derivation = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) for i in contents: component = SplitQName(i.getTagName())[1] if component in self.__class__.contents['xsd']: if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) continue elif component == 'restriction' and not self.derivation: self.derivation = self.__class__.Restriction(self) elif component == 'extension' and not self.derivation: self.derivation = self.__class__.Extension(self) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) self.derivation.fromDom(i) class ComplexContent(_DerivedType,\ ComplexMarker): """ parents: complexType attributes: id -- ID mixed -- boolean, false contents: annotation?, (restriction | extension) """ attributes = {'id':None, 'mixed':0 } contents = {'xsd':['annotation', 'restriction', 'extension']} class _DerivationBase(XMLSchemaComponent): """, parents: complexContent attributes: id -- ID base -- QName, required contents: annotation?, (group | all | choice | sequence)?, (attribute | attributeGroup)*, anyAttribute? """ required = ['base'] attributes = {'id':None, 'base':None } contents = {'xsd':['annotation', 'group', 'all', 'choice',\ 'sequence', 'attribute', 'attributeGroup', 'anyAttribute']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None self.attr_content = None def getAttributeContent(self): return self.attr_content def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) indx = 0 num = len(contents) #XXX ugly if not num: return component = SplitQName(contents[indx].getTagName())[1] if component == 'annotation': self.annotation = Annotation(self) self.annotation.fromDom(contents[indx]) indx += 1 component = SplitQName(contents[indx].getTagName())[1] if component == 'all': self.content = All(self) self.content.fromDom(contents[indx]) indx += 1 elif component == 'choice': self.content = Choice(self) self.content.fromDom(contents[indx]) indx += 1 elif component == 'sequence': self.content = Sequence(self) self.content.fromDom(contents[indx]) indx += 1 elif component == 'group': self.content = ModelGroupReference(self) self.content.fromDom(contents[indx]) indx += 1 else: self.content = None self.attr_content = [] while indx < num: component = SplitQName(contents[indx].getTagName())[1] if component == 'attribute': if contents[indx].hasattr('ref'): self.attr_content.append(AttributeReference(self)) else: self.attr_content.append(LocalAttributeDeclaration(self)) elif component == 'attributeGroup': if contents[indx].hasattr('ref'): self.attr_content.append(AttributeGroupReference(self)) else: self.attr_content.append(AttributeGroupDefinition(self)) elif component == 'anyAttribute': self.attr_content.append(AttributeWildCard(self)) else: raise SchemaError, 'Unknown component (%s)' %(contents[indx].getTagName()) self.attr_content[-1].fromDom(contents[indx]) indx += 1 class Extension(_DerivationBase, ExtensionMarker): """ parents: complexContent attributes: id -- ID base -- QName, required contents: annotation?, (group | all | choice | sequence)?, (attribute | attributeGroup)*, anyAttribute? """ pass class Restriction(_DerivationBase,\ RestrictionMarker): """ parents: complexContent attributes: id -- ID base -- QName, required contents: annotation?, (group | all | choice | sequence)?, (attribute | attributeGroup)*, anyAttribute? """ pass class SimpleContent(_DerivedType,\ SimpleMarker): """ parents: complexType attributes: id -- ID contents: annotation?, (restriction | extension) """ attributes = {'id':None} contents = {'xsd':['annotation', 'restriction', 'extension']} class Extension(XMLSchemaComponent,\ ExtensionMarker): """ parents: simpleContent attributes: id -- ID base -- QName, required contents: annotation?, (attribute | attributeGroup)*, anyAttribute? """ required = ['base'] attributes = {'id':None, 'base':None } contents = {'xsd':['annotation', 'attribute', 'attributeGroup', 'anyAttribute']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.attr_content = None def getAttributeContent(self): return self.attr_content def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) indx = 0 num = len(contents) component = SplitQName(contents[indx].getTagName())[1] if component == 'annotation': self.annotation = Annotation(self) self.annotation.fromDom(contents[indx]) indx += 1 component = SplitQName(contents[indx].getTagName())[1] content = [] while indx < num: component = SplitQName(contents[indx].getTagName())[1] if component == 'attribute': if contents[indx].hasattr('ref'): content.append(AttributeReference(self)) else: content.append(LocalAttributeDeclaration(self)) elif component == 'attributeGroup': content.append(AttributeGroupReference(self)) elif component == 'anyAttribute': content.append(AttributeWildCard(self)) else: raise SchemaError, 'Unknown component (%s)'\ %(contents[indx].getTagName()) content[-1].fromDom(contents[indx]) indx += 1 self.attr_content = tuple(content) class Restriction(XMLSchemaComponent,\ RestrictionMarker): """ parents: simpleContent attributes: id -- ID base -- QName, required contents: annotation?, simpleType?, (enumeration | length | maxExclusive | maxInclusive | maxLength | minExclusive | minInclusive | minLength | pattern | fractionDigits | totalDigits | whiteSpace)*, (attribute | attributeGroup)*, anyAttribute? """ required = ['base'] attributes = {'id':None, 'base':None } contents = {'xsd':['annotation', 'simpleType', 'attribute',\ 'attributeGroup', 'anyAttribute'] + RestrictionMarker.facets} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None self.attr_content = None def getAttributeContent(self): return self.attr_content def fromDom(self, node): self.content = [] self.setAttributes(node) contents = self.getContents(node) indx = 0 num = len(contents) component = SplitQName(contents[indx].getTagName())[1] if component == 'annotation': self.annotation = Annotation(self) self.annotation.fromDom(contents[indx]) indx += 1 component = SplitQName(contents[indx].getTagName())[1] content = [] while indx < num: component = SplitQName(contents[indx].getTagName())[1] if component == 'attribute': if contents[indx].hasattr('ref'): content.append(AttributeReference(self)) else: content.append(LocalAttributeDeclaration(self)) elif component == 'attributeGroup': content.append(AttributeGroupReference(self)) elif component == 'anyAttribute': content.append(AttributeWildCard(self)) elif component == 'simpleType': self.content.append(LocalSimpleType(self)) self.content[-1].fromDom(contents[indx]) else: raise SchemaError, 'Unknown component (%s)'\ %(contents[indx].getTagName()) content[-1].fromDom(contents[indx]) indx += 1 self.attr_content = tuple(content) class LocalComplexType(ComplexType,\ LocalMarker): """ parents: element attributes: id -- ID mixed -- boolean, false contents: annotation?, (simpleContent | complexContent | ((group | all | choice | sequence)?, (attribute | attributeGroup)*, anyAttribute?)) """ required = [] attributes = {'id':None, 'mixed':0} class SimpleType(XMLSchemaComponent,\ DefinitionMarker,\ SimpleMarker): """ parents: redefine, schema attributes: id -- ID name -- NCName, required final -- ('#all' | ('extension' | 'restriction' | 'list' | 'union')*), schema.finalDefault contents: annotation?, (restriction | list | union) """ required = ['name'] attributes = {'id':None, 'name':None, 'final':lambda self: self._parent().getFinalDefault()} contents = {'xsd':['annotation', 'restriction', 'list', 'union']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) for child in contents: component = SplitQName(child.getTagName())[1] if component == 'annotation': self.annotation = Annotation(self) self.annotation.fromDom(child) continue break else: return if component == 'restriction': self.content = self.__class__.Restriction(self) elif component == 'list': self.content = self.__class__.List(self) elif component == 'union': self.content = self.__class__.Union(self) else: raise SchemaError, 'Unknown component (%s)' %(component) self.content.fromDom(child) class Restriction(XMLSchemaComponent,\ RestrictionMarker): """ parents: simpleType attributes: id -- ID base -- QName, required or simpleType child contents: annotation?, simpleType?, (enumeration | length | maxExclusive | maxInclusive | maxLength | minExclusive | minInclusive | minLength | pattern | fractionDigits | totalDigits | whiteSpace)* """ attributes = {'id':None, 'base':None } contents = {'xsd':['annotation', 'simpleType']+RestrictionMarker.facets} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) content = [] for indx in range(len(contents)): component = SplitQName(contents[indx].getTagName())[1] if (component == 'annotation') and (not indx): self.annotation = Annotation(self) self.annotation.fromDom(contents[indx]) continue elif (component == 'simpleType') and (not indx or indx == 1): content.append(AnonymousSimpleType(self)) content[-1].fromDom(contents[indx]) elif component in RestrictionMarker.facets: #print_debug('%s class instance, skipping %s' %(self.__class__, component)) pass else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) self.content = tuple(content) class Union(XMLSchemaComponent): """ parents: simpleType attributes: id -- ID memberTypes -- list of QNames, required or simpleType child. contents: annotation?, simpleType* """ attributes = {'id':None, 'memberTypes':None } contents = {'xsd':['annotation', 'simpleType']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) content = [] for indx in range(len(contents)): component = SplitQName(contents[indx].getTagName())[1] if (component == 'annotation') and (not indx): self.annotation = Annotation(self) self.annotation.fromDom(contents[indx]) elif (component == 'simpleType'): content.append(AnonymousSimpleType(self)) content[-1].fromDom(contents[indx]) else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) self.content = tuple(content) class List(XMLSchemaComponent): """ parents: simpleType attributes: id -- ID itemType -- QName, required or simpleType child. contents: annotation?, simpleType? """ attributes = {'id':None, 'itemType':None } contents = {'xsd':['annotation', 'simpleType']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) self.content = [] for indx in range(len(contents)): component = SplitQName(contents[indx].getTagName())[1] if (component == 'annotation') and (not indx): self.annotation = Annotation(self) self.annotation.fromDom(contents[indx]) elif (component == 'simpleType'): self.content = AnonymousSimpleType(self) self.content.fromDom(contents[indx]) break else: raise SchemaError, 'Unknown component (%s)' %(i.getTagName()) class AnonymousSimpleType(SimpleType,\ SimpleMarker): """ parents: attribute, element, list, restriction, union attributes: id -- ID contents: annotation?, (restriction | list | union) """ required = [] attributes = {'id':None} class Redefine: """ parents: attributes: contents: """ pass ########################### ########################### if sys.version_info[:2] >= (2, 2): tupleClass = tuple else: import UserTuple tupleClass = UserTuple.UserTuple class TypeDescriptionComponent(tupleClass): """Tuple of length 2, consisting of a namespace and unprefixed name. """ def __init__(self, args): """args -- (namespace, name) Remove the name's prefix, irrelevant. """ if len(args) != 2: raise TypeError, 'expecting tuple (namespace, name), got %s' %args elif args[1].find(':') >= 0: args = (args[0], SplitQName(args[1])[1]) tuple.__init__(self, args) return def getTargetNamespace(self): return self[0] def getName(self): return self[1] ''' import string, types, base64, re from Utility import DOM, Collection from StringIO import StringIO class SchemaReader: """A SchemaReader creates XMLSchema objects from urls and xml data.""" def loadFromStream(self, file): """Return an XMLSchema instance loaded from a file object.""" document = DOM.loadDocument(file) schema = XMLSchema() schema.load(document) return schema def loadFromString(self, data): """Return an XMLSchema instance loaded from an xml string.""" return self.loadFromStream(StringIO(data)) def loadFromURL(self, url): """Return an XMLSchema instance loaded from the given url.""" document = DOM.loadFromURL(url) schema = XMLSchema() schema.location = url schema.load(document) return schema def loadFromFile(self, filename): """Return an XMLSchema instance loaded from the given file.""" file = open(filename, 'rb') try: schema = self.loadFromStream(file) finally: file.close() return schema class SchemaError(Exception): pass class XMLSchema: # This is temporary, for the benefit of WSDL until the real thing works. def __init__(self, element): self.targetNamespace = DOM.getAttr(element, 'targetNamespace') self.element = element class realXMLSchema: """A schema is a collection of schema components derived from one or more schema documents, that is, one or more element information items. It represents the abstract notion of a schema rather than a single schema document (or other representation).""" def __init__(self): self.simpleTypes = Collection(self) self.complexTypes = Collection(self) self.attributes = Collection(self) self.elements = Collection(self) self.attrGroups = Collection(self) self.idConstraints=None self.modelGroups = None self.notations = None self.extensions = [] targetNamespace = None attributeFormDefault = 'unqualified' elementFormDefault = 'unqualified' blockDefault = None finalDefault = None location = None version = None id = None def load(self, document): if document.nodeType == document.DOCUMENT_NODE: schema = DOM.getElement(document, 'schema', None, None) else: schema = document if schema is None: raise SchemaError('Missing element.') self.namespace = namespace = schema.namespaceURI if not namespace in DOM.NS_XSD_ALL: raise SchemaError( 'Unknown XML schema namespace: %s.' % self.namespace ) for attrname in ( 'targetNamespace', 'attributeFormDefault', 'elementFormDefault', 'blockDefault', 'finalDefault', 'version', 'id' ): value = DOM.getAttr(schema, attrname, None, None) if value is not None: setattr(self, attrname, value) # Resolve imports and includes here? ## imported = {} ## while 1: ## imports = [] ## for element in DOM.getElements(definitions, 'import', NS_WSDL): ## location = DOM.getAttr(element, 'location') ## if not imported.has_key(location): ## imports.append(element) ## if not imports: ## break ## for element in imports: ## self._import(document, element) ## imported[location] = 1 for element in DOM.getElements(schema, None, None): localName = element.localName if not DOM.nsUriMatch(element.namespaceURI, namespace): self.extensions.append(element) continue elif localName == 'message': name = DOM.getAttr(element, 'name') docs = GetDocumentation(element) message = self.addMessage(name, docs) parts = DOM.getElements(element, 'part', NS_WSDL) message.load(parts) continue def _import(self, document, element): namespace = DOM.getAttr(element, 'namespace', default=None) location = DOM.getAttr(element, 'location', default=None) if namespace is None or location is None: raise WSDLError( 'Invalid import element (missing namespace or location).' ) # Sort-of support relative locations to simplify unit testing. The # WSDL specification actually doesn't allow relative URLs, so its # ok that this only works with urls relative to the initial document. location = urllib.basejoin(self.location, location) obimport = self.addImport(namespace, location) obimport._loaded = 1 importdoc = DOM.loadFromURL(location) try: if location.find('#') > -1: idref = location.split('#')[-1] imported = DOM.getElementById(importdoc, idref) else: imported = importdoc.documentElement if imported is None: raise WSDLError( 'Import target element not found for: %s' % location ) imported_tns = DOM.getAttr(imported, 'targetNamespace') importer_tns = namespace if imported_tns != importer_tns: return if imported.localName == 'definitions': imported_nodes = imported.childNodes else: imported_nodes = [imported] parent = element.parentNode for node in imported_nodes: if node.nodeType != node.ELEMENT_NODE: continue child = DOM.importNode(document, node, 1) parent.appendChild(child) child.setAttribute('targetNamespace', importer_tns) attrsNS = imported._attrsNS for attrkey in attrsNS.keys(): if attrkey[0] == DOM.NS_XMLNS: attr = attrsNS[attrkey].cloneNode(1) child.setAttributeNode(attr) finally: importdoc.unlink() class Element: """Common base class for element representation classes.""" def __init__(self, name=None, documentation=''): self.name = name self.documentation = documentation self.extensions = [] def addExtension(self, item): self.extensions.append(item) class SimpleTypeDefinition: """Represents an xml schema simple type definition.""" class ComplexTypeDefinition: """Represents an xml schema complex type definition.""" class AttributeDeclaration: """Represents an xml schema attribute declaration.""" class ElementDeclaration: """Represents an xml schema element declaration.""" def __init__(self, name, type=None, targetNamespace=None): self.name = name targetNamespace = None annotation = None nillable = 0 abstract = 0 default = None fixed = None scope = 'global' type = None form = 0 # Things we will not worry about for now. id_constraint_defs = None sub_group_exclude = None sub_group_affils = None disallowed_subs = None class AttributeGroupDefinition: """Represents an xml schema attribute group definition.""" class IdentityConstraintDefinition: """Represents an xml schema identity constraint definition.""" class ModelGroupDefinition: """Represents an xml schema model group definition.""" class NotationDeclaration: """Represents an xml schema notation declaration.""" class Annotation: """Represents an xml schema annotation.""" class ModelGroup: """Represents an xml schema model group.""" class Particle: """Represents an xml schema particle.""" class WildCard: """Represents an xml schema wildcard.""" class AttributeUse: """Represents an xml schema attribute use.""" class ElementComponent: namespace = '' name = '' type = None form = 'qualified | unqualified' scope = 'global or complex def' constraint = ('value', 'default | fixed') nillable = 0 id_constraint_defs = None sub_group_affil = None sub_group_exclusions = None disallowed_subs = 'substitution, extension, restriction' abstract = 0 minOccurs = 1 maxOccurs = 1 ref = '' class AttributeThing: name = '' namespace = '' typeName = '' typeUri = '' scope = 'global | local to complex def' constraint = ('value:default', 'value:fixed') use = 'optional | prohibited | required' class ElementDataType: namespace = '' name = '' element_form = 'qualified | unqualified' attr_form = None type_name = '' type_uri = '' def __init__(self, name, namespace, type_name, type_uri): self.namespace = namespace self.name = name # type may be anonymous... self.type_name = type_name self.type_uri = type_uri def checkValue(self, value, context): # Delegate value checking to the type of the element. typeref = (self.type_uri, self.type_name) handler = context.serializer.getType(typeref) return handler.checkValue(value, context) def serialize(self, name, namespace, value, context, **kwargs): if context.check_values: self.checkValue(value, context) # Delegate serialization to the type of the element. typeref = (self.type_uri, self.type_name) handler = context.serializer.getType(typeref) return handler.serialize(self.name, self.namespace, value, context) def deserialize(self, element, context): if element_is_null(element, context): return None # Delegate deserialization to the type of the element. typeref = (self.type_uri, self.type_name) handler = context.serializer.getType(typeref) return handler.deserialize(element, context) def parse_schema(data): targetNS = '' attributeFormDefault = 0 elementFormDefault = 0 blockDefault = '' finalDefault = '' language = None version = None id = '' '''