You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

334 lines
11 KiB

  1. # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
  2. #
  3. # This software is subject to the provisions of the Zope Public License,
  4. # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
  5. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  6. # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  7. # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  8. # FOR A PARTICULAR PURPOSE.
  9. import string, types, base64, re
  10. from Utility import DOM, Collection
  11. from StringIO import StringIO
  12. class SchemaReader:
  13. """A SchemaReader creates XMLSchema objects from urls and xml data."""
  14. def loadFromStream(self, file):
  15. """Return an XMLSchema instance loaded from a file object."""
  16. document = DOM.loadDocument(file)
  17. schema = XMLSchema()
  18. schema.load(document)
  19. return schema
  20. def loadFromString(self, data):
  21. """Return an XMLSchema instance loaded from an xml string."""
  22. return self.loadFromStream(StringIO(data))
  23. def loadFromURL(self, url):
  24. """Return an XMLSchema instance loaded from the given url."""
  25. document = DOM.loadFromURL(url)
  26. schema = XMLSchema()
  27. schema.location = url
  28. schema.load(document)
  29. return schema
  30. def loadFromFile(self, filename):
  31. """Return an XMLSchema instance loaded from the given file."""
  32. file = open(filename, 'rb')
  33. try: schema = self.loadFromStream(file)
  34. finally: file.close()
  35. return schema
  36. class SchemaError(Exception):
  37. pass
  38. class XMLSchema:
  39. # This is temporary, for the benefit of WSDL until the real thing works.
  40. def __init__(self, element):
  41. self.targetNamespace = DOM.getAttr(element, 'targetNamespace')
  42. self.element = element
  43. class realXMLSchema:
  44. """A schema is a collection of schema components derived from one
  45. or more schema documents, that is, one or more <schema> element
  46. information items. It represents the abstract notion of a schema
  47. rather than a single schema document (or other representation)."""
  48. def __init__(self):
  49. self.simpleTypes = Collection(self)
  50. self.complexTypes = Collection(self)
  51. self.attributes = Collection(self)
  52. self.elements = Collection(self)
  53. self.attrGroups = Collection(self)
  54. self.idConstraints=None
  55. self.modelGroups = None
  56. self.notations = None
  57. self.extensions = []
  58. targetNamespace = None
  59. attributeFormDefault = 'unqualified'
  60. elementFormDefault = 'unqualified'
  61. blockDefault = None
  62. finalDefault = None
  63. location = None
  64. version = None
  65. id = None
  66. def load(self, document):
  67. if document.nodeType == document.DOCUMENT_NODE:
  68. schema = DOM.getElement(document, 'schema', None, None)
  69. else:
  70. schema = document
  71. if schema is None:
  72. raise SchemaError('Missing <schema> element.')
  73. self.namespace = namespace = schema.namespaceURI
  74. if not namespace in DOM.NS_XSD_ALL:
  75. raise SchemaError(
  76. 'Unknown XML schema namespace: %s.' % self.namespace
  77. )
  78. for attrname in (
  79. 'targetNamespace', 'attributeFormDefault', 'elementFormDefault',
  80. 'blockDefault', 'finalDefault', 'version', 'id'
  81. ):
  82. value = DOM.getAttr(schema, attrname, None, None)
  83. if value is not None:
  84. setattr(self, attrname, value)
  85. # Resolve imports and includes here?
  86. ## imported = {}
  87. ## while 1:
  88. ## imports = []
  89. ## for element in DOM.getElements(definitions, 'import', NS_WSDL):
  90. ## location = DOM.getAttr(element, 'location')
  91. ## if not imported.has_key(location):
  92. ## imports.append(element)
  93. ## if not imports:
  94. ## break
  95. ## for element in imports:
  96. ## self._import(document, element)
  97. ## imported[location] = 1
  98. for element in DOM.getElements(schema, None, None):
  99. localName = element.localName
  100. if not DOM.nsUriMatch(element.namespaceURI, namespace):
  101. self.extensions.append(element)
  102. continue
  103. elif localName == 'message':
  104. name = DOM.getAttr(element, 'name')
  105. docs = GetDocumentation(element)
  106. message = self.addMessage(name, docs)
  107. parts = DOM.getElements(element, 'part', NS_WSDL)
  108. message.load(parts)
  109. continue
  110. def _import(self, document, element):
  111. namespace = DOM.getAttr(element, 'namespace', default=None)
  112. location = DOM.getAttr(element, 'location', default=None)
  113. if namespace is None or location is None:
  114. raise WSDLError(
  115. 'Invalid import element (missing namespace or location).'
  116. )
  117. # Sort-of support relative locations to simplify unit testing. The
  118. # WSDL specification actually doesn't allow relative URLs, so its
  119. # ok that this only works with urls relative to the initial document.
  120. location = urllib.basejoin(self.location, location)
  121. obimport = self.addImport(namespace, location)
  122. obimport._loaded = 1
  123. importdoc = DOM.loadFromURL(location)
  124. try:
  125. if location.find('#') > -1:
  126. idref = location.split('#')[-1]
  127. imported = DOM.getElementById(importdoc, idref)
  128. else:
  129. imported = importdoc.documentElement
  130. if imported is None:
  131. raise WSDLError(
  132. 'Import target element not found for: %s' % location
  133. )
  134. imported_tns = DOM.getAttr(imported, 'targetNamespace')
  135. importer_tns = namespace
  136. if imported_tns != importer_tns:
  137. return
  138. if imported.localName == 'definitions':
  139. imported_nodes = imported.childNodes
  140. else:
  141. imported_nodes = [imported]
  142. parent = element.parentNode
  143. for node in imported_nodes:
  144. if node.nodeType != node.ELEMENT_NODE:
  145. continue
  146. child = DOM.importNode(document, node, 1)
  147. parent.appendChild(child)
  148. child.setAttribute('targetNamespace', importer_tns)
  149. attrsNS = imported._attrsNS
  150. for attrkey in attrsNS.keys():
  151. if attrkey[0] == DOM.NS_XMLNS:
  152. attr = attrsNS[attrkey].cloneNode(1)
  153. child.setAttributeNode(attr)
  154. finally:
  155. importdoc.unlink()
  156. class Element:
  157. """Common base class for element representation classes."""
  158. def __init__(self, name=None, documentation=''):
  159. self.name = name
  160. self.documentation = documentation
  161. self.extensions = []
  162. def addExtension(self, item):
  163. self.extensions.append(item)
  164. class SimpleTypeDefinition:
  165. """Represents an xml schema simple type definition."""
  166. class ComplexTypeDefinition:
  167. """Represents an xml schema complex type definition."""
  168. class AttributeDeclaration:
  169. """Represents an xml schema attribute declaration."""
  170. class ElementDeclaration:
  171. """Represents an xml schema element declaration."""
  172. def __init__(self, name, type=None, targetNamespace=None):
  173. self.name = name
  174. targetNamespace = None
  175. annotation = None
  176. nillable = 0
  177. abstract = 0
  178. default = None
  179. fixed = None
  180. scope = 'global'
  181. type = None
  182. form = 0
  183. # Things we will not worry about for now.
  184. id_constraint_defs = None
  185. sub_group_exclude = None
  186. sub_group_affils = None
  187. disallowed_subs = None
  188. class AttributeGroupDefinition:
  189. """Represents an xml schema attribute group definition."""
  190. class IdentityConstraintDefinition:
  191. """Represents an xml schema identity constraint definition."""
  192. class ModelGroupDefinition:
  193. """Represents an xml schema model group definition."""
  194. class NotationDeclaration:
  195. """Represents an xml schema notation declaration."""
  196. class Annotation:
  197. """Represents an xml schema annotation."""
  198. class ModelGroup:
  199. """Represents an xml schema model group."""
  200. class Particle:
  201. """Represents an xml schema particle."""
  202. class WildCard:
  203. """Represents an xml schema wildcard."""
  204. class AttributeUse:
  205. """Represents an xml schema attribute use."""
  206. class ElementComponent:
  207. namespace = ''
  208. name = ''
  209. type = None
  210. form = 'qualified | unqualified'
  211. scope = 'global or complex def'
  212. constraint = ('value', 'default | fixed')
  213. nillable = 0
  214. id_constraint_defs = None
  215. sub_group_affil = None
  216. sub_group_exclusions = None
  217. disallowed_subs = 'substitution, extension, restriction'
  218. abstract = 0
  219. minOccurs = 1
  220. maxOccurs = 1
  221. ref = ''
  222. class AttributeThing:
  223. name = ''
  224. namespace = ''
  225. typeName = ''
  226. typeUri = ''
  227. scope = 'global | local to complex def'
  228. constraint = ('value:default', 'value:fixed')
  229. use = 'optional | prohibited | required'
  230. class ElementDataType:
  231. namespace = ''
  232. name = ''
  233. element_form = 'qualified | unqualified'
  234. attr_form = None
  235. type_name = ''
  236. type_uri = ''
  237. def __init__(self, name, namespace, type_name, type_uri):
  238. self.namespace = namespace
  239. self.name = name
  240. # type may be anonymous...
  241. self.type_name = type_name
  242. self.type_uri = type_uri
  243. def checkValue(self, value, context):
  244. # Delegate value checking to the type of the element.
  245. typeref = (self.type_uri, self.type_name)
  246. handler = context.serializer.getType(typeref)
  247. return handler.checkValue(value, context)
  248. def serialize(self, name, namespace, value, context, **kwargs):
  249. if context.check_values:
  250. self.checkValue(value, context)
  251. # Delegate serialization to the type of the element.
  252. typeref = (self.type_uri, self.type_name)
  253. handler = context.serializer.getType(typeref)
  254. return handler.serialize(self.name, self.namespace, value, context)
  255. def deserialize(self, element, context):
  256. if element_is_null(element, context):
  257. return None
  258. # Delegate deserialization to the type of the element.
  259. typeref = (self.type_uri, self.type_name)
  260. handler = context.serializer.getType(typeref)
  261. return handler.deserialize(element, context)
  262. def parse_schema(data):
  263. targetNS = ''
  264. attributeFormDefault = 0
  265. elementFormDefault = 0
  266. blockDefault = ''
  267. finalDefault = ''
  268. language = None
  269. version = None
  270. id = ''