# 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. 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 = ''