bundled wstools-0.3
authorForrest Voight <forrest@forre.st>
Wed, 3 Aug 2011 19:37:29 +0000 (15:37 -0400)
committerForrest Voight <forrest@forre.st>
Wed, 3 Aug 2011 19:37:29 +0000 (15:37 -0400)
22 files changed:
wstools/.cvsignore [new file with mode: 0644]
wstools/MIMEAttachment.py [new file with mode: 0644]
wstools/Namespaces.py [new file with mode: 0755]
wstools/TimeoutSocket.py [new file with mode: 0755]
wstools/UserTuple.py [new file with mode: 0644]
wstools/Utility.py [new file with mode: 0755]
wstools/WSDLTools.py [new file with mode: 0755]
wstools/XMLSchema.py [new file with mode: 0755]
wstools/XMLname.py [new file with mode: 0644]
wstools/__init__.py [new file with mode: 0644]
wstools/c14n.py [new file with mode: 0755]
wstools/logging.py [new file with mode: 0644]
wstools/tests/.cvsignore [new file with mode: 0644]
wstools/tests/README [new file with mode: 0644]
wstools/tests/__init__.py [new file with mode: 0644]
wstools/tests/config.txt [new file with mode: 0644]
wstools/tests/schema.tar.gz [new file with mode: 0644]
wstools/tests/test_t1.py [new file with mode: 0644]
wstools/tests/test_wsdl.py [new file with mode: 0644]
wstools/tests/test_wstools.py [new file with mode: 0644]
wstools/tests/test_wstools_net.py [new file with mode: 0644]
wstools/tests/xmethods.tar.gz [new file with mode: 0644]

diff --git a/wstools/.cvsignore b/wstools/.cvsignore
new file mode 100644 (file)
index 0000000..0d20b64
--- /dev/null
@@ -0,0 +1 @@
+*.pyc
diff --git a/wstools/MIMEAttachment.py b/wstools/MIMEAttachment.py
new file mode 100644 (file)
index 0000000..2376cc8
--- /dev/null
@@ -0,0 +1,110 @@
+#TODO add the license
+#I had to rewrite this class because the python MIME email.mime (version 2.5)
+#are buggy, they use \n instead \r\n for new line which is not compliant
+#to standard!
+# http://bugs.python.org/issue5525
+
+#TODO do not load all the message in memory stream it from the disk
+
+import re
+import random
+import sys
+
+
+#new line
+NL='\r\n'
+
+_width = len(repr(sys.maxint-1))
+_fmt = '%%0%dd' % _width
+
+class MIMEMessage:
+
+    def __init__(self):
+        self._files = []
+        self._xmlMessage = ""
+        self._startCID = ""
+        self._boundary = ""
+
+    def makeBoundary(self):
+        #create the boundary 
+        msgparts = []
+        msgparts.append(self._xmlMessage)
+        for i in self._files:
+            msgparts.append(i.read())
+        #this sucks, all in memory
+        alltext = NL.join(msgparts)
+        self._boundary  = _make_boundary(alltext)
+        #maybe I can save some memory
+        del alltext
+        del msgparts
+        self._startCID =  "<" + (_fmt % random.randrange(sys.maxint)) + (_fmt % random.randrange(sys.maxint)) + ">"
+
+
+    def toString(self):
+        '''it return a string with the MIME message'''
+        if len(self._boundary) == 0:
+            #the makeBoundary hasn't been called yet
+            self.makeBoundary()
+        #ok we have everything let's start to spit the message out
+        #first the XML
+        returnstr = NL + "--" + self._boundary + NL
+        returnstr += "Content-Type: text/xml; charset=\"us-ascii\"" + NL
+        returnstr += "Content-Transfer-Encoding: 7bit" + NL
+        returnstr += "Content-Id: " + self._startCID + NL + NL
+        returnstr += self._xmlMessage + NL
+        #then the files
+        for file in self._files:
+            returnstr += "--" + self._boundary + NL
+            returnstr += "Content-Type: application/octet-stream" + NL
+            returnstr += "Content-Transfer-Encoding: binary" + NL
+            returnstr += "Content-Id: <" + str(id(file)) + ">" + NL + NL
+            file.seek(0)
+            returnstr += file.read() + NL
+        #closing boundary
+        returnstr += "--" + self._boundary + "--" + NL 
+        return returnstr
+
+    def attachFile(self, file):
+        '''
+        it adds a file to this attachment
+        '''
+        self._files.append(file)
+
+    def addXMLMessage(self, xmlMessage):
+        '''
+        it adds the XML message. we can have only one XML SOAP message
+        '''
+        self._xmlMessage = xmlMessage
+
+    def getBoundary(self):
+        '''
+        this function returns the string used in the mime message as a 
+        boundary. First the write method as to be called
+        '''
+        return self._boundary
+
+    def getStartCID(self):
+        '''
+        This function returns the CID of the XML message
+        '''
+        return self._startCID
+
+
+def _make_boundary(text=None):
+    #some code taken from python stdlib
+    # Craft a random boundary.  If text is given, ensure that the chosen
+    # boundary doesn't appear in the text.
+    token = random.randrange(sys.maxint)
+    boundary = ('=' * 10) + (_fmt % token) + '=='
+    if text is None:
+        return boundary
+    b = boundary
+    counter = 0
+    while True:
+        cre = re.compile('^--' + re.escape(b) + '(--)?$', re.MULTILINE)
+        if not cre.search(text):
+            break
+        b = boundary + '.' + str(counter)
+        counter += 1
+    return b
+
diff --git a/wstools/Namespaces.py b/wstools/Namespaces.py
new file mode 100755 (executable)
index 0000000..46a8b05
--- /dev/null
@@ -0,0 +1,214 @@
+# 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.
+"""Namespace module, so you don't need PyXML 
+"""
+
+ident = "$Id$"
+try:
+    from xml.ns import SOAP, SCHEMA, WSDL, XMLNS, DSIG, ENCRYPTION
+    DSIG.C14N       = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
+    
+except:
+    class SOAP:
+        ENV         = "http://schemas.xmlsoap.org/soap/envelope/"
+        ENC         = "http://schemas.xmlsoap.org/soap/encoding/"
+        ACTOR_NEXT  = "http://schemas.xmlsoap.org/soap/actor/next"
+
+    class SCHEMA:
+        XSD1        = "http://www.w3.org/1999/XMLSchema"
+        XSD2        = "http://www.w3.org/2000/10/XMLSchema"
+        XSD3        = "http://www.w3.org/2001/XMLSchema"
+        XSD_LIST    = [ XSD1, XSD2, XSD3]
+        XSI1        = "http://www.w3.org/1999/XMLSchema-instance"
+        XSI2        = "http://www.w3.org/2000/10/XMLSchema-instance"
+        XSI3        = "http://www.w3.org/2001/XMLSchema-instance"
+        XSI_LIST    = [ XSI1, XSI2, XSI3 ]
+        BASE        = XSD3
+
+    class WSDL:
+        BASE        = "http://schemas.xmlsoap.org/wsdl/"
+        BIND_HTTP   = "http://schemas.xmlsoap.org/wsdl/http/"
+        BIND_MIME   = "http://schemas.xmlsoap.org/wsdl/mime/"
+        BIND_SOAP   = "http://schemas.xmlsoap.org/wsdl/soap/"
+        BIND_SOAP12 = "http://schemas.xmlsoap.org/wsdl/soap12/"
+
+    class XMLNS:
+        BASE        = "http://www.w3.org/2000/xmlns/"
+        XML         = "http://www.w3.org/XML/1998/namespace"
+        HTML        = "http://www.w3.org/TR/REC-html40"
+
+    class DSIG:
+        BASE         = "http://www.w3.org/2000/09/xmldsig#"
+        C14N         = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
+        C14N_COMM    = "http://www.w3.org/TR/2000/CR-xml-c14n-20010315#WithComments"
+        C14N_EXCL    = "http://www.w3.org/2001/10/xml-exc-c14n#"
+        DIGEST_MD2   = "http://www.w3.org/2000/09/xmldsig#md2"
+        DIGEST_MD5   = "http://www.w3.org/2000/09/xmldsig#md5"
+        DIGEST_SHA1  = "http://www.w3.org/2000/09/xmldsig#sha1"
+        ENC_BASE64   = "http://www.w3.org/2000/09/xmldsig#base64"
+        ENVELOPED    = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
+        HMAC_SHA1    = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"
+        SIG_DSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"
+        SIG_RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
+        XPATH        = "http://www.w3.org/TR/1999/REC-xpath-19991116"
+        XSLT         = "http://www.w3.org/TR/1999/REC-xslt-19991116"
+
+    class ENCRYPTION:
+        BASE    = "http://www.w3.org/2001/04/xmlenc#"
+        BLOCK_3DES    = "http://www.w3.org/2001/04/xmlenc#des-cbc"
+        BLOCK_AES128    = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"
+        BLOCK_AES192    = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"
+        BLOCK_AES256    = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
+        DIGEST_RIPEMD160    = "http://www.w3.org/2001/04/xmlenc#ripemd160"
+        DIGEST_SHA256    = "http://www.w3.org/2001/04/xmlenc#sha256"
+        DIGEST_SHA512    = "http://www.w3.org/2001/04/xmlenc#sha512"
+        KA_DH    = "http://www.w3.org/2001/04/xmlenc#dh"
+        KT_RSA_1_5    = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+        KT_RSA_OAEP    = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"
+        STREAM_ARCFOUR    = "http://www.w3.org/2001/04/xmlenc#arcfour"
+        WRAP_3DES    = "http://www.w3.org/2001/04/xmlenc#kw-3des"
+        WRAP_AES128    = "http://www.w3.org/2001/04/xmlenc#kw-aes128"
+        WRAP_AES192    = "http://www.w3.org/2001/04/xmlenc#kw-aes192"
+        WRAP_AES256    = "http://www.w3.org/2001/04/xmlenc#kw-aes256"
+
+
+class WSRF_V1_2:
+    '''OASIS WSRF Specifications Version 1.2
+    '''
+    class LIFETIME:
+        XSD_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.xsd"
+        XSD_DRAFT4 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceLifetime-1.2-draft-04.xsd"
+
+        WSDL_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.wsdl"
+        WSDL_DRAFT4 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceLifetime-1.2-draft-04.wsdl"
+        LATEST = WSDL_DRAFT4
+        WSDL_LIST = (WSDL_DRAFT1, WSDL_DRAFT4)
+        XSD_LIST = (XSD_DRAFT1, XSD_DRAFT4)
+
+    class PROPERTIES:
+        XSD_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.xsd"
+        XSD_DRAFT5 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceProperties-1.2-draft-05.xsd"
+
+        WSDL_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
+        WSDL_DRAFT5 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-ResourceProperties-1.2-draft-05.wsdl"
+        LATEST = WSDL_DRAFT5
+        WSDL_LIST = (WSDL_DRAFT1, WSDL_DRAFT5)
+        XSD_LIST = (XSD_DRAFT1, XSD_DRAFT5)
+
+    class BASENOTIFICATION:
+        XSD_DRAFT1 = "http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd"
+
+        WSDL_DRAFT1 = "http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.wsdl"
+        LATEST = WSDL_DRAFT1
+        WSDL_LIST = (WSDL_DRAFT1,)
+        XSD_LIST = (XSD_DRAFT1,)
+
+    class BASEFAULTS:
+        XSD_DRAFT1 = "http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-BaseFaults-1.2-draft-01.xsd"
+        XSD_DRAFT3 = "http://docs.oasis-open.org/wsrf/2004/11/wsrf-WS-BaseFaults-1.2-draft-03.xsd"
+        #LATEST = DRAFT3
+        #WSDL_LIST = (WSDL_DRAFT1, WSDL_DRAFT3)
+        XSD_LIST = (XSD_DRAFT1, XSD_DRAFT3)
+
+WSRF = WSRF_V1_2
+WSRFLIST = (WSRF_V1_2,)
+
+
+class OASIS:
+    '''URLs for Oasis specifications
+    '''
+    WSSE    = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
+    UTILITY = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
+    
+    class X509TOKEN:
+        Base64Binary = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
+        STRTransform = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0"
+        PKCS7 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#PKCS7"
+        X509 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509"
+        X509PKIPathv1 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"
+        X509v3SubjectKeyIdentifier = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3SubjectKeyIdentifier"
+        
+    LIFETIME = WSRF_V1_2.LIFETIME.XSD_DRAFT1
+    PROPERTIES = WSRF_V1_2.PROPERTIES.XSD_DRAFT1
+    BASENOTIFICATION = WSRF_V1_2.BASENOTIFICATION.XSD_DRAFT1
+    BASEFAULTS = WSRF_V1_2.BASEFAULTS.XSD_DRAFT1
+
+
+class APACHE:
+    '''This name space is defined by AXIS and it is used for the TC in TCapache.py,
+    Map and file attachment (DataHandler)
+    '''
+    AXIS_NS = "http://xml.apache.org/xml-soap"
+
+
+class WSTRUST:
+    BASE = "http://schemas.xmlsoap.org/ws/2004/04/trust"
+    ISSUE = "http://schemas.xmlsoap.org/ws/2004/04/trust/Issue"
+
+class WSSE:
+    BASE    = "http://schemas.xmlsoap.org/ws/2002/04/secext"
+    TRUST   = WSTRUST.BASE
+
+
+class WSU:
+    BASE    = "http://schemas.xmlsoap.org/ws/2002/04/utility"
+    UTILITY = "http://schemas.xmlsoap.org/ws/2002/07/utility"
+
+
+class WSR:
+    PROPERTIES = "http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceProperties"
+    LIFETIME   = "http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceLifetime"
+
+
+class WSA200508:
+    ADDRESS    = "http://www.w3.org/2005/08/addressing"
+    ANONYMOUS  = "%s/anonymous" %ADDRESS
+    FAULT      = "%s/fault" %ADDRESS
+
+class WSA200408:
+    ADDRESS    = "http://schemas.xmlsoap.org/ws/2004/08/addressing"
+    ANONYMOUS  = "%s/role/anonymous" %ADDRESS
+    FAULT      = "%s/fault" %ADDRESS
+
+class WSA200403:
+    ADDRESS    = "http://schemas.xmlsoap.org/ws/2004/03/addressing"
+    ANONYMOUS  = "%s/role/anonymous" %ADDRESS
+    FAULT      = "%s/fault" %ADDRESS
+
+class WSA200303:
+    ADDRESS    = "http://schemas.xmlsoap.org/ws/2003/03/addressing"
+    ANONYMOUS  = "%s/role/anonymous" %ADDRESS
+    FAULT      = None
+
+
+WSA = WSA200408
+WSA_LIST = (WSA200508, WSA200408, WSA200403, WSA200303)
+
+class _WSAW(str):
+    """ Define ADDRESS attribute to be compatible with WSA* layout """
+    ADDRESS = property(lambda s: s)
+
+WSAW200605 = _WSAW("http://www.w3.org/2006/05/addressing/wsdl")
+
+WSAW_LIST = (WSAW200605,)
+class WSP:
+    POLICY = "http://schemas.xmlsoap.org/ws/2002/12/policy"
+
+class BEA:
+    SECCONV = "http://schemas.xmlsoap.org/ws/2004/04/sc"
+    SCTOKEN = "http://schemas.xmlsoap.org/ws/2004/04/security/sc/sct"
+
+class GLOBUS:
+    SECCONV = "http://wsrf.globus.org/core/2004/07/security/secconv"
+    CORE    = "http://www.globus.org/namespaces/2004/06/core"
+    SIG     = "http://www.globus.org/2002/04/xmlenc#gssapi-sign"
+    TOKEN   = "http://www.globus.org/ws/2004/09/security/sc#GSSAPI_GSI_TOKEN"
+
+ZSI_SCHEMA_URI = 'http://www.zolera.com/schemas/ZSI/'
diff --git a/wstools/TimeoutSocket.py b/wstools/TimeoutSocket.py
new file mode 100755 (executable)
index 0000000..48b898d
--- /dev/null
@@ -0,0 +1,179 @@
+"""Based on code from timeout_socket.py, with some tweaks for compatibility.
+   These tweaks should really be rolled back into timeout_socket, but it's
+   not totally clear who is maintaining it at this point. In the meantime,
+   we'll use a different module name for our tweaked version to avoid any
+   confusion.
+
+   The original timeout_socket is by:
+
+       Scott Cotton <scott@chronis.pobox.com>
+       Lloyd Zusman <ljz@asfast.com>
+       Phil Mayes <pmayes@olivebr.com>
+       Piers Lauder <piers@cs.su.oz.au>
+       Radovan Garabik <garabik@melkor.dnp.fmph.uniba.sk>
+"""
+
+ident = "$Id$"
+
+import string, socket, select, errno
+
+WSAEINVAL = getattr(errno, 'WSAEINVAL', 10022)
+
+
+class TimeoutSocket:
+    """A socket imposter that supports timeout limits."""
+
+    def __init__(self, timeout=20, sock=None):
+        self.timeout = float(timeout)
+        self.inbuf = ''
+        if sock is None:
+            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.sock = sock
+        self.sock.setblocking(0)
+        self._rbuf = ''
+        self._wbuf = ''
+
+    def __getattr__(self, name):
+        # Delegate to real socket attributes.
+        return getattr(self.sock, name)
+
+    def connect(self, *addr):
+        timeout = self.timeout
+        sock = self.sock
+        try:
+            # Non-blocking mode
+            sock.setblocking(0)
+            apply(sock.connect, addr)
+            sock.setblocking(timeout != 0)
+            return 1
+        except socket.error,why:
+            if not timeout:
+                raise
+            sock.setblocking(1)
+            if len(why.args) == 1:
+                code = 0
+            else:
+                code, why = why
+            if code not in (
+                errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK
+                ):
+                raise
+            r,w,e = select.select([],[sock],[],timeout)
+            if w:
+                try:
+                    apply(sock.connect, addr)
+                    return 1
+                except socket.error,why:
+                    if len(why.args) == 1:
+                        code = 0
+                    else:
+                        code, why = why
+                    if code in (errno.EISCONN, WSAEINVAL):
+                        return 1
+                    raise
+        raise TimeoutError('socket connect() timeout.')
+
+    def send(self, data, flags=0):
+        total = len(data)
+        next = 0
+        while 1:
+            r, w, e = select.select([],[self.sock], [], self.timeout)
+            if w:
+                buff = data[next:next + 8192]
+                sent = self.sock.send(buff, flags)
+                next = next + sent
+                if next == total:
+                    return total
+                continue
+            raise TimeoutError('socket send() timeout.')
+
+    def recv(self, amt, flags=0):
+        if select.select([self.sock], [], [], self.timeout)[0]:
+            return self.sock.recv(amt, flags)
+        raise TimeoutError('socket recv() timeout.')
+
+    buffsize = 4096
+    handles = 1
+
+    def makefile(self, mode="r", buffsize=-1):
+        self.handles = self.handles + 1
+        self.mode = mode
+        return self
+
+    def close(self):
+        self.handles = self.handles - 1
+        if self.handles == 0 and self.sock.fileno() >= 0:
+            self.sock.close()
+
+    def read(self, n=-1):
+        if not isinstance(n, type(1)):
+            n = -1
+        if n >= 0:
+            k = len(self._rbuf)
+            if n <= k:
+                data = self._rbuf[:n]
+                self._rbuf = self._rbuf[n:]
+                return data
+            n = n - k
+            L = [self._rbuf]
+            self._rbuf = ""
+            while n > 0:
+                new = self.recv(max(n, self.buffsize))
+                if not new: break
+                k = len(new)
+                if k > n:
+                    L.append(new[:n])
+                    self._rbuf = new[n:]
+                    break
+                L.append(new)
+                n = n - k
+            return "".join(L)
+        k = max(4096, self.buffsize)
+        L = [self._rbuf]
+        self._rbuf = ""
+        while 1:
+            new = self.recv(k)
+            if not new: break
+            L.append(new)
+            k = min(k*2, 1024**2)
+        return "".join(L)
+
+    def readline(self, limit=-1):
+        data = ""
+        i = self._rbuf.find('\n')
+        while i < 0 and not (0 < limit <= len(self._rbuf)):
+            new = self.recv(self.buffsize)
+            if not new: break
+            i = new.find('\n')
+            if i >= 0: i = i + len(self._rbuf)
+            self._rbuf = self._rbuf + new
+        if i < 0: i = len(self._rbuf)
+        else: i = i+1
+        if 0 <= limit < len(self._rbuf): i = limit
+        data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
+        return data
+
+    def readlines(self, sizehint = 0):
+        total = 0
+        list = []
+        while 1:
+            line = self.readline()
+            if not line: break
+            list.append(line)
+            total += len(line)
+            if sizehint and total >= sizehint:
+                break
+        return list
+
+    def writelines(self, list):
+        self.send(''.join(list))
+
+    def write(self, data):
+        self.send(data)
+
+    def flush(self):
+        pass
+
+
+class TimeoutError(Exception):
+    pass
diff --git a/wstools/UserTuple.py b/wstools/UserTuple.py
new file mode 100644 (file)
index 0000000..b8c3653
--- /dev/null
@@ -0,0 +1,99 @@
+"""
+A more or less complete user-defined wrapper around tuple objects.
+Adapted version of the standard library's UserList.
+
+Taken from Stefan Schwarzer's ftputil library, available at
+<http://www.ndh.net/home/sschwarzer/python/python_software.html>, and used under this license:
+
+
+
+
+Copyright (C) 1999, Stefan Schwarzer 
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+- Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+- Neither the name of the above author nor the names of the
+  contributors to the software may be used to endorse or promote
+  products derived from this software without specific prior written
+  permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+
+
+
+
+# $Id$
+
+#XXX tuple instances (in Python 2.2) contain also:
+#   __class__, __delattr__, __getattribute__, __hash__, __new__,
+#   __reduce__, __setattr__, __str__
+# What about these?
+
+class UserTuple:
+    def __init__(self, inittuple=None):
+        self.data = ()
+        if inittuple is not None:
+            # XXX should this accept an arbitrary sequence?
+            if type(inittuple) == type(self.data):
+                self.data = inittuple
+            elif isinstance(inittuple, UserTuple):
+                # this results in
+                #   self.data is inittuple.data
+                # but that's ok for tuples because they are
+                # immutable. (Builtin tuples behave the same.)
+                self.data = inittuple.data[:]
+            else:
+                # the same applies here; (t is tuple(t)) == 1
+                self.data = tuple(inittuple)
+    def __repr__(self): return repr(self.data)
+    def __lt__(self, other): return self.data <  self.__cast(other)
+    def __le__(self, other): return self.data <= self.__cast(other)
+    def __eq__(self, other): return self.data == self.__cast(other)
+    def __ne__(self, other): return self.data != self.__cast(other)
+    def __gt__(self, other): return self.data >  self.__cast(other)
+    def __ge__(self, other): return self.data >= self.__cast(other)
+    def __cast(self, other):
+        if isinstance(other, UserTuple): return other.data
+        else: return other
+    def __cmp__(self, other):
+        return cmp(self.data, self.__cast(other))
+    def __contains__(self, item): return item in self.data
+    def __len__(self): return len(self.data)
+    def __getitem__(self, i): return self.data[i]
+    def __getslice__(self, i, j):
+        i = max(i, 0); j = max(j, 0)
+        return self.__class__(self.data[i:j])
+    def __add__(self, other):
+        if isinstance(other, UserTuple):
+            return self.__class__(self.data + other.data)
+        elif isinstance(other, type(self.data)):
+            return self.__class__(self.data + other)
+        else:
+            return self.__class__(self.data + tuple(other))
+    # dir( () ) contains no __radd__ (at least in Python 2.2)
+    def __mul__(self, n):
+        return self.__class__(self.data*n)
+    __rmul__ = __mul__
+
diff --git a/wstools/Utility.py b/wstools/Utility.py
new file mode 100755 (executable)
index 0000000..df536b9
--- /dev/null
@@ -0,0 +1,1382 @@
+# 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 sys, types, httplib, urllib, socket, weakref
+from os.path import isfile
+from string import join, strip, split
+from UserDict import UserDict
+from cStringIO import StringIO
+from TimeoutSocket import TimeoutSocket, TimeoutError
+from urlparse import urlparse
+from httplib import HTTPConnection, HTTPSConnection
+from exceptions import Exception
+try:
+    from ZSI import _get_idstr
+except:
+    def _get_idstr(pyobj):
+        '''Python 2.3.x generates a FutureWarning for negative IDs, so
+        we use a different prefix character to ensure uniqueness, and
+        call abs() to avoid the warning.'''
+        x = id(pyobj)
+        if x < 0:
+            return 'x%x' % abs(x)
+        return 'o%x' % x
+
+import xml.dom.minidom
+from xml.dom import Node
+
+import logging
+from c14n import Canonicalize
+from Namespaces import SCHEMA, SOAP, XMLNS, ZSI_SCHEMA_URI
+
+
+try:
+    from xml.dom.ext import SplitQName
+except:
+    def SplitQName(qname):
+        '''SplitQName(qname) -> (string, string)
+        
+           Split Qualified Name into a tuple of len 2, consisting 
+           of the prefix and the local name.  
+    
+           (prefix, localName)
+        
+           Special Cases:
+               xmlns -- (localName, 'xmlns')
+               None -- (None, localName)
+        '''
+        
+        l = qname.split(':')
+        if len(l) == 1:
+            l.insert(0, None)
+        elif len(l) == 2:
+            if l[0] == 'xmlns':
+                l.reverse()
+        else:
+            return
+        return tuple(l)
+
+#
+# python2.3 urllib.basejoin does not remove current directory ./
+# from path and this causes problems on subsequent basejoins.
+#
+basejoin = urllib.basejoin
+if sys.version_info[0:2] < (2, 4, 0, 'final', 0)[0:2]:
+    #basejoin = lambda base,url: urllib.basejoin(base,url.lstrip('./'))
+    token = './'
+    def basejoin(base, url): 
+        if url.startswith(token) is True:
+            return urllib.basejoin(base,url[2:])
+        return urllib.basejoin(base,url)
+
+class NamespaceError(Exception):
+    """Used to indicate a Namespace Error."""
+
+
+class RecursionError(Exception):
+    """Used to indicate a HTTP redirect recursion."""
+
+
+class ParseError(Exception):
+    """Used to indicate a XML parsing error."""
+
+
+class DOMException(Exception):
+    """Used to indicate a problem processing DOM."""
+
+
+class Base:
+    """Base class for instance level Logging"""
+    def __init__(self, module=__name__):
+        self.logger = logging.getLogger('%s-%s(%s)' %(module, self.__class__, _get_idstr(self)))
+
+
+class HTTPResponse:
+    """Captures the information in an HTTP response message."""
+
+    def __init__(self, response):
+        self.status = response.status
+        self.reason = response.reason
+        self.headers = response.msg
+        self.body = response.read() or None
+        response.close()
+
+class TimeoutHTTP(HTTPConnection):
+    """A custom http connection object that supports socket timeout."""
+    def __init__(self, host, port=None, timeout=20):
+        HTTPConnection.__init__(self, host, port)
+        self.timeout = timeout
+
+    def connect(self):
+        self.sock = TimeoutSocket(self.timeout)
+        self.sock.connect((self.host, self.port))
+
+
+class TimeoutHTTPS(HTTPSConnection):
+    """A custom https object that supports socket timeout. Note that this
+       is not really complete. The builtin SSL support in the Python socket
+       module requires a real socket (type) to be passed in to be hooked to
+       SSL. That means our fake socket won't work and our timeout hacks are
+       bypassed for send and recv calls. Since our hack _is_ in place at
+       connect() time, it should at least provide some timeout protection."""
+    def __init__(self, host, port=None, timeout=20, **kwargs):
+        HTTPSConnection.__init__(self, str(host), port, **kwargs)
+        self.timeout = timeout
+
+    def connect(self):
+        sock = TimeoutSocket(self.timeout)
+        sock.connect((self.host, self.port))
+        realsock = getattr(sock.sock, '_sock', sock.sock)
+        ssl = socket.ssl(realsock, self.key_file, self.cert_file)
+        self.sock = httplib.FakeSocket(sock, ssl)
+
+
+def urlopen(url, timeout=20, redirects=None):
+    """A minimal urlopen replacement hack that supports timeouts for http.
+       Note that this supports GET only."""
+    scheme, host, path, params, query, frag = urlparse(url)
+
+    if not scheme in ('http', 'https'):
+        return urllib.urlopen(url)
+    if params: path = '%s;%s' % (path, params)
+    if query:  path = '%s?%s' % (path, query)
+    if frag:   path = '%s#%s' % (path, frag)
+
+    if scheme == 'https':
+        # If ssl is not compiled into Python, you will not get an exception
+        # until a conn.endheaders() call.   We need to know sooner, so use
+        # getattr.
+        try:
+            import M2Crypto
+        except ImportError:
+            if not hasattr(socket, 'ssl'):
+                raise RuntimeError, 'no built-in SSL Support'
+
+            conn = TimeoutHTTPS(host, None, timeout)
+        else:
+            ctx = M2Crypto.SSL.Context()
+            ctx.set_session_timeout(timeout)
+            conn = M2Crypto.httpslib.HTTPSConnection(host, ssl_context=ctx)
+            conn.set_debuglevel(1)
+
+    else:
+        conn = TimeoutHTTP(host, None, timeout)
+
+    conn.putrequest('GET', path)
+    conn.putheader('Connection', 'close')
+    conn.endheaders()
+    response = None
+    while 1:
+        response = conn.getresponse()
+        if response.status != 100:
+            break
+        conn._HTTPConnection__state = httplib._CS_REQ_SENT
+        conn._HTTPConnection__response = None
+
+    status = response.status
+
+    # If we get an HTTP redirect, we will follow it automatically.
+    if status >= 300 and status < 400:
+        location = response.msg.getheader('location')
+        if location is not None:
+            response.close()
+            if redirects is not None and redirects.has_key(location):
+                raise RecursionError(
+                    'Circular HTTP redirection detected.'
+                    )
+            if redirects is None:
+                redirects = {}
+            redirects[location] = 1
+            return urlopen(location, timeout, redirects)
+        raise HTTPResponse(response)
+
+    if not (status >= 200 and status < 300):
+        raise HTTPResponse(response)
+
+    body = StringIO(response.read())
+    response.close()
+    return body
+
+class DOM:
+    """The DOM singleton defines a number of XML related constants and
+       provides a number of utility methods for DOM related tasks. It
+       also provides some basic abstractions so that the rest of the
+       package need not care about actual DOM implementation in use."""
+
+    # Namespace stuff related to the SOAP specification.
+
+    NS_SOAP_ENV_1_1 = 'http://schemas.xmlsoap.org/soap/envelope/'
+    NS_SOAP_ENC_1_1 = 'http://schemas.xmlsoap.org/soap/encoding/'
+
+    NS_SOAP_ENV_1_2 = 'http://www.w3.org/2001/06/soap-envelope'
+    NS_SOAP_ENC_1_2 = 'http://www.w3.org/2001/06/soap-encoding'
+
+    NS_SOAP_ENV_ALL = (NS_SOAP_ENV_1_1, NS_SOAP_ENV_1_2)
+    NS_SOAP_ENC_ALL = (NS_SOAP_ENC_1_1, NS_SOAP_ENC_1_2)
+
+    NS_SOAP_ENV = NS_SOAP_ENV_1_1
+    NS_SOAP_ENC = NS_SOAP_ENC_1_1
+
+    _soap_uri_mapping = {
+        NS_SOAP_ENV_1_1 : '1.1',
+        NS_SOAP_ENV_1_2 : '1.2',
+    }
+
+    SOAP_ACTOR_NEXT_1_1 = 'http://schemas.xmlsoap.org/soap/actor/next'
+    SOAP_ACTOR_NEXT_1_2 = 'http://www.w3.org/2001/06/soap-envelope/actor/next'
+    SOAP_ACTOR_NEXT_ALL = (SOAP_ACTOR_NEXT_1_1, SOAP_ACTOR_NEXT_1_2)
+    
+    def SOAPUriToVersion(self, uri):
+        """Return the SOAP version related to an envelope uri."""
+        value = self._soap_uri_mapping.get(uri)
+        if value is not None:
+            return value
+        raise ValueError(
+            'Unsupported SOAP envelope uri: %s' % uri
+            )
+
+    def GetSOAPEnvUri(self, version):
+        """Return the appropriate SOAP envelope uri for a given
+           human-friendly SOAP version string (e.g. '1.1')."""
+        attrname = 'NS_SOAP_ENV_%s' % join(split(version, '.'), '_')
+        value = getattr(self, attrname, None)
+        if value is not None:
+            return value
+        raise ValueError(
+            'Unsupported SOAP version: %s' % version
+            )
+
+    def GetSOAPEncUri(self, version):
+        """Return the appropriate SOAP encoding uri for a given
+           human-friendly SOAP version string (e.g. '1.1')."""
+        attrname = 'NS_SOAP_ENC_%s' % join(split(version, '.'), '_')
+        value = getattr(self, attrname, None)
+        if value is not None:
+            return value
+        raise ValueError(
+            'Unsupported SOAP version: %s' % version
+            )
+
+    def GetSOAPActorNextUri(self, version):
+        """Return the right special next-actor uri for a given
+           human-friendly SOAP version string (e.g. '1.1')."""
+        attrname = 'SOAP_ACTOR_NEXT_%s' % join(split(version, '.'), '_')
+        value = getattr(self, attrname, None)
+        if value is not None:
+            return value
+        raise ValueError(
+            'Unsupported SOAP version: %s' % version
+            )
+
+
+    # Namespace stuff related to XML Schema.
+
+    NS_XSD_99 = 'http://www.w3.org/1999/XMLSchema'
+    NS_XSI_99 = 'http://www.w3.org/1999/XMLSchema-instance'    
+
+    NS_XSD_00 = 'http://www.w3.org/2000/10/XMLSchema'
+    NS_XSI_00 = 'http://www.w3.org/2000/10/XMLSchema-instance'    
+
+    NS_XSD_01 = 'http://www.w3.org/2001/XMLSchema'
+    NS_XSI_01 = 'http://www.w3.org/2001/XMLSchema-instance'
+
+    NS_XSD_ALL = (NS_XSD_99, NS_XSD_00, NS_XSD_01)
+    NS_XSI_ALL = (NS_XSI_99, NS_XSI_00, NS_XSI_01)
+
+    NS_XSD = NS_XSD_01
+    NS_XSI = NS_XSI_01
+
+    _xsd_uri_mapping = {
+        NS_XSD_99 : NS_XSI_99,
+        NS_XSD_00 : NS_XSI_00,
+        NS_XSD_01 : NS_XSI_01,
+    }
+
+    for key, value in _xsd_uri_mapping.items():
+        _xsd_uri_mapping[value] = key
+
+
+    def InstanceUriForSchemaUri(self, uri):
+        """Return the appropriate matching XML Schema instance uri for
+           the given XML Schema namespace uri."""
+        return self._xsd_uri_mapping.get(uri)
+
+    def SchemaUriForInstanceUri(self, uri):
+        """Return the appropriate matching XML Schema namespace uri for
+           the given XML Schema instance namespace uri."""
+        return self._xsd_uri_mapping.get(uri)
+
+
+    # Namespace stuff related to WSDL.
+
+    NS_WSDL_1_1 = 'http://schemas.xmlsoap.org/wsdl/'
+    NS_WSDL_ALL = (NS_WSDL_1_1,)
+    NS_WSDL = NS_WSDL_1_1
+
+    NS_SOAP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/soap/'
+    NS_HTTP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/http/'
+    NS_MIME_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/mime/'
+
+    NS_SOAP_BINDING_ALL = (NS_SOAP_BINDING_1_1,)
+    NS_HTTP_BINDING_ALL = (NS_HTTP_BINDING_1_1,)
+    NS_MIME_BINDING_ALL = (NS_MIME_BINDING_1_1,)
+
+    NS_SOAP_BINDING = NS_SOAP_BINDING_1_1
+    NS_HTTP_BINDING = NS_HTTP_BINDING_1_1
+    NS_MIME_BINDING = NS_MIME_BINDING_1_1
+
+    NS_SOAP_HTTP_1_1 = 'http://schemas.xmlsoap.org/soap/http'
+    NS_SOAP_HTTP_ALL = (NS_SOAP_HTTP_1_1,)
+    NS_SOAP_HTTP = NS_SOAP_HTTP_1_1
+    
+
+    _wsdl_uri_mapping = {
+        NS_WSDL_1_1 : '1.1',
+    }
+    
+    def WSDLUriToVersion(self, uri):
+        """Return the WSDL version related to a WSDL namespace uri."""
+        value = self._wsdl_uri_mapping.get(uri)
+        if value is not None:
+            return value
+        raise ValueError(
+            'Unsupported SOAP envelope uri: %s' % uri
+            )
+
+    def GetWSDLUri(self, version):
+        attr = 'NS_WSDL_%s' % join(split(version, '.'), '_')
+        value = getattr(self, attr, None)
+        if value is not None:
+            return value
+        raise ValueError(
+            'Unsupported WSDL version: %s' % version
+            )
+
+    def GetWSDLSoapBindingUri(self, version):
+        attr = 'NS_SOAP_BINDING_%s' % join(split(version, '.'), '_')
+        value = getattr(self, attr, None)
+        if value is not None:
+            return value
+        raise ValueError(
+            'Unsupported WSDL version: %s' % version
+            )
+
+    def GetWSDLHttpBindingUri(self, version):
+        attr = 'NS_HTTP_BINDING_%s' % join(split(version, '.'), '_')
+        value = getattr(self, attr, None)
+        if value is not None:
+            return value
+        raise ValueError(
+            'Unsupported WSDL version: %s' % version
+            )
+
+    def GetWSDLMimeBindingUri(self, version):
+        attr = 'NS_MIME_BINDING_%s' % join(split(version, '.'), '_')
+        value = getattr(self, attr, None)
+        if value is not None:
+            return value
+        raise ValueError(
+            'Unsupported WSDL version: %s' % version
+            )
+
+    def GetWSDLHttpTransportUri(self, version):
+        attr = 'NS_SOAP_HTTP_%s' % join(split(version, '.'), '_')
+        value = getattr(self, attr, None)
+        if value is not None:
+            return value
+        raise ValueError(
+            'Unsupported WSDL version: %s' % version
+            )
+
+
+    # Other xml namespace constants.
+    NS_XMLNS     = 'http://www.w3.org/2000/xmlns/'
+
+
+
+    def isElement(self, node, name, nsuri=None):
+        """Return true if the given node is an element with the given
+           name and optional namespace uri."""
+        if node.nodeType != node.ELEMENT_NODE:
+            return 0
+        return node.localName == name and \
+               (nsuri is None or self.nsUriMatch(node.namespaceURI, nsuri))
+
+    def getElement(self, node, name, nsuri=None, default=join):
+        """Return the first child of node with a matching name and
+           namespace uri, or the default if one is provided."""
+        nsmatch = self.nsUriMatch
+        ELEMENT_NODE = node.ELEMENT_NODE
+        for child in node.childNodes:
+            if child.nodeType == ELEMENT_NODE:
+                if ((child.localName == name or name is None) and
+                    (nsuri is None or nsmatch(child.namespaceURI, nsuri))
+                    ):
+                    return child
+        if default is not join:
+            return default
+        raise KeyError, name
+
+    def getElementById(self, node, id, default=join):
+        """Return the first child of node matching an id reference."""
+        attrget = self.getAttr
+        ELEMENT_NODE = node.ELEMENT_NODE
+        for child in node.childNodes:
+            if child.nodeType == ELEMENT_NODE:
+                if attrget(child, 'id') == id:
+                    return child
+        if default is not join:
+            return default
+        raise KeyError, name
+
+    def getMappingById(self, document, depth=None, element=None,
+                       mapping=None, level=1):
+        """Create an id -> element mapping of those elements within a
+           document that define an id attribute. The depth of the search
+           may be controlled by using the (1-based) depth argument."""
+        if document is not None:
+            element = document.documentElement
+            mapping = {}
+        attr = element._attrs.get('id', None)
+        if attr is not None:
+            mapping[attr.value] = element
+        if depth is None or depth > level:
+            level = level + 1
+            ELEMENT_NODE = element.ELEMENT_NODE
+            for child in element.childNodes:
+                if child.nodeType == ELEMENT_NODE:
+                    self.getMappingById(None, depth, child, mapping, level)
+        return mapping        
+
+    def getElements(self, node, name, nsuri=None):
+        """Return a sequence of the child elements of the given node that
+           match the given name and optional namespace uri."""
+        nsmatch = self.nsUriMatch
+        result = []
+        ELEMENT_NODE = node.ELEMENT_NODE
+        for child in node.childNodes:
+            if child.nodeType == ELEMENT_NODE:
+                if ((child.localName == name or name is None) and (
+                    (nsuri is None) or nsmatch(child.namespaceURI, nsuri))):
+                    result.append(child)
+        return result
+
+    def hasAttr(self, node, name, nsuri=None):
+        """Return true if element has attribute with the given name and
+           optional nsuri. If nsuri is not specified, returns true if an
+           attribute exists with the given name with any namespace."""
+        if nsuri is None:
+            if node.hasAttribute(name):
+                return True
+            return False
+        return node.hasAttributeNS(nsuri, name)
+
+    def getAttr(self, node, name, nsuri=None, default=join):
+        """Return the value of the attribute named 'name' with the
+           optional nsuri, or the default if one is specified. If
+           nsuri is not specified, an attribute that matches the
+           given name will be returned regardless of namespace."""
+        if nsuri is None:
+            result = node._attrs.get(name, None)
+            if result is None:
+                for item in node._attrsNS.keys():
+                    if item[1] == name:
+                        result = node._attrsNS[item]
+                        break
+        else:
+            result = node._attrsNS.get((nsuri, name), None)
+        if result is not None:
+            return result.value
+        if default is not join:
+            return default
+        return ''
+
+    def getAttrs(self, node):
+        """Return a Collection of all attributes 
+        """
+        attrs = {}
+        for k,v in node._attrs.items():
+            attrs[k] = v.value
+        return attrs
+
+    def getElementText(self, node, preserve_ws=None):
+        """Return the text value of an xml element node. Leading and trailing
+           whitespace is stripped from the value unless the preserve_ws flag
+           is passed with a true value."""
+        result = []
+        for child in node.childNodes:
+            nodetype = child.nodeType
+            if nodetype == child.TEXT_NODE or \
+               nodetype == child.CDATA_SECTION_NODE:
+                result.append(child.nodeValue)
+        value = join(result, '')
+        if preserve_ws is None:
+            value = strip(value)
+        return value
+
+    def findNamespaceURI(self, prefix, node):
+        """Find a namespace uri given a prefix and a context node."""
+        attrkey = (self.NS_XMLNS, prefix)
+        DOCUMENT_NODE = node.DOCUMENT_NODE
+        ELEMENT_NODE = node.ELEMENT_NODE
+        while 1:
+            if node is None:
+                raise DOMException('Value for prefix %s not found.' % prefix)
+            if node.nodeType != ELEMENT_NODE:
+                node = node.parentNode
+                continue
+            result = node._attrsNS.get(attrkey, None)
+            if result is not None:
+                return result.value
+            if hasattr(node, '__imported__'):
+                raise DOMException('Value for prefix %s not found.' % prefix)
+            node = node.parentNode
+            if node.nodeType == DOCUMENT_NODE:
+                raise DOMException('Value for prefix %s not found.' % prefix)
+
+    def findDefaultNS(self, node):
+        """Return the current default namespace uri for the given node."""
+        attrkey = (self.NS_XMLNS, 'xmlns')
+        DOCUMENT_NODE = node.DOCUMENT_NODE
+        ELEMENT_NODE = node.ELEMENT_NODE
+        while 1:
+            if node.nodeType != ELEMENT_NODE:
+                node = node.parentNode
+                continue
+            result = node._attrsNS.get(attrkey, None)
+            if result is not None:
+                return result.value
+            if hasattr(node, '__imported__'):
+                raise DOMException('Cannot determine default namespace.')
+            node = node.parentNode
+            if node.nodeType == DOCUMENT_NODE:
+                raise DOMException('Cannot determine default namespace.')
+
+    def findTargetNS(self, node):
+        """Return the defined target namespace uri for the given node."""
+        attrget = self.getAttr
+        attrkey = (self.NS_XMLNS, 'xmlns')
+        DOCUMENT_NODE = node.DOCUMENT_NODE
+        ELEMENT_NODE = node.ELEMENT_NODE
+        while 1:
+            if node.nodeType != ELEMENT_NODE:
+                node = node.parentNode
+                continue
+            result = attrget(node, 'targetNamespace', default=None)
+            if result is not None:
+                return result
+            node = node.parentNode
+            if node.nodeType == DOCUMENT_NODE:
+                raise DOMException('Cannot determine target namespace.')
+
+    def getTypeRef(self, element):
+        """Return (namespaceURI, name) for a type attribue of the given
+           element, or None if the element does not have a type attribute."""
+        typeattr = self.getAttr(element, 'type', default=None)
+        if typeattr is None:
+            return None
+        parts = typeattr.split(':', 1)
+        if len(parts) == 2:
+            nsuri = self.findNamespaceURI(parts[0], element)
+        else:
+            nsuri = self.findDefaultNS(element)
+        return (nsuri, parts[1])
+
+    def importNode(self, document, node, deep=0):
+        """Implements (well enough for our purposes) DOM node import."""
+        nodetype = node.nodeType
+        if nodetype in (node.DOCUMENT_NODE, node.DOCUMENT_TYPE_NODE):
+            raise DOMException('Illegal node type for importNode')
+        if nodetype == node.ENTITY_REFERENCE_NODE:
+            deep = 0
+        clone = node.cloneNode(deep)
+        self._setOwnerDoc(document, clone)
+        clone.__imported__ = 1
+        return clone
+
+    def _setOwnerDoc(self, document, node):
+        node.ownerDocument = document
+        for child in node.childNodes:
+            self._setOwnerDoc(document, child)
+
+    def nsUriMatch(self, value, wanted, strict=0, tt=type(())):
+        """Return a true value if two namespace uri values match."""
+        if value == wanted or (type(wanted) is tt) and value in wanted:
+            return 1
+        if not strict and value is not None:
+            wanted = type(wanted) is tt and wanted or (wanted,)
+            value = value[-1:] != '/' and value or value[:-1]
+            for item in wanted:
+                if item == value or item[:-1] == value:
+                    return 1
+        return 0
+
+    def createDocument(self, nsuri, qname, doctype=None):
+        """Create a new writable DOM document object."""
+        impl = xml.dom.minidom.getDOMImplementation()
+        return impl.createDocument(nsuri, qname, doctype)
+
+    def loadDocument(self, data):
+        """Load an xml file from a file-like object and return a DOM
+           document instance."""
+        return xml.dom.minidom.parse(data)
+
+    def loadFromURL(self, url):
+        """Load an xml file from a URL and return a DOM document."""
+        if isfile(url) is True:
+            file = open(url, 'r')
+        else:
+            file = urlopen(url)
+
+        try:     
+            result = self.loadDocument(file)
+        except Exception, ex:
+            file.close()
+            raise ParseError(('Failed to load document %s' %url,) + ex.args)
+        else:
+            file.close()
+        return result
+
+DOM = DOM()
+
+
+class MessageInterface:
+    '''Higher Level Interface, delegates to DOM singleton, must 
+    be subclassed and implement all methods that throw NotImplementedError.
+    '''
+    def __init__(self, sw):
+        '''Constructor, May be extended, do not override.
+            sw -- soapWriter instance
+        '''
+        self.sw = None
+        if type(sw) != weakref.ReferenceType and sw is not None:
+            self.sw = weakref.ref(sw)
+        else:
+            self.sw = sw
+
+    def AddCallback(self, func, *arglist):
+        self.sw().AddCallback(func, *arglist)
+
+    def Known(self, obj):
+        return self.sw().Known(obj)
+
+    def Forget(self, obj):
+        return self.sw().Forget(obj)
+
+    def canonicalize(self):
+        '''canonicalize the underlying DOM, and return as string.
+        '''
+        raise NotImplementedError, ''
+
+    def createDocument(self, namespaceURI=SOAP.ENV, localName='Envelope'):
+        '''create Document
+        '''
+        raise NotImplementedError, ''
+
+    def createAppendElement(self, namespaceURI, localName):
+        '''create and append element(namespaceURI,localName), and return
+        the node.
+        '''
+        raise NotImplementedError, ''
+
+    def findNamespaceURI(self, qualifiedName):
+        raise NotImplementedError, ''
+
+    def resolvePrefix(self, prefix):
+        raise NotImplementedError, ''
+
+    def setAttributeNS(self, namespaceURI, localName, value):
+        '''set attribute (namespaceURI, localName)=value
+        '''
+        raise NotImplementedError, ''
+
+    def setAttributeType(self, namespaceURI, localName):
+        '''set attribute xsi:type=(namespaceURI, localName)
+        '''
+        raise NotImplementedError, ''
+
+    def setNamespaceAttribute(self, namespaceURI, prefix):
+        '''set namespace attribute xmlns:prefix=namespaceURI 
+        '''
+        raise NotImplementedError, ''
+
+
+class ElementProxy(Base, MessageInterface):
+    '''
+    '''
+    _soap_env_prefix = 'SOAP-ENV'
+    _soap_enc_prefix = 'SOAP-ENC'
+    _zsi_prefix = 'ZSI'
+    _xsd_prefix = 'xsd'
+    _xsi_prefix = 'xsi'
+    _xml_prefix = 'xml'
+    _xmlns_prefix = 'xmlns'
+
+    _soap_env_nsuri = SOAP.ENV
+    _soap_enc_nsuri = SOAP.ENC
+    _zsi_nsuri = ZSI_SCHEMA_URI
+    _xsd_nsuri =  SCHEMA.XSD3
+    _xsi_nsuri = SCHEMA.XSI3
+    _xml_nsuri = XMLNS.XML
+    _xmlns_nsuri = XMLNS.BASE
+
+    standard_ns = {\
+        _xml_prefix:_xml_nsuri,
+        _xmlns_prefix:_xmlns_nsuri
+    }
+    reserved_ns = {\
+        _soap_env_prefix:_soap_env_nsuri,
+        _soap_enc_prefix:_soap_enc_nsuri,
+        _zsi_prefix:_zsi_nsuri,
+        _xsd_prefix:_xsd_nsuri,
+        _xsi_prefix:_xsi_nsuri,
+    }
+    name = None
+    namespaceURI = None
+
+    def __init__(self, sw, message=None):
+        '''Initialize. 
+           sw -- SoapWriter
+        '''
+        self._indx = 0
+        MessageInterface.__init__(self, sw)
+        Base.__init__(self)
+        self._dom = DOM
+        self.node = None
+        if type(message) in (types.StringType,types.UnicodeType):
+            self.loadFromString(message)
+        elif isinstance(message, ElementProxy):
+            self.node = message._getNode()
+        else:
+            self.node = message
+        self.processorNss = self.standard_ns.copy()
+        self.processorNss.update(self.reserved_ns)
+
+    def __str__(self):
+        return self.toString()
+
+    def evaluate(self, expression, processorNss=None):
+        '''expression -- XPath compiled expression
+        '''
+        from Ft.Xml import XPath
+        if not processorNss:
+            context = XPath.Context.Context(self.node, processorNss=self.processorNss)
+        else:
+            context = XPath.Context.Context(self.node, processorNss=processorNss)
+        nodes = expression.evaluate(context)
+        return map(lambda node: ElementProxy(self.sw,node), nodes)
+
+    #############################################
+    # Methods for checking/setting the
+    # classes (namespaceURI,name) node. 
+    #############################################
+    def checkNode(self, namespaceURI=None, localName=None):
+        '''
+            namespaceURI -- namespace of element
+            localName -- local name of element
+        '''
+        namespaceURI = namespaceURI or self.namespaceURI
+        localName = localName or self.name
+        check = False
+        if localName and self.node:
+            check = self._dom.isElement(self.node, localName, namespaceURI)
+        if not check:
+            raise NamespaceError, 'unexpected node type %s, expecting %s' %(self.node, localName)
+
+    def setNode(self, node=None):
+        if node:
+            if isinstance(node, ElementProxy):
+                self.node = node._getNode()
+            else:
+                self.node = node
+        elif self.node:
+            node = self._dom.getElement(self.node, self.name, self.namespaceURI, default=None)
+            if not node:
+                raise NamespaceError, 'cant find element (%s,%s)' %(self.namespaceURI,self.name)
+            self.node = node
+        else:
+            #self.node = self._dom.create(self.node, self.name, self.namespaceURI, default=None)
+            self.createDocument(self.namespaceURI, localName=self.name, doctype=None)
+        
+        self.checkNode()
+
+    #############################################
+    # Wrapper Methods for direct DOM Element Node access
+    #############################################
+    def _getNode(self):
+        return self.node
+
+    def _getElements(self):
+        return self._dom.getElements(self.node, name=None)
+
+    def _getOwnerDocument(self):
+        return self.node.ownerDocument or self.node
+
+    def _getUniquePrefix(self):
+        '''I guess we need to resolve all potential prefixes
+        because when the current node is attached it copies the 
+        namespaces into the parent node.
+        '''
+        while 1:
+            self._indx += 1
+            prefix = 'ns%d' %self._indx
+            try:
+                self._dom.findNamespaceURI(prefix, self._getNode())
+            except DOMException, ex:
+                break
+        return prefix
+
+    def _getPrefix(self, node, nsuri):
+        '''
+        Keyword arguments:
+            node -- DOM Element Node
+            nsuri -- namespace of attribute value
+        '''
+        try:
+            if node and (node.nodeType == node.ELEMENT_NODE) and \
+                (nsuri == self._dom.findDefaultNS(node)):
+                return None
+        except DOMException, ex:
+            pass
+        if nsuri == XMLNS.XML:
+            return self._xml_prefix
+        if node.nodeType == Node.ELEMENT_NODE:
+            for attr in node.attributes.values():
+                if attr.namespaceURI == XMLNS.BASE \
+                   and nsuri == attr.value:
+                        return attr.localName
+            else:
+                if node.parentNode:
+                    return self._getPrefix(node.parentNode, nsuri)
+        raise NamespaceError, 'namespaceURI "%s" is not defined' %nsuri
+
+    def _appendChild(self, node):
+        '''
+        Keyword arguments:
+            node -- DOM Element Node
+        '''
+        if node is None:
+            raise TypeError, 'node is None'
+        self.node.appendChild(node)
+
+    def _insertBefore(self, newChild, refChild):
+        '''
+        Keyword arguments:
+            child -- DOM Element Node to insert
+            refChild -- DOM Element Node 
+        '''
+        self.node.insertBefore(newChild, refChild)
+
+    def _setAttributeNS(self, namespaceURI, qualifiedName, value):
+        '''
+        Keyword arguments:
+            namespaceURI -- namespace of attribute
+            qualifiedName -- qualified name of new attribute value
+            value -- value of attribute
+        '''
+        self.node.setAttributeNS(namespaceURI, qualifiedName, value)
+
+    #############################################
+    #General Methods
+    #############################################
+    def isFault(self):
+        '''check to see if this is a soap:fault message.
+        '''
+        return False
+
+    def getPrefix(self, namespaceURI):
+        try:
+            prefix = self._getPrefix(node=self.node, nsuri=namespaceURI)
+        except NamespaceError, ex:
+            prefix = self._getUniquePrefix() 
+            self.setNamespaceAttribute(prefix, namespaceURI)
+        return prefix
+
+    def getDocument(self):
+        return self._getOwnerDocument()
+
+    def setDocument(self, document):
+        self.node = document
+
+    def importFromString(self, xmlString):
+        doc = self._dom.loadDocument(StringIO(xmlString))
+        node = self._dom.getElement(doc, name=None)
+        clone = self.importNode(node)
+        self._appendChild(clone)
+
+    def importNode(self, node):
+        if isinstance(node, ElementProxy):
+            node = node._getNode()
+        return self._dom.importNode(self._getOwnerDocument(), node, deep=1)
+
+    def loadFromString(self, data):
+        self.node = self._dom.loadDocument(StringIO(data))
+
+    def canonicalize(self):
+        return Canonicalize(self.node)
+
+    def toString(self):
+        return self.canonicalize()
+
+    def createDocument(self, namespaceURI, localName, doctype=None):
+        '''If specified must be a SOAP envelope, else may contruct an empty document.
+        '''
+        prefix = self._soap_env_prefix
+
+        if namespaceURI == self.reserved_ns[prefix]:
+            qualifiedName = '%s:%s' %(prefix,localName)
+        elif namespaceURI is localName is None:
+            self.node = self._dom.createDocument(None,None,None)
+            return
+        else:
+            raise KeyError, 'only support creation of document in %s' %self.reserved_ns[prefix]
+
+        document = self._dom.createDocument(nsuri=namespaceURI, qname=qualifiedName, doctype=doctype)
+        self.node = document.childNodes[0]
+
+        #set up reserved namespace attributes
+        for prefix,nsuri in self.reserved_ns.items():
+            self._setAttributeNS(namespaceURI=self._xmlns_nsuri, 
+                qualifiedName='%s:%s' %(self._xmlns_prefix,prefix), 
+                value=nsuri)
+
+    #############################################
+    #Methods for attributes
+    #############################################
+    def hasAttribute(self, namespaceURI, localName):
+        return self._dom.hasAttr(self._getNode(), name=localName, nsuri=namespaceURI)
+
+    def setAttributeType(self, namespaceURI, localName):
+        '''set xsi:type
+        Keyword arguments:
+            namespaceURI -- namespace of attribute value
+            localName -- name of new attribute value
+
+        '''
+        self.logger.debug('setAttributeType: (%s,%s)', namespaceURI, localName)
+        value = localName
+        if namespaceURI:
+            value = '%s:%s' %(self.getPrefix(namespaceURI),localName)
+
+        xsi_prefix = self.getPrefix(self._xsi_nsuri)
+        self._setAttributeNS(self._xsi_nsuri, '%s:type' %xsi_prefix, value)
+
+    def createAttributeNS(self, namespace, name, value):
+        document = self._getOwnerDocument()
+        ##this function doesn't exist!! it has only two arguments
+        attrNode = document.createAttributeNS(namespace, name, value)
+
+    def setAttributeNS(self, namespaceURI, localName, value):
+        '''
+        Keyword arguments:
+            namespaceURI -- namespace of attribute to create, None is for
+                attributes in no namespace.
+            localName -- local name of new attribute
+            value -- value of new attribute
+        ''' 
+        prefix = None
+        if namespaceURI:
+            try:
+                prefix = self.getPrefix(namespaceURI)
+            except KeyError, ex:
+                prefix = 'ns2'
+                self.setNamespaceAttribute(prefix, namespaceURI)
+        qualifiedName = localName
+        if prefix:
+            qualifiedName = '%s:%s' %(prefix, localName)
+        self._setAttributeNS(namespaceURI, qualifiedName, value)
+
+    def setNamespaceAttribute(self, prefix, namespaceURI):
+        '''
+        Keyword arguments:
+            prefix -- xmlns prefix
+            namespaceURI -- value of prefix
+        '''
+        self._setAttributeNS(XMLNS.BASE, 'xmlns:%s' %prefix, namespaceURI)
+
+    #############################################
+    #Methods for elements
+    #############################################
+    def createElementNS(self, namespace, qname):
+        '''
+        Keyword arguments:
+            namespace -- namespace of element to create
+            qname -- qualified name of new element
+        '''
+        document = self._getOwnerDocument()
+        node = document.createElementNS(namespace, qname)
+        return ElementProxy(self.sw, node)
+
+    def createAppendSetElement(self, namespaceURI, localName, prefix=None):
+        '''Create a new element (namespaceURI,name), append it
+           to current node, then set it to be the current node.
+        Keyword arguments:
+            namespaceURI -- namespace of element to create
+            localName -- local name of new element
+            prefix -- if namespaceURI is not defined, declare prefix.  defaults
+                to 'ns1' if left unspecified.
+        '''
+        node = self.createAppendElement(namespaceURI, localName, prefix=None)
+        node=node._getNode()
+        self._setNode(node._getNode())
+
+    def createAppendElement(self, namespaceURI, localName, prefix=None):
+        '''Create a new element (namespaceURI,name), append it
+           to current node, and return the newly created node.
+        Keyword arguments:
+            namespaceURI -- namespace of element to create
+            localName -- local name of new element
+            prefix -- if namespaceURI is not defined, declare prefix.  defaults
+                to 'ns1' if left unspecified.
+        '''
+        declare = False
+        qualifiedName = localName
+        if namespaceURI:
+            try:
+                prefix = self.getPrefix(namespaceURI)
+            except:
+                declare = True
+                prefix = prefix or self._getUniquePrefix()
+            if prefix: 
+                qualifiedName = '%s:%s' %(prefix, localName)
+        node = self.createElementNS(namespaceURI, qualifiedName)
+        if declare:
+            node._setAttributeNS(XMLNS.BASE, 'xmlns:%s' %prefix, namespaceURI)
+        self._appendChild(node=node._getNode())
+        return node
+
+    def createInsertBefore(self, namespaceURI, localName, refChild):
+        qualifiedName = localName
+        prefix = self.getPrefix(namespaceURI)
+        if prefix: 
+            qualifiedName = '%s:%s' %(prefix, localName)
+        node = self.createElementNS(namespaceURI, qualifiedName)
+        self._insertBefore(newChild=node._getNode(), refChild=refChild._getNode())
+        return node
+
+    def getElement(self, namespaceURI, localName):
+        '''
+        Keyword arguments:
+            namespaceURI -- namespace of element
+            localName -- local name of element
+        '''
+        node = self._dom.getElement(self.node, localName, namespaceURI, default=None)
+        if node:
+            return ElementProxy(self.sw, node)
+        return None
+
+    def getAttributeValue(self, namespaceURI, localName):
+        '''
+        Keyword arguments:
+            namespaceURI -- namespace of attribute
+            localName -- local name of attribute
+        '''
+        if self.hasAttribute(namespaceURI, localName):
+            attr = self.node.getAttributeNodeNS(namespaceURI,localName)
+            return attr.value
+        return None
+
+    def getValue(self):
+        return self._dom.getElementText(self.node, preserve_ws=True)    
+
+    #############################################
+    #Methods for text nodes
+    #############################################
+    def createAppendTextNode(self, pyobj):
+        node = self.createTextNode(pyobj)
+        self._appendChild(node=node._getNode())
+        return node
+
+    def createTextNode(self, pyobj):
+        document = self._getOwnerDocument()
+        node = document.createTextNode(pyobj)
+        return ElementProxy(self.sw, node)
+
+    #############################################
+    #Methods for retrieving namespaceURI's
+    #############################################
+    def findNamespaceURI(self, qualifiedName):
+        parts = SplitQName(qualifiedName)
+        element = self._getNode()
+        if len(parts) == 1:
+            return (self._dom.findTargetNS(element), value)
+        return self._dom.findNamespaceURI(parts[0], element)
+
+    def resolvePrefix(self, prefix):
+        element = self._getNode()
+        return self._dom.findNamespaceURI(prefix, element)
+
+    def getSOAPEnvURI(self):
+        return self._soap_env_nsuri
+
+    def isEmpty(self):
+        return not self.node
+
+
+
+class Collection(UserDict):
+    """Helper class for maintaining ordered named collections."""
+    default = lambda self,k: k.name
+    def __init__(self, parent, key=None):
+        UserDict.__init__(self)
+        self.parent = weakref.ref(parent)
+        self.list = []
+        self._func = key or self.default
+
+    def __getitem__(self, key):
+        if type(key) is type(1):
+            return self.list[key]
+        return self.data[key]
+
+    def __setitem__(self, key, item):
+        item.parent = weakref.ref(self)
+        self.list.append(item)
+        self.data[key] = item
+
+    def keys(self):
+        return map(lambda i: self._func(i), self.list)
+
+    def items(self):
+        return map(lambda i: (self._func(i), i), self.list)
+
+    def values(self):
+        return self.list
+
+
+class CollectionNS(UserDict):
+    """Helper class for maintaining ordered named collections."""
+    default = lambda self,k: k.name
+    def __init__(self, parent, key=None):
+        UserDict.__init__(self)
+        self.parent = weakref.ref(parent)
+        self.targetNamespace = None
+        self.list = []
+        self._func = key or self.default
+
+    def __getitem__(self, key):
+        self.targetNamespace = self.parent().targetNamespace
+        if type(key) is types.IntType:
+            return self.list[key]
+        elif self.__isSequence(key):
+            nsuri,name = key
+            return self.data[nsuri][name]
+        return self.data[self.parent().targetNamespace][key]
+
+    def __setitem__(self, key, item):
+        item.parent = weakref.ref(self)
+        self.list.append(item)
+        targetNamespace = getattr(item, 'targetNamespace', self.parent().targetNamespace)
+        if not self.data.has_key(targetNamespace):
+            self.data[targetNamespace] = {}
+        self.data[targetNamespace][key] = item
+
+    def __isSequence(self, key):
+        return (type(key) in (types.TupleType,types.ListType) and len(key) == 2)
+
+    def keys(self):
+        keys = []
+        for tns in self.data.keys():
+            keys.append(map(lambda i: (tns,self._func(i)), self.data[tns].values()))
+        return keys
+
+    def items(self):
+        return map(lambda i: (self._func(i), i), self.list)
+
+    def values(self):
+        return self.list
+
+
+
+# This is a runtime guerilla patch for pulldom (used by minidom) so
+# that xml namespace declaration attributes are not lost in parsing.
+# We need them to do correct QName linking for XML Schema and WSDL.
+# The patch has been submitted to SF for the next Python version.
+
+from xml.dom.pulldom import PullDOM, START_ELEMENT
+if 1:
+    def startPrefixMapping(self, prefix, uri):
+        if not hasattr(self, '_xmlns_attrs'):
+            self._xmlns_attrs = []
+        self._xmlns_attrs.append((prefix or 'xmlns', uri))
+        self._ns_contexts.append(self._current_context.copy())
+        self._current_context[uri] = prefix or ''
+
+    PullDOM.startPrefixMapping = startPrefixMapping
+
+    def startElementNS(self, name, tagName , attrs):
+        # Retrieve xml namespace declaration attributes.
+        xmlns_uri = 'http://www.w3.org/2000/xmlns/'
+        xmlns_attrs = getattr(self, '_xmlns_attrs', None)
+        if xmlns_attrs is not None:
+            for aname, value in xmlns_attrs:
+                attrs._attrs[(xmlns_uri, aname)] = value
+            self._xmlns_attrs = []
+        uri, localname = name
+        if uri:
+            # When using namespaces, the reader may or may not
+            # provide us with the original name. If not, create
+            # *a* valid tagName from the current context.
+            if tagName is None:
+                prefix = self._current_context[uri]
+                if prefix:
+                    tagName = prefix + ":" + localname
+                else:
+                    tagName = localname
+            if self.document:
+                node = self.document.createElementNS(uri, tagName)
+            else:
+                node = self.buildDocument(uri, tagName)
+        else:
+            # When the tagname is not prefixed, it just appears as
+            # localname
+            if self.document:
+                node = self.document.createElement(localname)
+            else:
+                node = self.buildDocument(None, localname)
+
+        for aname,value in attrs.items():
+            a_uri, a_localname = aname
+            if a_uri == xmlns_uri:
+                if a_localname == 'xmlns':
+                    qname = a_localname
+                else:
+                    qname = 'xmlns:' + a_localname
+                attr = self.document.createAttributeNS(a_uri, qname)
+                node.setAttributeNodeNS(attr)
+            elif a_uri:
+                prefix = self._current_context[a_uri]
+                if prefix:
+                    qname = prefix + ":" + a_localname
+                else:
+                    qname = a_localname
+                attr = self.document.createAttributeNS(a_uri, qname)
+                node.setAttributeNodeNS(attr)
+            else:
+                attr = self.document.createAttribute(a_localname)
+                node.setAttributeNode(attr)
+            attr.value = value
+
+        self.lastEvent[1] = [(START_ELEMENT, node), None]
+        self.lastEvent = self.lastEvent[1]
+        self.push(node)
+
+    PullDOM.startElementNS = startElementNS
+
+#
+# This is a runtime guerilla patch for minidom so
+# that xmlns prefixed attributes dont raise AttributeErrors
+# during cloning.
+#
+# Namespace declarations can appear in any start-tag, must look for xmlns
+# prefixed attribute names during cloning.
+#
+# key (attr.namespaceURI, tag)
+# ('http://www.w3.org/2000/xmlns/', u'xsd')   <xml.dom.minidom.Attr instance at 0x82227c4>
+# ('http://www.w3.org/2000/xmlns/', 'xmlns')   <xml.dom.minidom.Attr instance at 0x8414b3c>
+#
+# xml.dom.minidom.Attr.nodeName = xmlns:xsd
+# xml.dom.minidom.Attr.value =  = http://www.w3.org/2001/XMLSchema 
+
+if 1:
+    def _clone_node(node, deep, newOwnerDocument):
+        """
+        Clone a node and give it the new owner document.
+        Called by Node.cloneNode and Document.importNode
+        """
+        if node.ownerDocument.isSameNode(newOwnerDocument):
+            operation = xml.dom.UserDataHandler.NODE_CLONED
+        else:
+            operation = xml.dom.UserDataHandler.NODE_IMPORTED
+        if node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE:
+            clone = newOwnerDocument.createElementNS(node.namespaceURI,
+                                                     node.nodeName)
+            for attr in node.attributes.values():
+                clone.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value)
+
+                prefix, tag = xml.dom.minidom._nssplit(attr.nodeName)
+                if prefix == 'xmlns':
+                    a = clone.getAttributeNodeNS(attr.namespaceURI, tag)
+                elif prefix:
+                    a = clone.getAttributeNodeNS(attr.namespaceURI, tag)
+                else:
+                    a = clone.getAttributeNodeNS(attr.namespaceURI, attr.nodeName)
+                a.specified = attr.specified
+
+            if deep:
+                for child in node.childNodes:
+                    c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument)
+                    clone.appendChild(c)
+        elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_FRAGMENT_NODE:
+            clone = newOwnerDocument.createDocumentFragment()
+            if deep:
+                for child in node.childNodes:
+                    c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument)
+                    clone.appendChild(c)
+
+        elif node.nodeType == xml.dom.minidom.Node.TEXT_NODE:
+            clone = newOwnerDocument.createTextNode(node.data)
+        elif node.nodeType == xml.dom.minidom.Node.CDATA_SECTION_NODE:
+            clone = newOwnerDocument.createCDATASection(node.data)
+        elif node.nodeType == xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE:
+            clone = newOwnerDocument.createProcessingInstruction(node.target,
+                                                                 node.data)
+        elif node.nodeType == xml.dom.minidom.Node.COMMENT_NODE:
+            clone = newOwnerDocument.createComment(node.data)
+        elif node.nodeType == xml.dom.minidom.Node.ATTRIBUTE_NODE:
+            clone = newOwnerDocument.createAttributeNS(node.namespaceURI,
+                                                       node.nodeName)
+            clone.specified = True
+            clone.value = node.value
+        elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_TYPE_NODE:
+            assert node.ownerDocument is not newOwnerDocument
+            operation = xml.dom.UserDataHandler.NODE_IMPORTED
+            clone = newOwnerDocument.implementation.createDocumentType(
+                node.name, node.publicId, node.systemId)
+            clone.ownerDocument = newOwnerDocument
+            if deep:
+                clone.entities._seq = []
+                clone.notations._seq = []
+                for n in node.notations._seq:
+                    notation = xml.dom.minidom.Notation(n.nodeName, n.publicId, n.systemId)
+                    notation.ownerDocument = newOwnerDocument
+                    clone.notations._seq.append(notation)
+                    if hasattr(n, '_call_user_data_handler'):
+                        n._call_user_data_handler(operation, n, notation)
+                for e in node.entities._seq:
+                    entity = xml.dom.minidom.Entity(e.nodeName, e.publicId, e.systemId,
+                                    e.notationName)
+                    entity.actualEncoding = e.actualEncoding
+                    entity.encoding = e.encoding
+                    entity.version = e.version
+                    entity.ownerDocument = newOwnerDocument
+                    clone.entities._seq.append(entity)
+                    if hasattr(e, '_call_user_data_handler'):
+                        e._call_user_data_handler(operation, n, entity)
+        else:
+            # Note the cloning of Document and DocumentType nodes is
+            # implemenetation specific.  minidom handles those cases
+            # directly in the cloneNode() methods.
+            raise xml.dom.NotSupportedErr("Cannot clone node %s" % repr(node))
+
+        # Check for _call_user_data_handler() since this could conceivably
+        # used with other DOM implementations (one of the FourThought
+        # DOMs, perhaps?).
+        if hasattr(node, '_call_user_data_handler'):
+            node._call_user_data_handler(operation, node, clone)
+        return clone
+
+    xml.dom.minidom._clone_node = _clone_node
+
diff --git a/wstools/WSDLTools.py b/wstools/WSDLTools.py
new file mode 100755 (executable)
index 0000000..3dd651a
--- /dev/null
@@ -0,0 +1,1668 @@
+# 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 weakref
+from cStringIO import StringIO
+from Namespaces import OASIS, XMLNS, WSA, WSA_LIST, WSAW_LIST, WSRF_V1_2, WSRF
+from Utility import Collection, CollectionNS, DOM, ElementProxy, basejoin
+from XMLSchema import XMLSchema, SchemaReader, WSDLToolsAdapter
+
+
+class WSDLReader:
+    """A WSDLReader creates WSDL instances from urls and xml data."""
+
+    # Custom subclasses of WSDLReader may wish to implement a caching
+    # strategy or other optimizations. Because application needs vary 
+    # so widely, we don't try to provide any caching by default.
+
+    def loadFromStream(self, stream, name=None):
+        """Return a WSDL instance loaded from a stream object."""
+        document = DOM.loadDocument(stream)
+        wsdl = WSDL()
+        if name:
+            wsdl.location = name
+        elif hasattr(stream, 'name'):
+            wsdl.location = stream.name
+        wsdl.load(document)
+        return wsdl
+
+    def loadFromURL(self, url):
+        """Return a WSDL instance loaded from the given url."""
+        document = DOM.loadFromURL(url)
+        wsdl = WSDL()
+        wsdl.location = url
+        wsdl.load(document)
+        return wsdl
+
+    def loadFromString(self, data):
+        """Return a WSDL instance loaded from an xml string."""
+        return self.loadFromStream(StringIO(data))
+
+    def loadFromFile(self, filename):
+        """Return a WSDL instance loaded from the given file."""
+        file = open(filename, 'rb')
+        try:
+            wsdl = self.loadFromStream(file)
+        finally:
+            file.close()
+        return wsdl
+
+class WSDL:
+    """A WSDL object models a WSDL service description. WSDL objects
+       may be created manually or loaded from an xml representation
+       using a WSDLReader instance."""
+
+    def __init__(self, targetNamespace=None, strict=1):
+        self.targetNamespace = targetNamespace or 'urn:this-document.wsdl'
+        self.documentation = ''
+        self.location = None
+        self.document = None
+        self.name = None
+        self.services = CollectionNS(self)
+        self.messages = CollectionNS(self)
+        self.portTypes = CollectionNS(self)
+        self.bindings = CollectionNS(self)
+        self.imports = Collection(self)
+        self.types = Types(self)
+        self.extensions = []
+        self.strict = strict
+
+    def __del__(self):
+        if self.document is not None:
+            self.document.unlink()
+
+    version = '1.1'
+
+    def addService(self, name, documentation='', targetNamespace=None):
+        if self.services.has_key(name):
+            raise WSDLError(
+                'Duplicate service element: %s' % name
+                )
+        item = Service(name, documentation)
+        if targetNamespace:
+            item.targetNamespace = targetNamespace
+        self.services[name] = item
+        return item
+
+    def addMessage(self, name, documentation='', targetNamespace=None):
+        if self.messages.has_key(name):
+            raise WSDLError(
+                'Duplicate message element: %s.' % name
+                )
+        item = Message(name, documentation)
+        if targetNamespace:
+            item.targetNamespace = targetNamespace
+        self.messages[name] = item
+        return item
+
+    def addPortType(self, name, documentation='', targetNamespace=None):
+        if self.portTypes.has_key(name):
+            raise WSDLError(
+                'Duplicate portType element: name'
+                )
+        item = PortType(name, documentation)
+        if targetNamespace:
+            item.targetNamespace = targetNamespace
+        self.portTypes[name] = item
+        return item
+
+    def addBinding(self, name, type, documentation='', targetNamespace=None):
+        if self.bindings.has_key(name):
+            raise WSDLError(
+                'Duplicate binding element: %s' % name
+                )
+        item = Binding(name, type, documentation)
+        if targetNamespace:
+            item.targetNamespace = targetNamespace
+        self.bindings[name] = item
+        return item
+
+    def addImport(self, namespace, location):
+        item = ImportElement(namespace, location)
+        self.imports[namespace] = item
+        return item
+
+    def toDom(self):
+        """ Generate a DOM representation of the WSDL instance.
+        Not dealing with generating XML Schema, thus the targetNamespace
+        of all XML Schema elements or types used by WSDL message parts 
+        needs to be specified via import information items.
+        """
+        namespaceURI = DOM.GetWSDLUri(self.version)
+        self.document = DOM.createDocument(namespaceURI ,'wsdl:definitions')
+
+        # Set up a couple prefixes for easy reading.
+        child = DOM.getElement(self.document, None)
+        child.setAttributeNS(None, 'targetNamespace', self.targetNamespace)
+        child.setAttributeNS(XMLNS.BASE, 'xmlns:wsdl', namespaceURI)
+        child.setAttributeNS(XMLNS.BASE, 'xmlns:xsd', 'http://www.w3.org/1999/XMLSchema')
+        child.setAttributeNS(XMLNS.BASE, 'xmlns:soap', 'http://schemas.xmlsoap.org/wsdl/soap/')
+        child.setAttributeNS(XMLNS.BASE, 'xmlns:tns', self.targetNamespace)
+        
+        if self.name:
+            child.setAttributeNS(None, 'name', self.name)
+
+        # wsdl:import
+        for item in self.imports: 
+            item.toDom()
+        # wsdl:message
+        for item in self.messages:
+            item.toDom()
+        # wsdl:portType
+        for item in self.portTypes:
+            item.toDom()
+        # wsdl:binding
+        for item in self.bindings:
+            item.toDom()
+        # wsdl:service
+        for item in self.services:
+            item.toDom()
+
+    def load(self, document):
+        # We save a reference to the DOM document to ensure that elements
+        # saved as "extensions" will continue to have a meaningful context
+        # for things like namespace references. The lifetime of the DOM
+        # document is bound to the lifetime of the WSDL instance.
+        self.document = document
+
+        definitions = DOM.getElement(document, 'definitions', None, None)
+        if definitions is None:
+            raise WSDLError(
+                'Missing <definitions> element.'
+                )
+        self.version = DOM.WSDLUriToVersion(definitions.namespaceURI)
+        NS_WSDL = DOM.GetWSDLUri(self.version)
+
+        self.targetNamespace = DOM.getAttr(definitions, 'targetNamespace',
+                                           None, None)
+        self.name = DOM.getAttr(definitions, 'name', None, None)
+        self.documentation = GetDocumentation(definitions)
+
+        # 
+        # Retrieve all <wsdl:import>'s, append all children of imported
+        # document to main document.  First iteration grab all original 
+        # <wsdl:import>'s from document, second iteration grab all 
+        # "imported" <wsdl:imports> from document, etc break out when 
+        # no more <wsdl:import>'s.
+        # 
+        imported = []
+        base_location = self.location
+        do_it = True
+        while do_it:
+            do_it = False
+            for element in DOM.getElements(definitions, 'import', NS_WSDL):
+                location = DOM.getAttr(element, 'location')
+
+                if base_location is not None:
+                    location = basejoin(base_location, location)
+                    
+                if location not in imported:
+                    do_it = True
+                    self._import(document, element, base_location)
+                    imported.append(location)
+                else:
+                    definitions.removeChild(element)
+
+            base_location = None
+
+        # 
+        # No more <wsdl:import>'s, now load up all other 
+        # WSDL information items.
+        # 
+        for element in DOM.getElements(definitions, None, None):
+            targetNamespace = DOM.getAttr(element, 'targetNamespace')
+            localName = element.localName
+
+            if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL):
+                if localName == 'schema':
+                    tns = DOM.getAttr(element, 'targetNamespace')
+                    reader = SchemaReader(base_url=self.imports[tns].location)
+                    schema = reader.loadFromNode(WSDLToolsAdapter(self),
+                                                 element)
+#                    schema.setBaseUrl(self.location)
+                    self.types.addSchema(schema)
+                else:
+                    self.extensions.append(element)
+                continue
+
+            elif localName == 'message':
+                name = DOM.getAttr(element, 'name')
+                docs = GetDocumentation(element)
+                message = self.addMessage(name, docs, targetNamespace)
+                parts = DOM.getElements(element, 'part', NS_WSDL)
+                message.load(parts)
+                continue
+
+            elif localName == 'portType':
+                name = DOM.getAttr(element, 'name')
+                docs = GetDocumentation(element)
+                ptype = self.addPortType(name, docs, targetNamespace)
+                #operations = DOM.getElements(element, 'operation', NS_WSDL)
+                #ptype.load(operations)
+                ptype.load(element)
+                continue
+
+            elif localName == 'binding':
+                name = DOM.getAttr(element, 'name')
+                type = DOM.getAttr(element, 'type', default=None)
+                if type is None:
+                    raise WSDLError(
+                        'Missing type attribute for binding %s.' % name
+                        )
+                type = ParseQName(type, element)
+                docs = GetDocumentation(element)
+                binding = self.addBinding(name, type, docs, targetNamespace)
+                operations = DOM.getElements(element, 'operation', NS_WSDL)
+                binding.load(operations)
+                binding.load_ex(GetExtensions(element))
+                continue
+
+            elif localName == 'service':
+                name = DOM.getAttr(element, 'name')
+                docs = GetDocumentation(element)
+                service = self.addService(name, docs, targetNamespace)
+                ports = DOM.getElements(element, 'port', NS_WSDL)
+                service.load(ports)
+                service.load_ex(GetExtensions(element))
+                continue
+
+            elif localName == 'types':
+                self.types.documentation = GetDocumentation(element)
+                base_location = DOM.getAttr(element, 'base-location')
+                if base_location:
+                    element.removeAttribute('base-location')
+                base_location = base_location or self.location
+                reader = SchemaReader(base_url=base_location)
+                for item in DOM.getElements(element, None, None):
+                    if item.localName == 'schema':
+                        schema = reader.loadFromNode(WSDLToolsAdapter(self), item)
+                        # XXX <types> could have been imported
+                        #schema.setBaseUrl(self.location)
+                        schema.setBaseUrl(base_location)
+                        self.types.addSchema(schema)
+                    else:
+                        self.types.addExtension(item)
+                # XXX remove the attribute
+                # element.removeAttribute('base-location')
+                continue
+
+    def _import(self, document, element, base_location=None):
+        '''Algo take <import> element's children, clone them,
+        and add them to the main document.  Support for relative 
+        locations is a bit complicated.  The orig document context
+        is lost, so we need to store base location in DOM elements
+        representing <types>, by creating a special temporary 
+        "base-location" attribute,  and <import>, by resolving
+        the relative "location" and storing it as "location".
+        
+        document -- document we are loading
+        element -- DOM Element representing <import> 
+        base_location -- location of document from which this
+            <import> was gleaned.
+        '''
+        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).'
+                )
+        if base_location:
+            location = basejoin(base_location, location)
+            element.setAttributeNS(None, '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.findTargetNS(imported)
+            if imported_tns != namespace:
+                return
+
+            if imported.localName == 'definitions':
+                imported_nodes = imported.childNodes
+            else:
+                imported_nodes = [imported]
+            parent = element.parentNode
+
+            parent.removeChild(element)
+            
+            for node in imported_nodes:
+                if node.nodeType != node.ELEMENT_NODE:
+                    continue
+                child = DOM.importNode(document, node, 1)
+                parent.appendChild(child)
+                child.setAttribute('targetNamespace', namespace)
+                attrsNS = imported._attrsNS
+                for attrkey in attrsNS.keys():
+                    if attrkey[0] == DOM.NS_XMLNS:
+                        attr = attrsNS[attrkey].cloneNode(1)
+                        child.setAttributeNode(attr)
+
+                #XXX Quick Hack, should be in WSDL Namespace.
+                if child.localName == 'import':
+                    rlocation = child.getAttributeNS(None, 'location')
+                    alocation = basejoin(location, rlocation)
+                    child.setAttribute('location', alocation)
+                elif child.localName == 'types':
+                    child.setAttribute('base-location', location)
+
+        finally:
+            importdoc.unlink()
+        return location
+
+class Element:
+    """A class that provides common functions for WSDL element classes."""
+    def __init__(self, name=None, documentation=''):
+        self.name = name
+        self.documentation = documentation
+        self.extensions = []
+
+    def addExtension(self, item):
+        item.parent = weakref.ref(self)
+        self.extensions.append(item)
+        
+    def getWSDL(self):
+        """Return the WSDL object that contains this information item."""
+        parent = self
+        while 1:
+            # skip any collections
+            if isinstance(parent, WSDL):
+                return parent
+            try: parent = parent.parent()
+            except: break
+            
+        return None
+
+
+class ImportElement(Element):
+    def __init__(self, namespace, location):
+        self.namespace = namespace
+        self.location = location
+
+#    def getWSDL(self):
+#        """Return the WSDL object that contains this Message Part."""
+#        return self.parent().parent()
+
+    def toDom(self):
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+        epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'import')
+        epc.setAttributeNS(None, 'namespace', self.namespace)
+        epc.setAttributeNS(None, 'location', self.location)
+
+    _loaded = None
+
+
+class Types(Collection):
+    default = lambda self,k: k.targetNamespace
+    def __init__(self, parent):
+        Collection.__init__(self, parent)
+        self.documentation = ''
+        self.extensions = []
+
+    def addSchema(self, schema):
+        name = schema.targetNamespace
+        self[name] = schema
+        return schema
+
+    def addExtension(self, item):
+        self.extensions.append(item)
+
+
+class Message(Element):
+    def __init__(self, name, documentation=''):
+        Element.__init__(self, name, documentation)
+        self.parts = Collection(self)
+
+    def addPart(self, name, type=None, element=None):
+        if self.parts.has_key(name):
+            raise WSDLError(
+                'Duplicate message part element: %s' % name
+                )
+        if type is None and element is None:
+            raise WSDLError(
+                'Missing type or element attribute for part: %s' % name
+                )
+        item = MessagePart(name)
+        item.element = element
+        item.type = type
+        self.parts[name] = item
+        return item
+
+    def load(self, elements):
+        for element in elements:
+            name = DOM.getAttr(element, 'name')
+            part = MessagePart(name)
+            self.parts[name] = part
+            elemref = DOM.getAttr(element, 'element', default=None)
+            typeref = DOM.getAttr(element, 'type', default=None)
+            if typeref is None and elemref is None:
+                raise WSDLError(
+                    'No type or element attribute for part: %s' % name
+                    )
+            if typeref is not None:
+                part.type = ParseTypeRef(typeref, element)
+            if elemref is not None:
+                part.element = ParseTypeRef(elemref, element)
+
+#    def getElementDeclaration(self):
+#        """Return the XMLSchema.ElementDeclaration instance or None"""
+#        element = None
+#        if self.element:
+#            nsuri,name = self.element
+#            wsdl = self.getWSDL()
+#            if wsdl.types.has_key(nsuri) and wsdl.types[nsuri].elements.has_key(name):
+#                element = wsdl.types[nsuri].elements[name]
+#        return element
+#
+#    def getTypeDefinition(self):
+#        """Return the XMLSchema.TypeDefinition instance or None"""
+#        type = None
+#        if self.type:
+#            nsuri,name = self.type
+#            wsdl = self.getWSDL()
+#            if wsdl.types.has_key(nsuri) and wsdl.types[nsuri].types.has_key(name):
+#                type = wsdl.types[nsuri].types[name]
+#        return type
+
+#    def getWSDL(self):
+#        """Return the WSDL object that contains this Message Part."""
+#        return self.parent().parent()
+
+    def toDom(self):
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+        epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'message')
+        epc.setAttributeNS(None, 'name', self.name)
+
+        for part in self.parts:
+            part.toDom(epc._getNode())
+
+
+class MessagePart(Element):
+    def __init__(self, name):
+        Element.__init__(self, name, '')
+        self.element = None
+        self.type = None
+
+#    def getWSDL(self):
+#        """Return the WSDL object that contains this Message Part."""
+#        return self.parent().parent().parent().parent()
+
+    def getTypeDefinition(self):
+        wsdl = self.getWSDL()
+        nsuri,name = self.type
+        schema = wsdl.types.get(nsuri, {})
+        return schema.get(name)
+
+    def getElementDeclaration(self):
+        wsdl = self.getWSDL()
+        nsuri,name = self.element
+        schema = wsdl.types.get(nsuri, {})
+        return schema.get(name)
+
+    def toDom(self, node):
+        """node -- node representing message"""
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, node)
+        epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'part')
+        epc.setAttributeNS(None, 'name', self.name)
+
+        if self.element is not None:
+            ns,name = self.element
+            prefix = epc.getPrefix(ns)
+            epc.setAttributeNS(None, 'element', '%s:%s'%(prefix,name))
+        elif self.type is not None:
+            ns,name = self.type
+            prefix = epc.getPrefix(ns)
+            epc.setAttributeNS(None, 'type', '%s:%s'%(prefix,name))
+
+
+class PortType(Element):
+    '''PortType has a anyAttribute, thus must provide for an extensible
+       mechanism for supporting such attributes.  ResourceProperties is
+       specified in WS-ResourceProperties.   wsa:Action is specified in
+       WS-Address.
+
+       Instance Data:
+           name -- name attribute
+           resourceProperties -- optional. wsr:ResourceProperties attribute,
+              value is a QName this is Parsed into a (namespaceURI, name)
+              that represents a Global Element Declaration.
+           operations
+    '''
+
+    def __init__(self, name, documentation=''):
+        Element.__init__(self, name, documentation)
+        self.operations = Collection(self)
+        self.resourceProperties = None
+
+#    def getWSDL(self):
+#        return self.parent().parent()
+
+    def getTargetNamespace(self):
+        return self.targetNamespace or self.getWSDL().targetNamespace
+
+    def getResourceProperties(self):
+        return self.resourceProperties
+
+    def addOperation(self, name, documentation='', parameterOrder=None):
+        item = Operation(name, documentation, parameterOrder)
+        self.operations[name] = item
+        return item
+
+    def load(self, element):
+        self.name = DOM.getAttr(element, 'name')
+        self.documentation = GetDocumentation(element)
+        self.targetNamespace = DOM.getAttr(element, 'targetNamespace')
+
+        for nsuri in WSRF_V1_2.PROPERTIES.XSD_LIST:
+            if DOM.hasAttr(element, 'ResourceProperties', nsuri):
+                rpref = DOM.getAttr(element, 'ResourceProperties', nsuri)
+                self.resourceProperties = ParseQName(rpref, element)
+
+        NS_WSDL = DOM.GetWSDLUri(self.getWSDL().version)
+        elements = DOM.getElements(element, 'operation', NS_WSDL)
+        for element in elements:
+            name = DOM.getAttr(element, 'name')
+            docs = GetDocumentation(element)
+            param_order = DOM.getAttr(element, 'parameterOrder', default=None)
+            if param_order is not None:
+                param_order = param_order.split(' ')
+            operation = self.addOperation(name, docs, param_order)
+
+            item = DOM.getElement(element, 'input', None, None)
+            if item is not None:
+                name = DOM.getAttr(item, 'name')
+                docs = GetDocumentation(item)
+                msgref = DOM.getAttr(item, 'message')
+                message = ParseQName(msgref, item)
+                for WSA in WSA_LIST + WSAW_LIST:
+                    action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
+                    if action: break
+                operation.setInput(message, name, docs, action)
+
+            item = DOM.getElement(element, 'output', None, None)
+            if item is not None:
+                name = DOM.getAttr(item, 'name')
+                docs = GetDocumentation(item)
+                msgref = DOM.getAttr(item, 'message')
+                message = ParseQName(msgref, item)
+                for WSA in WSA_LIST + WSAW_LIST:
+                    action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
+                    if action: break
+                operation.setOutput(message, name, docs, action)
+
+            for item in DOM.getElements(element, 'fault', None):
+                name = DOM.getAttr(item, 'name')
+                docs = GetDocumentation(item)
+                msgref = DOM.getAttr(item, 'message')
+                message = ParseQName(msgref, item)
+                for WSA in WSA_LIST + WSAW_LIST:
+                    action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
+                    if action: break
+                operation.addFault(message, name, docs, action)
+                
+    def toDom(self):
+        wsdl = self.getWSDL()
+
+        ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+        epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'portType')
+        epc.setAttributeNS(None, 'name', self.name)
+        if self.resourceProperties:
+            ns,name = self.resourceProperties
+            prefix = epc.getPrefix(ns)
+            epc.setAttributeNS(WSRF.PROPERTIES.LATEST, 'ResourceProperties', 
+                                '%s:%s'%(prefix,name))
+
+        for op in self.operations:
+            op.toDom(epc._getNode())
+
+
+
+class Operation(Element):
+    def __init__(self, name, documentation='', parameterOrder=None):
+        Element.__init__(self, name, documentation)
+        self.parameterOrder = parameterOrder
+        self.faults = Collection(self)
+        self.input = None
+        self.output = None
+
+    def getWSDL(self):
+        """Return the WSDL object that contains this Operation."""
+        return self.parent().parent().parent().parent()
+
+    def getPortType(self):
+        return self.parent().parent()
+
+    def getInputAction(self):
+        """wsa:Action attribute"""
+        return GetWSAActionInput(self)
+
+    def getInputMessage(self):
+        if self.input is None:
+            return None
+        wsdl = self.getPortType().getWSDL()
+        return wsdl.messages[self.input.message]
+
+    def getOutputAction(self):
+        """wsa:Action attribute"""
+        return GetWSAActionOutput(self)
+
+    def getOutputMessage(self):
+        if self.output is None:
+            return None
+        wsdl = self.getPortType().getWSDL()
+        return wsdl.messages[self.output.message]
+
+    def getFaultAction(self, name):
+        """wsa:Action attribute"""
+        return GetWSAActionFault(self, name)
+
+    def getFaultMessage(self, name):
+        wsdl = self.getPortType().getWSDL()
+        return wsdl.messages[self.faults[name].message]
+
+    def addFault(self, message, name, documentation='', action=None):
+        if self.faults.has_key(name):
+            raise WSDLError(
+                'Duplicate fault element: %s' % name
+                )
+        item = MessageRole('fault', message, name, documentation, action)
+        self.faults[name] = item
+        return item
+
+    def setInput(self, message, name='', documentation='', action=None):
+        self.input = MessageRole('input', message, name, documentation, action)
+        self.input.parent = weakref.ref(self)
+        return self.input
+
+    def setOutput(self, message, name='', documentation='', action=None):
+        self.output = MessageRole('output', message, name, documentation, action)
+        self.output.parent = weakref.ref(self)
+        return self.output
+
+    def toDom(self, node):
+        wsdl = self.getWSDL()
+
+        ep = ElementProxy(None, node)
+        epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'operation')
+        epc.setAttributeNS(None, 'name', self.name)
+        node = epc._getNode()
+        if self.input:
+           self.input.toDom(node)
+        if self.output:
+           self.output.toDom(node)
+        for fault in self.faults:
+           fault.toDom(node)
+
+
+class MessageRole(Element):
+    def __init__(self, type, message, name='', documentation='', action=None):
+        Element.__init__(self, name, documentation)
+        self.message = message
+        self.type = type
+        self.action = action
+        
+    def getWSDL(self):
+        """Return the WSDL object that contains this information item."""
+        parent = self
+        while 1:
+            # skip any collections
+            if isinstance(parent, WSDL):
+                return parent
+            try: parent = parent.parent()
+            except: break
+            
+        return None
+
+    def getMessage(self):
+        """Return the WSDL object that represents the attribute message 
+        (namespaceURI, name) tuple
+        """
+        wsdl = self.getWSDL()
+        return wsdl.messages[self.message]
+
+    def toDom(self, node):
+        wsdl = self.getWSDL()
+
+        ep = ElementProxy(None, node)
+        epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), self.type)
+        if not isinstance(self.message, basestring) and len(self.message) == 2:
+            ns,name = self.message
+            prefix = epc.getPrefix(ns)
+            epc.setAttributeNS(None, 'message', '%s:%s' %(prefix,name))
+        else:
+            epc.setAttributeNS(None, 'message', self.message)
+
+        if self.action:
+            epc.setAttributeNS(WSA.ADDRESS, 'Action', self.action)
+            
+        if self.name:
+            epc.setAttributeNS(None, 'name', self.name)
+        
+
+class Binding(Element):
+    def __init__(self, name, type, documentation=''):
+        Element.__init__(self, name, documentation)
+        self.operations = Collection(self)
+        self.type = type
+
+#    def getWSDL(self):
+#        """Return the WSDL object that contains this binding."""
+#        return self.parent().parent()
+
+    def getPortType(self):
+        """Return the PortType object associated with this binding."""
+        return self.getWSDL().portTypes[self.type]
+
+    def findBinding(self, kind):
+        for item in self.extensions:
+            if isinstance(item, kind):
+                return item
+        return None
+
+    def findBindings(self, kind):
+        return [ item for item in self.extensions if isinstance(item, kind) ]
+
+    def addOperationBinding(self, name, documentation=''):
+        item = OperationBinding(name, documentation)
+        self.operations[name] = item
+        return item
+
+    def load(self, elements):
+        for element in elements:
+            name = DOM.getAttr(element, 'name')
+            docs = GetDocumentation(element)
+            opbinding = self.addOperationBinding(name, docs)
+            opbinding.load_ex(GetExtensions(element))
+
+            item = DOM.getElement(element, 'input', None, None)
+            if item is not None:
+                #TODO: addInputBinding?
+                mbinding = MessageRoleBinding('input')
+                mbinding.documentation = GetDocumentation(item)
+                opbinding.input = mbinding
+                mbinding.load_ex(GetExtensions(item))
+                mbinding.parent = weakref.ref(opbinding)
+
+            item = DOM.getElement(element, 'output', None, None)
+            if item is not None:
+                mbinding = MessageRoleBinding('output')
+                mbinding.documentation = GetDocumentation(item)
+                opbinding.output = mbinding
+                mbinding.load_ex(GetExtensions(item))
+                mbinding.parent = weakref.ref(opbinding)
+
+            for item in DOM.getElements(element, 'fault', None):
+                name = DOM.getAttr(item, 'name')
+                mbinding = MessageRoleBinding('fault', name)
+                mbinding.documentation = GetDocumentation(item)
+                opbinding.faults[name] = mbinding
+                mbinding.load_ex(GetExtensions(item))
+                mbinding.parent = weakref.ref(opbinding)
+
+    def load_ex(self, elements):
+        for e in elements:
+            ns, name = e.namespaceURI, e.localName
+            if ns in DOM.NS_SOAP_BINDING_ALL and name == 'binding':
+                transport = DOM.getAttr(e, 'transport', default=None)
+                style = DOM.getAttr(e, 'style', default='document')
+                ob = SoapBinding(transport, style)
+                self.addExtension(ob)
+                continue
+            elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'binding':
+                verb = DOM.getAttr(e, 'verb')
+                ob = HttpBinding(verb)
+                self.addExtension(ob)
+                continue
+            else:
+                self.addExtension(e)
+
+    def toDom(self):
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+        epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'binding')
+        epc.setAttributeNS(None, 'name', self.name)
+
+        ns,name = self.type
+        prefix = epc.getPrefix(ns)
+        epc.setAttributeNS(None, 'type', '%s:%s' %(prefix,name))
+
+        node = epc._getNode()
+        for ext in self.extensions:
+            ext.toDom(node)
+        for op_binding in self.operations:
+            op_binding.toDom(node)
+
+
+class OperationBinding(Element):
+    def __init__(self, name, documentation=''):
+        Element.__init__(self, name, documentation)
+        self.input = None
+        self.output = None
+        self.faults = Collection(self)
+
+#    def getWSDL(self):
+#        """Return the WSDL object that contains this binding."""
+#        return self.parent().parent().parent().parent()
+
+
+    def getBinding(self):
+        """Return the parent Binding object of the operation binding."""
+        return self.parent().parent()
+
+    def getOperation(self):
+        """Return the abstract Operation associated with this binding."""
+        return self.getBinding().getPortType().operations[self.name]
+        
+    def findBinding(self, kind):
+        for item in self.extensions:
+            if isinstance(item, kind):
+                return item
+        return None
+
+    def findBindings(self, kind):
+        return [ item for item in self.extensions if isinstance(item, kind) ]
+
+    def addInputBinding(self, binding):
+        if self.input is None:
+            self.input = MessageRoleBinding('input')
+            self.input.parent = weakref.ref(self)
+        self.input.addExtension(binding)
+        return binding
+
+    def addOutputBinding(self, binding):
+        if self.output is None:
+            self.output = MessageRoleBinding('output')
+            self.output.parent = weakref.ref(self)
+        self.output.addExtension(binding)
+        return binding
+
+    def addFaultBinding(self, name, binding):
+        fault = self.get(name, None)
+        if fault is None:
+            fault = MessageRoleBinding('fault', name)
+        fault.addExtension(binding)
+        return binding
+
+    def load_ex(self, elements):
+        for e in elements:
+            ns, name = e.namespaceURI, e.localName
+            if ns in DOM.NS_SOAP_BINDING_ALL and name == 'operation':
+                soapaction = DOM.getAttr(e, 'soapAction', default=None)
+                style = DOM.getAttr(e, 'style', default=None)
+                ob = SoapOperationBinding(soapaction, style)
+                self.addExtension(ob)
+                continue
+            elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'operation':
+                location = DOM.getAttr(e, 'location')
+                ob = HttpOperationBinding(location)
+                self.addExtension(ob)
+                continue
+            else:
+                self.addExtension(e)
+
+    def toDom(self, node):
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, node)
+        epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'operation')
+        epc.setAttributeNS(None, 'name', self.name)
+
+        node = epc._getNode()
+        for ext in self.extensions:
+            ext.toDom(node)
+        if self.input:
+            self.input.toDom(node)
+        if self.output:
+            self.output.toDom(node)
+        for fault in self.faults:
+            fault.toDom(node)
+
+
+class MessageRoleBinding(Element):
+    def __init__(self, type, name='', documentation=''):
+        Element.__init__(self, name, documentation)
+        self.type = type
+
+    def findBinding(self, kind):
+        for item in self.extensions:
+            if isinstance(item, kind):
+                return item
+        return None
+
+    def findBindings(self, kind):
+        return [ item for item in self.extensions if isinstance(item, kind) ]
+
+    def load_ex(self, elements):
+        for e in elements:
+            ns, name = e.namespaceURI, e.localName
+            if ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
+                encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
+                namespace = DOM.getAttr(e, 'namespace', default=None)
+                parts = DOM.getAttr(e, 'parts', default=None)
+                use = DOM.getAttr(e, 'use', default=None)
+                if use is None:
+                    raise WSDLError(
+                        'Invalid soap:body binding element.'
+                        )
+                ob = SoapBodyBinding(use, namespace, encstyle, parts)
+                self.addExtension(ob)
+                continue
+
+            elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'fault':
+                encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
+                namespace = DOM.getAttr(e, 'namespace', default=None)
+                name = DOM.getAttr(e, 'name', default=None)
+                use = DOM.getAttr(e, 'use', default=None)
+                if use is None or name is None:
+                    raise WSDLError(
+                        'Invalid soap:fault binding element.'
+                        )
+                ob = SoapFaultBinding(name, use, namespace, encstyle)
+                self.addExtension(ob)
+                continue
+
+            elif ns in DOM.NS_SOAP_BINDING_ALL and name in (
+                'header', 'headerfault'
+                ):
+                encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
+                namespace = DOM.getAttr(e, 'namespace', default=None)
+                message = DOM.getAttr(e, 'message')
+                part = DOM.getAttr(e, 'part')
+                use = DOM.getAttr(e, 'use')
+                if name == 'header':
+                    _class = SoapHeaderBinding
+                else:
+                    _class = SoapHeaderFaultBinding
+                message = ParseQName(message, e)
+                ob = _class(message, part, use, namespace, encstyle)
+                self.addExtension(ob)
+                continue
+
+            elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlReplacement':
+                ob = HttpUrlReplacementBinding()
+                self.addExtension(ob)
+                continue
+
+            elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlEncoded':
+                ob = HttpUrlEncodedBinding()
+                self.addExtension(ob)
+                continue
+
+            elif ns in DOM.NS_MIME_BINDING_ALL and name == 'multipartRelated':
+                ob = MimeMultipartRelatedBinding()
+                self.addExtension(ob)
+                ob.load_ex(GetExtensions(e))
+                continue
+
+            elif ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
+                part = DOM.getAttr(e, 'part', default=None)
+                type = DOM.getAttr(e, 'type', default=None)
+                ob = MimeContentBinding(part, type)
+                self.addExtension(ob)
+                continue
+
+            elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
+                part = DOM.getAttr(e, 'part', default=None)
+                ob = MimeXmlBinding(part)
+                self.addExtension(ob)
+                continue
+
+            else:
+                self.addExtension(e)
+
+    def toDom(self, node):
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, node)
+        epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), self.type)
+
+        node = epc._getNode()
+        for item in self.extensions:
+            if item: item.toDom(node)
+
+
+class Service(Element):
+    def __init__(self, name, documentation=''):
+        Element.__init__(self, name, documentation)
+        self.ports = Collection(self)
+
+    def getWSDL(self):
+        return self.parent().parent()
+
+    def addPort(self, name, binding, documentation=''):
+        item = Port(name, binding, documentation)
+        self.ports[name] = item
+        return item
+
+    def load(self, elements):
+        for element in elements:
+            name = DOM.getAttr(element, 'name', default=None)
+            docs = GetDocumentation(element)
+            binding = DOM.getAttr(element, 'binding', default=None)
+            if name is None or binding is None:
+                raise WSDLError(
+                    'Invalid port element.'
+                    )
+            binding = ParseQName(binding, element)
+            port = self.addPort(name, binding, docs)
+            port.load_ex(GetExtensions(element))
+
+    def load_ex(self, elements):
+        for e in elements:
+            self.addExtension(e)
+
+    def toDom(self):
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
+        epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), "service")
+        epc.setAttributeNS(None, "name", self.name)
+
+        node = epc._getNode()
+        for port in self.ports:
+            port.toDom(node)
+
+
+class Port(Element):
+    def __init__(self, name, binding, documentation=''):
+        Element.__init__(self, name, documentation)
+        self.binding = binding
+
+#    def getWSDL(self):
+#        return self.parent().parent().getWSDL()
+
+    def getService(self):
+        """Return the Service object associated with this port."""
+        return self.parent().parent()
+
+    def getBinding(self):
+        """Return the Binding object that is referenced by this port."""
+        wsdl = self.getService().getWSDL()
+        return wsdl.bindings[self.binding]
+
+    def getPortType(self):
+        """Return the PortType object that is referenced by this port."""
+        wsdl = self.getService().getWSDL()
+        binding = wsdl.bindings[self.binding]
+        return wsdl.portTypes[binding.type]
+
+    def getAddressBinding(self):
+        """A convenience method to obtain the extension element used
+           as the address binding for the port."""
+        for item in self.extensions:
+            if isinstance(item, SoapAddressBinding) or \
+               isinstance(item, HttpAddressBinding):
+                return item
+        raise WSDLError(
+            'No address binding found in port.'
+            )
+
+    def load_ex(self, elements):
+        for e in elements:
+            ns, name = e.namespaceURI, e.localName
+            if ns in DOM.NS_SOAP_BINDING_ALL and name == 'address':
+                location = DOM.getAttr(e, 'location', default=None)
+                ob = SoapAddressBinding(location)
+                self.addExtension(ob)
+                continue
+            elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'address':
+                location = DOM.getAttr(e, 'location', default=None)
+                ob = HttpAddressBinding(location)
+                self.addExtension(ob)
+                continue
+            else:
+                self.addExtension(e)
+
+    def toDom(self, node):
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, node)
+        epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), "port")
+        epc.setAttributeNS(None, "name", self.name)
+
+        ns,name = self.binding
+        prefix = epc.getPrefix(ns)
+        epc.setAttributeNS(None, "binding", "%s:%s" %(prefix,name))
+
+        node = epc._getNode()
+        for ext in self.extensions:
+            ext.toDom(node)
+
+
+class SoapBinding:
+    def __init__(self, transport, style='rpc'):
+        self.transport = transport
+        self.style = style
+
+    def getWSDL(self):
+        return self.parent().getWSDL()
+
+    def toDom(self, node):
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, node)
+        epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'binding')
+        if self.transport:
+            epc.setAttributeNS(None, "transport", self.transport)
+        if self.style:
+            epc.setAttributeNS(None, "style", self.style)
+
+class SoapAddressBinding:
+    def __init__(self, location):
+        self.location = location
+
+    def getWSDL(self):
+        return self.parent().getWSDL()
+
+    def toDom(self, node):
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, node)
+        epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'address')
+        epc.setAttributeNS(None, "location", self.location)
+
+
+class SoapOperationBinding:
+    def __init__(self, soapAction=None, style=None):
+        self.soapAction = soapAction
+        self.style = style
+
+    def getWSDL(self):
+        return self.parent().getWSDL()
+
+    def toDom(self, node):
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, node)
+        epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'operation')
+        if self.soapAction:
+            epc.setAttributeNS(None, 'soapAction', self.soapAction)
+        if self.style:
+            epc.setAttributeNS(None, 'style', self.style)
+
+
+class SoapBodyBinding:
+    def __init__(self, use, namespace=None, encodingStyle=None, parts=None):
+        if not use in ('literal', 'encoded'):
+            raise WSDLError(
+                'Invalid use attribute value: %s' % use
+                )
+        self.encodingStyle = encodingStyle
+        self.namespace = namespace
+        if type(parts) in (type(''), type(u'')):
+            parts = parts.split()
+        self.parts = parts
+        self.use = use
+
+    def getWSDL(self):
+        return self.parent().getWSDL()
+
+    def toDom(self, node):
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, node)
+        epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'body')
+        epc.setAttributeNS(None, "use", self.use)
+        epc.setAttributeNS(None, "namespace", self.namespace)
+
+
+class SoapFaultBinding:
+    def __init__(self, name, use, namespace=None, encodingStyle=None):
+        if not use in ('literal', 'encoded'):
+            raise WSDLError(
+                'Invalid use attribute value: %s' % use
+                )
+        self.encodingStyle = encodingStyle
+        self.namespace = namespace
+        self.name = name
+        self.use = use
+        
+    def getWSDL(self):
+        return self.parent().getWSDL()
+    
+    def toDom(self, node):
+        wsdl = self.getWSDL()
+        ep = ElementProxy(None, node)
+        epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'body')
+        epc.setAttributeNS(None, "use", self.use)
+        epc.setAttributeNS(None, "name", self.name)
+        if self.namespace is not None:
+            epc.setAttributeNS(None, "namespace", self.namespace)
+        if self.encodingStyle is not None:
+            epc.setAttributeNS(None, "encodingStyle", self.encodingStyle)
+
+
+class SoapHeaderBinding:
+    def __init__(self, message, part, use, namespace=None, encodingStyle=None):
+        if not use in ('literal', 'encoded'):
+            raise WSDLError(
+                'Invalid use attribute value: %s' % use
+                )
+        self.encodingStyle = encodingStyle
+        self.namespace = namespace
+        self.message = message
+        self.part = part
+        self.use = use
+
+    tagname = 'header'
+
+class SoapHeaderFaultBinding(SoapHeaderBinding):
+    tagname = 'headerfault'
+
+
+class HttpBinding:
+    def __init__(self, verb):
+        self.verb = verb
+
+class HttpAddressBinding:
+    def __init__(self, location):
+        self.location = location
+
+
+class HttpOperationBinding:
+    def __init__(self, location):
+        self.location = location
+
+class HttpUrlReplacementBinding:
+    pass
+
+
+class HttpUrlEncodedBinding:
+    pass
+
+
+class MimeContentBinding:
+    def __init__(self, part=None, type=None):
+        self.part = part
+        self.type = type
+
+
+class MimeXmlBinding:
+    def __init__(self, part=None):
+        self.part = part
+
+
+class MimeMultipartRelatedBinding:
+    def __init__(self):
+        self.parts = []
+
+    def load_ex(self, elements):
+        for e in elements:
+            ns, name = e.namespaceURI, e.localName
+            if ns in DOM.NS_MIME_BINDING_ALL and name == 'part':
+                self.parts.append(MimePartBinding())
+                continue
+
+
+class MimePartBinding:
+    def __init__(self):
+        self.items = []
+
+    def load_ex(self, elements):
+        for e in elements:
+            ns, name = e.namespaceURI, e.localName
+            if ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
+                part = DOM.getAttr(e, 'part', default=None)
+                type = DOM.getAttr(e, 'type', default=None)
+                ob = MimeContentBinding(part, type)
+                self.items.append(ob)
+                continue
+
+            elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
+                part = DOM.getAttr(e, 'part', default=None)
+                ob = MimeXmlBinding(part)
+                self.items.append(ob)
+                continue
+
+            elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
+                encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
+                namespace = DOM.getAttr(e, 'namespace', default=None)
+                parts = DOM.getAttr(e, 'parts', default=None)
+                use = DOM.getAttr(e, 'use', default=None)
+                if use is None:
+                    raise WSDLError(
+                        'Invalid soap:body binding element.'
+                        )
+                ob = SoapBodyBinding(use, namespace, encstyle, parts)
+                self.items.append(ob)
+                continue
+
+
+class WSDLError(Exception):
+    pass
+
+
+
+def DeclareNSPrefix(writer, prefix, nsuri):
+    if writer.hasNSPrefix(nsuri):
+        return
+    writer.declareNSPrefix(prefix, nsuri)
+
+def ParseTypeRef(value, element):
+    parts = value.split(':', 1)
+    if len(parts) == 1:
+        return (DOM.findTargetNS(element), value)
+    nsuri = DOM.findNamespaceURI(parts[0], element)
+    return (nsuri, parts[1])
+
+def ParseQName(value, element):
+    nameref = value.split(':', 1)
+    if len(nameref) == 2:
+        nsuri = DOM.findNamespaceURI(nameref[0], element)
+        name = nameref[-1]
+    else:
+        nsuri = DOM.findTargetNS(element)
+        name  = nameref[-1]
+    return nsuri, name
+
+def GetDocumentation(element):
+    docnode = DOM.getElement(element, 'documentation', None, None)
+    if docnode is not None:
+        return DOM.getElementText(docnode)
+    return ''
+
+def GetExtensions(element):
+    return [ item for item in DOM.getElements(element, None, None)
+        if item.namespaceURI != DOM.NS_WSDL ]
+
+def GetWSAActionFault(operation, name):
+    """Find wsa:Action attribute, and return value or WSA.FAULT
+       for the default.
+    """
+    attr = operation.faults[name].action
+    if attr is not None:
+        return attr
+    return WSA.FAULT
+
+def GetWSAActionInput(operation):
+    """Find wsa:Action attribute, and return value or the default."""
+    attr = operation.input.action
+    if attr is not None:
+        return attr
+    portType = operation.getPortType()
+    targetNamespace = portType.getTargetNamespace()
+    ptName = portType.name
+    msgName = operation.input.name
+    if not msgName:
+        msgName = operation.name + 'Request'
+    if targetNamespace.endswith('/'):
+        return '%s%s/%s' %(targetNamespace, ptName, msgName)
+    return '%s/%s/%s' %(targetNamespace, ptName, msgName)
+
+def GetWSAActionOutput(operation):
+    """Find wsa:Action attribute, and return value or the default."""
+    attr = operation.output.action
+    if attr is not None:
+        return attr
+    targetNamespace = operation.getPortType().getTargetNamespace()
+    ptName = operation.getPortType().name
+    msgName = operation.output.name
+    if not msgName:
+        msgName = operation.name + 'Response'
+    if targetNamespace.endswith('/'):
+        return '%s%s/%s' %(targetNamespace, ptName, msgName)
+    return '%s/%s/%s' %(targetNamespace, ptName, msgName)
+
+def FindExtensions(object, kind, t_type=type(())):
+    if isinstance(kind, t_type):
+        result = []
+        namespaceURI, name = kind
+        return [ item for item in object.extensions
+                if hasattr(item, 'nodeType') \
+                and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
+                and item.name == name ]
+    return [ item for item in object.extensions if isinstance(item, kind) ]
+
+def FindExtension(object, kind, t_type=type(())):
+    if isinstance(kind, t_type):
+        namespaceURI, name = kind
+        for item in object.extensions:
+            if hasattr(item, 'nodeType') \
+            and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
+            and item.name == name:
+                return item
+    else:
+        for item in object.extensions:
+            if isinstance(item, kind):
+                return item
+    return None
+
+
+class SOAPCallInfo:
+    """SOAPCallInfo captures the important binding information about a 
+       SOAP operation, in a structure that is easier to work with than
+       raw WSDL structures."""
+
+    def __init__(self, methodName):
+        self.methodName = methodName
+        self.inheaders = []
+        self.outheaders = []
+        self.inparams = []
+        self.outparams = []
+        self.retval = None
+
+    encodingStyle = DOM.NS_SOAP_ENC
+    documentation = ''
+    soapAction = None
+    transport = None
+    namespace = None
+    location = None
+    use = 'encoded'
+    style = 'rpc'
+
+    def addInParameter(self, name, type, namespace=None, element_type=0):
+        """Add an input parameter description to the call info."""
+        parameter = ParameterInfo(name, type, namespace, element_type)
+        self.inparams.append(parameter)
+        return parameter
+
+    def addOutParameter(self, name, type, namespace=None, element_type=0):
+        """Add an output parameter description to the call info."""
+        parameter = ParameterInfo(name, type, namespace, element_type)
+        self.outparams.append(parameter)
+        return parameter
+
+    def setReturnParameter(self, name, type, namespace=None, element_type=0):
+        """Set the return parameter description for the call info."""
+        parameter = ParameterInfo(name, type, namespace, element_type)
+        self.retval = parameter
+        return parameter
+
+    def addInHeaderInfo(self, name, type, namespace, element_type=0,
+                        mustUnderstand=0):
+        """Add an input SOAP header description to the call info."""
+        headerinfo = HeaderInfo(name, type, namespace, element_type)
+        if mustUnderstand:
+            headerinfo.mustUnderstand = 1
+        self.inheaders.append(headerinfo)
+        return headerinfo
+
+    def addOutHeaderInfo(self, name, type, namespace, element_type=0,
+                         mustUnderstand=0):
+        """Add an output SOAP header description to the call info."""
+        headerinfo = HeaderInfo(name, type, namespace, element_type)
+        if mustUnderstand:
+            headerinfo.mustUnderstand = 1
+        self.outheaders.append(headerinfo)
+        return headerinfo
+
+    def getInParameters(self):
+        """Return a sequence of the in parameters of the method."""
+        return self.inparams
+
+    def getOutParameters(self):
+        """Return a sequence of the out parameters of the method."""
+        return self.outparams
+
+    def getReturnParameter(self):
+        """Return param info about the return value of the method."""
+        return self.retval
+
+    def getInHeaders(self):
+        """Return a sequence of the in headers of the method."""
+        return self.inheaders
+
+    def getOutHeaders(self):
+        """Return a sequence of the out headers of the method."""
+        return self.outheaders
+
+
+class ParameterInfo:
+    """A ParameterInfo object captures parameter binding information."""
+    def __init__(self, name, type, namespace=None, element_type=0):
+        if element_type:
+            self.element_type = 1
+        if namespace is not None:
+            self.namespace = namespace
+        self.name = name
+        self.type = type
+
+    element_type = 0
+    namespace = None
+    default = None
+
+
+class HeaderInfo(ParameterInfo):
+    """A HeaderInfo object captures SOAP header binding information."""
+    def __init__(self, name, type, namespace, element_type=None):
+        ParameterInfo.__init__(self, name, type, namespace, element_type)
+
+    mustUnderstand = 0
+    actor = None
+
+
+def callInfoFromWSDL(port, name):
+    """Return a SOAPCallInfo given a WSDL port and operation name."""
+    wsdl = port.getService().getWSDL()
+    binding = port.getBinding()
+    portType = binding.getPortType()
+    operation = portType.operations[name]
+    opbinding = binding.operations[name]
+    messages = wsdl.messages
+    callinfo = SOAPCallInfo(name)
+
+    addrbinding = port.getAddressBinding()
+    if not isinstance(addrbinding, SoapAddressBinding):
+        raise ValueError, 'Unsupported binding type.'        
+    callinfo.location = addrbinding.location
+
+    soapbinding = binding.findBinding(SoapBinding)
+    if soapbinding is None:
+        raise ValueError, 'Missing soap:binding element.'
+    callinfo.transport = soapbinding.transport
+    callinfo.style = soapbinding.style or 'document'
+
+    soap_op_binding = opbinding.findBinding(SoapOperationBinding)
+    if soap_op_binding is not None:
+        callinfo.soapAction = soap_op_binding.soapAction
+        callinfo.style = soap_op_binding.style or callinfo.style
+
+    parameterOrder = operation.parameterOrder
+
+    if operation.input is not None:
+        message = messages[operation.input.message]
+        msgrole = opbinding.input
+
+        mime = msgrole.findBinding(MimeMultipartRelatedBinding)
+        if mime is not None:
+            raise ValueError, 'Mime bindings are not supported.'
+        else:
+            for item in msgrole.findBindings(SoapHeaderBinding):
+                part = messages[item.message].parts[item.part]
+                header = callinfo.addInHeaderInfo(
+                    part.name,
+                    part.element or part.type,
+                    item.namespace,
+                    element_type = part.element and 1 or 0
+                    )
+                header.encodingStyle = item.encodingStyle
+
+            body = msgrole.findBinding(SoapBodyBinding)
+            if body is None:
+                raise ValueError, 'Missing soap:body binding.'
+            callinfo.encodingStyle = body.encodingStyle
+            callinfo.namespace = body.namespace
+            callinfo.use = body.use
+
+            if body.parts is not None:
+                parts = []
+                for name in body.parts:
+                    parts.append(message.parts[name])
+            else:
+                parts = message.parts.values()
+
+            for part in parts:
+                callinfo.addInParameter(
+                    part.name,
+                    part.element or part.type,
+                    element_type = part.element and 1 or 0
+                    )
+
+    if operation.output is not None:
+        try:
+            message = messages[operation.output.message]
+        except KeyError:
+            if self.strict:
+                raise RuntimeError(
+                    "Recieved message not defined in the WSDL schema: %s" %
+                    operation.output.message)
+            else:
+                message = wsdl.addMessage(operation.output.message)
+                print "Warning:", \
+                      "Recieved message not defined in the WSDL schema.", \
+                      "Adding it."
+                print "Message:", operation.output.message
+         
+        msgrole = opbinding.output
+
+        mime = msgrole.findBinding(MimeMultipartRelatedBinding)
+        if mime is not None:
+            raise ValueError, 'Mime bindings are not supported.'
+        else:
+            for item in msgrole.findBindings(SoapHeaderBinding):
+                part = messages[item.message].parts[item.part]
+                header = callinfo.addOutHeaderInfo(
+                    part.name,
+                    part.element or part.type,
+                    item.namespace,
+                    element_type = part.element and 1 or 0
+                    )
+                header.encodingStyle = item.encodingStyle
+
+            body = msgrole.findBinding(SoapBodyBinding)
+            if body is None:
+                raise ValueError, 'Missing soap:body binding.'
+            callinfo.encodingStyle = body.encodingStyle
+            callinfo.namespace = body.namespace
+            callinfo.use = body.use
+
+            if body.parts is not None:
+                parts = []
+                for name in body.parts:
+                    parts.append(message.parts[name])
+            else:
+                parts = message.parts.values()
+
+            if parts:
+                for part in parts:
+                    callinfo.addOutParameter(
+                        part.name,
+                        part.element or part.type,
+                        element_type = part.element and 1 or 0
+                        )
+
+    return callinfo
diff --git a/wstools/XMLSchema.py b/wstools/XMLSchema.py
new file mode 100755 (executable)
index 0000000..eb5b3fe
--- /dev/null
@@ -0,0 +1,3116 @@
+# 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, sys, warnings
+from Namespaces import SCHEMA, XMLNS, SOAP, APACHE
+from Utility import DOM, DOMException, Collection, SplitQName, basejoin
+from StringIO import StringIO
+
+# If we have no threading, this should be a no-op
+try:
+    from threading import RLock
+except ImportError:
+    class RLock:
+        def acquire():
+            pass
+        def release():
+            pass
+
+# 
+# Collections in XMLSchema class
+# 
+TYPES = 'types'
+ATTRIBUTE_GROUPS = 'attr_groups'
+ATTRIBUTES = 'attr_decl'
+ELEMENTS = 'elements'
+MODEL_GROUPS = 'model_groups'
+BUILT_IN_NAMESPACES = [SOAP.ENC,] + SCHEMA.XSD_LIST + [APACHE.AXIS_NS]
+
+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.
+    """
+    
+    namespaceToSchema = {}
+    
+    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(schemaLocation, 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, url=None):
+        """Return an XMLSchema instance loaded from a file object.
+           file -- file object
+           url -- base location for resolving imports/includes.
+        """
+        reader = self.__readerClass()
+        reader.loadDocument(file)
+        schema = XMLSchema()
+        if url is not None:
+             schema.setBaseUrl(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, schema=None):
+        """Return an XMLSchema instance loaded from the given url.
+           url -- URL to dereference
+           schema -- Optional XMLSchema instance.
+        """
+        reader = self.__readerClass()
+        if self.__base_url:
+            url = basejoin(self.__base_url,url)
+
+        reader.loadFromURL(url)
+        schema = schema or XMLSchema()
+        schema.setBaseUrl(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
+        """
+        if self.__base_url:
+            filename = basejoin(self.__base_url,filename)
+        file = open(filename, 'rb')
+        try:
+            schema = self.loadFromStream(file, filename)
+        finally:
+            file.close()
+
+        return schema
+
+
+class SchemaError(Exception): 
+    pass
+
+class NoSchemaLocationWarning(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 getNode(self):
+        return self.__node
+    
+    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.
+    """ 
+    tag = None
+    __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 AllMarker(ModelGroupMarker): 
+    """marker for all model group
+    """
+    pass
+
+class ChoiceMarker(ModelGroupMarker): 
+    """marker for choice model group
+    """
+    pass
+
+class SequenceMarker(ModelGroupMarker): 
+    """marker for sequence model group
+    """
+    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 ListMarker: 
+    """marker for simple type list
+    """
+    pass
+
+class UnionMarker: 
+    """marker for simple type Union
+    """
+    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 isAll(self):
+        return isinstance(self, AllMarker)
+
+    def isChoice(self):
+        return isinstance(self, ChoiceMarker)
+
+    def isSequence(self):
+        return isinstance(self, SequenceMarker)
+
+    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)
+
+    def isList(self):
+        return isinstance(self, ListMarker)
+
+    def isUnion(self):
+        return isinstance(self, UnionMarker)
+
+
+##########################################################
+# 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 getItemTrace(self):
+        """Returns a node trace up to the <schema> item.
+        """
+        item, path, name, ref = self, [], 'name', 'ref'
+        while not isinstance(item,XMLSchema) and not isinstance(item,WSDLToolsAdapter):
+            attr = item.getAttribute(name)
+            if not attr:
+                attr = item.getAttribute(ref)
+                if not attr:
+                    path.append('<%s>' %(item.tag))
+                else: 
+                    path.append('<%s ref="%s">' %(item.tag, attr))
+            else:
+                path.append('<%s name="%s">' %(item.tag,attr))
+
+            item = item._parent()
+        try:
+            tns = item.getTargetNamespace()
+        except: 
+            tns = ''
+        path.append('<%s targetNamespace="%s">' %(item.tag, tns))
+        path.reverse()
+        return ''.join(path)
+
+    def getTargetNamespace(self):
+        """return targetNamespace
+        """
+        parent = self
+        targetNamespace = 'targetNamespace'
+        tns = self.attributes.get(targetNamespace)
+        while not tns and parent and parent._parent is not None:
+            parent = parent._parent()
+            tns = parent.attributes.get(targetNamespace)
+        return tns or ''
+
+    def getAttributeDeclaration(self, attribute):
+        """attribute -- attribute with a QName value (eg. type).
+           collection -- check types collection in parent Schema instance
+        """
+        return self.getQNameAttribute(ATTRIBUTES, attribute)
+
+    def getAttributeGroup(self, attribute):
+        """attribute -- attribute with a QName value (eg. type).
+           collection -- check types collection in parent Schema instance
+        """
+        return self.getQNameAttribute(ATTRIBUTE_GROUPS, attribute)
+
+    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 getModelGroup(self, attribute):
+        """attribute -- attribute with a QName value (eg. ref).
+           collection -- check model_group collection in parent Schema instance.
+        """
+        return self.getQNameAttribute(MODEL_GROUPS, 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.
+        """
+        tdc = self.getAttributeQName(attribute)
+        if not tdc:
+            return
+
+        obj = self.getSchemaItem(collection, tdc.getTargetNamespace(), tdc.getName())
+        if obj: 
+            return obj
+
+#        raise SchemaError, 'No schema item "%s" in collection %s' %(tdc, collection)
+        return
+
+    def getSchemaItem(self, collection, namespace, name):
+        """returns object instance representing namespace, name,
+           or if does not exist return None if built-in, else
+           raise SchemaError.
+           
+           namespace -- namespace item defined in.
+           name -- name of item.
+           collection -- collection in parent Schema instance to search.
+        """
+        parent = GetSchema(self)
+        if parent.targetNamespace == namespace:
+            try:
+                obj = getattr(parent, collection)[name]
+            except KeyError, ex:
+                raise KeyError, 'targetNamespace(%s) collection(%s) has no item(%s)'\
+                    %(namespace, collection, name)
+                    
+            return obj
+        
+        if not parent.imports.has_key(namespace):
+            if namespace in BUILT_IN_NAMESPACES:            
+                # built-in just return
+                # WARNING: expecting import if "redefine" or add to built-in namespace.
+                return
+            
+            raise SchemaError, 'schema "%s" does not import namespace "%s"' %(
+                parent.targetNamespace, namespace)
+            
+        # Lazy Eval
+        schema = parent.imports[namespace]
+        if not isinstance(schema, XMLSchema):
+            schema = schema.getSchema()    
+            if schema is not None:
+                parent.imports[namespace] = schema
+            
+        if schema is None:
+            if namespace in BUILT_IN_NAMESPACES:
+                # built-in just return
+                return
+            
+            raise SchemaError, 'no schema instance for imported namespace (%s).'\
+                %(namespace)
+                
+        if not isinstance(schema, XMLSchema):
+            raise TypeError, 'expecting XMLSchema instance not "%r"' %schema
+                
+        try:
+            obj = getattr(schema, collection)[name]
+        except KeyError, ex:
+            raise KeyError, 'targetNamespace(%s) collection(%s) has no item(%s)'\
+                %(namespace, collection, name)
+                    
+        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):
+                if prefix is None:
+                    return ''
+                raise SchemaError, 'unknown prefix %s' %prefix
+        return ns
+
+    def getAttribute(self, attribute):
+        """return requested attribute value or None
+        """
+        if type(attribute) in (list, tuple):
+            if len(attribute) != 2:
+                raise LookupError, 'To access attributes must use name or (namespace,name)'
+
+            ns_dict = self.attributes.get(attribute[0])
+            if ns_dict is None:
+                return None
+
+            return ns_dict.get(attribute[1])
+
+        return self.attributes.get(attribute)
+
+    def getAttributeQName(self, attribute):
+        """return requested attribute value as (namespace,name) or None 
+        """
+        qname = self.getAttribute(attribute)
+        if isinstance(qname, TypeDescriptionComponent) is True:
+            return qname
+        if qname is None:
+            return None
+
+        prefix,ncname = SplitQName(qname)
+        namespace = self.getXMLNS(prefix)
+        return TypeDescriptionComponent((namespace,ncname))
+
+    def getAttributeName(self):
+        """return attribute name or None
+        """
+        return self.getAttribute('name')
+    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
+
+        if not isinstance(self, WSDLToolsAdapter):
+            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 is not None and self.attributes.has_key(k) is False:
+                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,v in self.attributes.items():
+            # attribute #other, ie. not in empty namespace
+            if type(v) is dict:
+                continue
+            
+            # predefined prefixes xmlns, xml
+            if a in (XMLSchemaComponent.xmlns, XMLNS.XML):
+                continue
+            
+            if (a not in self.__class__.attributes.keys()) and not\
+                (self.isAttribute() and self.isReference()):
+                raise SchemaError, '%s, unknown attribute(%s,%s)' \
+                    %(self.getItemTrace(), a, self.attributes[a])
+
+
+class WSDLToolsAdapter(XMLSchemaComponent):
+    """WSDL Adapter to grab the attributes from the wsdl document node.
+    """
+    attributes = {'name':None, 'targetNamespace':None}
+    tag = 'definitions'
+
+    def __init__(self, wsdl):
+        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):
+    """<notation>
+       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')}
+    tag = 'notation'
+
+    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):
+    """<annotation>
+       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')}
+    tag = 'annotation'
+
+    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):
+        """<documentation>
+           parent:
+               annotation
+           attributes:
+               source, anyURI
+               xml:lang, language
+           contents:
+               mixed, any
+        """
+        attributes = {'source':None, 'xml:lang':None}
+        contents = {'xsd':('mixed', 'any')}
+        tag = 'documentation'
+
+        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):
+        """<appinfo>
+           parent:
+               annotation
+           attributes:
+               source, anyURI
+           contents:
+               mixed, any
+        """
+        attributes = {'source':None, 'anyURI':None}
+        contents = {'xsd':('mixed', 'any')}
+        tag = '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 == '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 <schema> element
+       information items. It represents the abstract notion of a schema
+       rather than a single schema document (or other representation).
+
+       <schema>
+       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 = ''
+    tag = 'schema'
+
+    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.__node = None
+        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 getNode(self):
+        """
+        Interacting with the underlying DOM tree.
+        """
+        return self.__node
+    
+    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 isElementFormDefaultQualified(self):
+        return self.attributes.get('elementFormDefault') == 'qualified'
+
+    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, location=None):
+        self.__node = 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()
+        for childNode in self.getContents(node):
+            component = SplitQName(childNode.getTagName())[1]
+                
+            if component == 'include':
+                tp = self.__class__.Include(self)
+                tp.fromDom(childNode)
+
+                sl = tp.attributes['schemaLocation']
+                schema = tp.getSchema()
+
+                if not self.getIncludeSchemas().has_key(sl):
+                    self.addIncludeSchema(sl, schema)
+
+                self.includes[sl] = tp
+
+                pn = childNode.getParentNode().getNode()
+                pn.removeChild(childNode.getNode())
+                for child in schema.getNode().getNode().childNodes:
+                    pn.appendChild(child.cloneNode(1))
+
+                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
+                        else:
+                            warnings.warn("Not keeping schema component.")
+      
+            elif component == 'import':
+                slocd = SchemaReader.namespaceToSchema
+                tp = self.__class__.Import(self)
+                tp.fromDom(childNode)
+                import_ns = tp.getAttribute('namespace') or\
+                    self.__class__.empty_namespace
+                schema = slocd.get(import_ns)
+                if schema is None:
+                    schema = XMLSchema()
+                    slocd[import_ns] = schema
+                    try:
+                        tp.loadSchema(schema)
+                    except NoSchemaLocationWarning, ex:
+                        # Dependency declaration, hopefully implementation
+                        # is aware of this namespace (eg. SOAP,WSDL,?)
+                        print "IMPORT: ", import_ns
+                        print ex
+                        del slocd[import_ns]
+                        continue
+                    except SchemaError, ex:
+                        #warnings.warn(\
+                        #    '<import namespace="%s" schemaLocation=?>, %s'\
+                        #    %(import_ns, 'failed to load schema instance')
+                        #)
+                        print ex
+                        del slocd[import_ns]
+                        class _LazyEvalImport(str):
+                            '''Lazy evaluation of import, replace entry in self.imports.'''
+                            #attributes = dict(namespace=import_ns)
+                            def getSchema(namespace):
+                                schema = slocd.get(namespace)
+                                if schema is None:
+                                    parent = self._parent()
+                                    wstypes = parent
+                                    if isinstance(parent, WSDLToolsAdapter):
+                                        wstypes = parent.getImportSchemas()
+                                    schema = wstypes.get(namespace)
+                                if isinstance(schema, XMLSchema):
+                                    self.imports[namespace] = schema
+                                    return schema
+
+                                return None
+
+                        self.imports[import_ns] = _LazyEvalImport(import_ns)
+                        continue
+                else:           
+                    tp._schema = schema
+            
+                if self.getImportSchemas().has_key(import_ns):
+                    warnings.warn(\
+                        'Detected multiple imports of the namespace "%s" '\
+                        %import_ns)
+            
+                self.addImportSchema(schema)
+                # spec says can have multiple imports of same namespace
+                # but purpose of import is just dependency declaration.
+                self.imports[import_ns] = tp
+                
+            elif component == 'redefine':
+                warnings.warn('redefine is ignored')
+            elif component == 'annotation':
+                warnings.warn('annotation is ignored')
+            elif component == 'attribute':
+                tp = AttributeDeclaration(self)
+                tp.fromDom(childNode)
+                self.attr_decl[tp.getAttribute('name')] = tp
+            elif component == 'attributeGroup':
+                tp = AttributeGroupDefinition(self)
+                tp.fromDom(childNode)
+                self.attr_groups[tp.getAttribute('name')] = tp
+            elif component == 'element':
+                tp = ElementDeclaration(self)
+                tp.fromDom(childNode)
+                self.elements[tp.getAttribute('name')] = tp
+            elif component == 'group':
+                tp = ModelGroupDefinition(self)
+                tp.fromDom(childNode)
+                self.model_groups[tp.getAttribute('name')] = tp
+            elif component == 'notation':
+                tp = Notation(self)
+                tp.fromDom(childNode)
+                self.notations[tp.getAttribute('name')] = tp
+            elif component == 'complexType':
+                tp = ComplexType(self)
+                tp.fromDom(childNode)
+                self.types[tp.getAttribute('name')] = tp
+            elif component == 'simpleType':
+                tp = SimpleType(self)
+                tp.fromDom(childNode)
+                self.types[tp.getAttribute('name')] = tp
+            else:
+                break
+
+    class Import(XMLSchemaComponent):
+        """<import> 
+           parent:
+               schema
+           attributes:
+               id -- ID
+               namespace -- anyURI
+               schemaLocation -- anyURI
+           contents:
+               annotation?
+        """
+        attributes = {'id':None,
+                      'namespace':None,
+                      'schemaLocation':None}
+        contents = {'xsd':['annotation']}
+        tag = 'import'
+
+        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.getTargetNamespace():
+                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
+            
+        def loadSchema(self, schema):
+            """
+            """
+            base_url = self._parent().getBaseUrl()
+            reader = SchemaReader(base_url=base_url)
+            reader._imports = self._parent().getImportSchemas()
+            reader._includes = self._parent().getIncludeSchemas()
+            self._schema = schema
+
+            if not self.attributes.has_key('schemaLocation'):
+                raise NoSchemaLocationWarning('no schemaLocation attribute in import')
+
+            reader.loadFromURL(self.attributes.get('schemaLocation'), schema)
+
+
+    class Include(XMLSchemaComponent):
+        """<include schemaLocation>
+           parent:
+               schema
+           attributes:
+               id -- ID
+               schemaLocation -- anyURI, required
+           contents:
+               annotation?
+        """
+        required = ['schemaLocation']
+        attributes = {'id':None,
+            'schemaLocation':None}
+        contents = {'xsd':['annotation']}
+        tag = 'include'
+
+        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()
+                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()
+                    
+                    # create schema before loading so chameleon include 
+                    # will evalute targetNamespace correctly.
+                    self._schema = XMLSchema(schema)
+                    reader.loadFromURL(url, self._schema)
+
+            return self._schema
+
+
+class AttributeDeclaration(XMLSchemaComponent,\
+                           AttributeMarker,\
+                           DeclarationMarker):
+    """<attribute name>
+       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']}
+    tag = 'attribute'
+
+    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):
+    """<attribute name>
+       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):
+    """<anyAttribute>
+       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']}
+    tag = 'anyAttribute'
+
+    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):
+    """<attribute ref>
+       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']}
+    tag = 'attribute'
+
+    def __init__(self, parent):
+        XMLSchemaComponent.__init__(self, parent)
+        self.annotation = None
+
+    def getAttributeDeclaration(self, attribute='ref'):
+        return XMLSchemaComponent.getAttributeDeclaration(self, attribute)
+
+    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):
+    """<attributeGroup name>
+       parents: 
+           schema, redefine
+       attributes:
+           id -- ID
+           name -- NCName,  required
+       contents:
+           annotation?, (attribute | attributeGroup)*, anyAttribute?
+    """
+    required = ['name']
+    attributes = {'id':None, 
+        'name':None}
+    contents = {'xsd':['annotation', 'attribute', 'attributeGroup', 'anyAttribute']}
+    tag = 'attributeGroup'
+
+    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(LocalAttributeDeclaration(self))
+                elif contents[indx].hasattr('ref'):
+                    content.append(AttributeReference(self))
+                else:
+                    raise SchemaError, 'Unknown attribute type'
+                content[-1].fromDom(contents[indx])
+            elif component == 'attributeGroup':
+                content.append(AttributeGroupReference(self))
+                content[-1].fromDom(contents[indx])
+            elif component == 'anyAttribute':
+                if len(contents) != indx+1: 
+                    raise SchemaError, 'anyAttribute is out of order in %s' %self.getItemTrace()
+                content.append(AttributeWildCard(self))
+                content[-1].fromDom(contents[indx])
+            else:
+                raise SchemaError, 'Unknown component (%s)' %(contents[indx].getTagName())
+
+        self.attr_content = tuple(content)
+
+class AttributeGroupReference(XMLSchemaComponent,\
+                              AttributeGroupMarker,\
+                              ReferenceMarker):
+    """<attributeGroup ref>
+       parents: 
+           complexType, restriction, extension, attributeGroup
+       attributes:
+           id -- ID
+           ref -- QName, required
+       contents:
+           annotation?
+    """
+    required = ['ref']
+    attributes = {'id':None, 
+        'ref':None}
+    contents = {'xsd':['annotation']}
+    tag = 'attributeGroup'
+
+    def __init__(self, parent):
+        XMLSchemaComponent.__init__(self, parent)
+        self.annotation = None
+
+    def getAttributeGroup(self, attribute='ref'):
+        """attribute -- attribute with a QName value (eg. type).
+           collection -- check types collection in parent Schema instance
+        """
+        return XMLSchemaComponent.getAttributeGroup(self, attribute)
+
+    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):
+        """<selector xpath>
+           parent: 
+               unique, key, keyref
+           attributes:
+               id -- ID
+               xpath -- XPath subset,  required
+           contents:
+               annotation?
+        """
+        required = ['xpath']
+        attributes = {'id':None, 
+            'xpath':None}
+        contents = {'xsd':['annotation']}
+        tag = 'selector'
+
+    class Field(Constraint): 
+        """<field xpath>
+           parent: 
+               unique, key, keyref
+           attributes:
+               id -- ID
+               xpath -- XPath subset,  required
+           contents:
+               annotation?
+        """
+        required = ['xpath']
+        attributes = {'id':None, 
+            'xpath':None}
+        contents = {'xsd':['annotation']}
+        tag = 'field'
+
+
+class Unique(IdentityConstrants):
+    """<unique name> 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']}
+    tag = 'unique'
+
+
+class Key(IdentityConstrants):
+    """<key name> 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']}
+    tag = 'key'
+
+
+class KeyRef(IdentityConstrants):
+    """<keyref name refer> 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']}
+    tag = 'keyref'
+
+
+class ElementDeclaration(XMLSchemaComponent,\
+                         ElementMarker,\
+                         DeclarationMarker):
+    """<element name>
+       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']}
+    tag = 'element'
+
+    def __init__(self, parent):
+        XMLSchemaComponent.__init__(self, parent)
+        self.annotation = None
+        self.content = None
+        self.constraints = ()
+
+    def isQualified(self):
+        """Global elements are always qualified.
+        """
+        return True
+    
+    def getAttribute(self, attribute):
+        """return attribute.
+        If attribute is type and it's None, and no simple or complex content, 
+        return the default type "xsd:anyType"
+        """
+        value = XMLSchemaComponent.getAttribute(self, attribute)
+        if attribute != 'type' or value is not None:
+            return value
+        
+        if self.content is not None:
+            return None
+        
+        parent = self
+        while 1:
+            nsdict = parent.attributes[XMLSchemaComponent.xmlns]
+            for k,v in nsdict.items():
+                if v not in SCHEMA.XSD_LIST: continue
+                return TypeDescriptionComponent((v, 'anyType'))
+            
+            if isinstance(parent, WSDLToolsAdapter)\
+                or not hasattr(parent, '_parent'):
+                break
+            
+            parent = parent._parent()
+            
+        raise SchemaError, 'failed to locate the XSD namespace'
+    
+    def getElementDeclaration(self, attribute):
+        raise Warning, 'invalid operation for <%s>' %self.tag
+
+    def getTypeDefinition(self, attribute=None):
+        """If attribute is None, "type" is assumed, return the corresponding
+        representation of the global type definition (TypeDefinition),
+        or the local definition if don't find "type".  To maintain backwards
+        compat, if attribute is provided call base class method.
+        """
+        if attribute:
+            return XMLSchemaComponent.getTypeDefinition(self, attribute)
+        gt = XMLSchemaComponent.getTypeDefinition(self, 'type')
+        if gt:
+            return gt
+        return self.content
+
+    def getConstraints(self):
+        return self._constraints
+    def setConstraints(self, constraints):
+        self._constraints = tuple(constraints)
+    constraints = property(getConstraints, setConstraints, None, "tuple of key, keyref, unique constraints")
+
+    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 = constraints
+
+
+class LocalElementDeclaration(ElementDeclaration,\
+                              LocalMarker):
+    """<element>
+       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']}
+
+    def isQualified(self):
+        """
+Local elements can be qualified or unqualifed according
+        to the attribute form, or the elementFormDefault.  By default
+        local elements are unqualified.
+        """
+        form = self.getAttribute('form')
+        if form == 'qualified':
+            return True
+        if form == 'unqualified':
+            return False
+        raise SchemaError, 'Bad form (%s) for element: %s' %(form, self.getItemTrace())
+
+
+class ElementReference(XMLSchemaComponent,\
+                       ElementMarker,\
+                       ReferenceMarker):
+    """<element ref>
+       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']}
+    tag = 'element'
+
+    def __init__(self, parent):
+        XMLSchemaComponent.__init__(self, parent)
+        self.annotation = None
+
+    def getElementDeclaration(self, attribute=None):
+        """If attribute is None, "ref" is assumed, return the corresponding
+        representation of the global element declaration (ElementDeclaration),
+        To maintain backwards compat, if attribute is provided call base class method.
+        """
+        if attribute:
+            return XMLSchemaComponent.getElementDeclaration(self, attribute)
+        return XMLSchemaComponent.getElementDeclaration(self, 'ref')
+    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):
+    """<any>
+       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']}
+    tag = 'any'
+
+    def __init__(self, parent):
+        XMLSchemaComponent.__init__(self, parent)
+        self.annotation = None
+
+    def isQualified(self):
+        """
+        Global elements are always qualified, but if processContents
+        are not strict could have dynamically generated local elements.
+        """
+        return GetSchema(self).isElementFormDefaultQualified()
+
+    def getAttribute(self, attribute):
+        """return attribute.
+        """
+        return XMLSchemaComponent.getAttribute(self, attribute)
+
+    def getTypeDefinition(self, attribute):
+        raise Warning, 'invalid operation for <%s>' % self.tag
+
+    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,\
+               SequenceMarker):
+    """<sequence>
+       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']}
+    tag = '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)
+        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,\
+          AllMarker):
+    """<all>
+       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']}
+    tag = 'all'
+
+    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,\
+             ChoiceMarker):
+    """<choice>
+       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']}
+    tag = 'choice'
+
+    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):
+    """<group name>
+       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']}
+    tag = 'group'
+
+    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):
+    """<group ref>
+       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']}
+    tag = 'group'
+
+    def __init__(self, parent):
+        XMLSchemaComponent.__init__(self, parent)
+        self.annotation = None
+
+    def getModelGroupReference(self):
+        return self.getModelGroup('ref')
+
+    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):
+    """<complexType name>
+       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']}
+    tag = 'complexType'
+
+    def __init__(self, parent):
+        XMLSchemaComponent.__init__(self, parent)
+        self.annotation = None
+        self.content = None
+        self.attr_content = None
+
+    def isMixed(self):
+        m = self.getAttribute('mixed')
+        if m == 0 or m == False:
+            return False
+        if isinstance(m, basestring) is True:
+            if m in ('false', '0'):
+                return False
+            if m in ('true', '1'):
+                return True
+
+        raise SchemaError, 'invalid value for attribute mixed(%s): %s'\
+            %(m, self.getItemTrace())
+
+    def getAttributeContent(self):
+        return self.attr_content
+
+    def getElementDeclaration(self, attribute):
+        raise Warning, 'invalid operation for <%s>' %self.tag
+
+    def getTypeDefinition(self, attribute):
+        raise Warning, 'invalid operation for <%s>' %self.tag
+
+    def fromDom(self, node):
+        self.setAttributes(node)
+        contents = self.getContents(node)
+      
+        indx = 0
+        num = len(contents)
+        if not num:
+            return
+
+        component = SplitQName(contents[indx].getTagName())[1]
+        if component == 'annotation':
+            self.annotation = Annotation(self)
+            self.annotation.fromDom(contents[indx])
+            indx += 1
+            if indx < num:
+                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): %s' \
+                        %(contents[indx].getTagName(),self.getItemTrace())
+                self.attr_content[-1].fromDom(contents[indx])
+                indx += 1
+
+    class _DerivedType(XMLSchemaComponent):
+        def __init__(self, parent):
+            XMLSchemaComponent.__init__(self, parent)
+            self.annotation = None
+            # XXX remove attribute derivation, inconsistent
+            self.derivation = 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 == '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)
+            self.content = self.derivation
+
+    class ComplexContent(_DerivedType,\
+                         ComplexMarker):
+        """<complexContent>
+           parents:
+               complexType
+           attributes:
+               id -- ID
+               mixed -- boolean, false
+
+           contents:
+               annotation?, (restriction | extension)
+        """
+        attributes = {'id':None, 
+            'mixed':0}
+        contents = {'xsd':['annotation', 'restriction', 'extension']}
+        tag = 'complexContent'
+
+        def isMixed(self):
+            m = self.getAttribute('mixed')
+            if m == 0 or m == False:
+                return False
+            if isinstance(m, basestring) is True:
+                if m in ('false', '0'):
+                    return False
+                if m in ('true', '1'):
+                    return True
+            raise SchemaError, 'invalid value for attribute mixed(%s): %s'\
+                %(m, self.getItemTrace())
+
+        class _DerivationBase(XMLSchemaComponent):
+            """<extension>,<restriction>
+               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):
+            """<extension base>
+               parents:
+                   complexContent
+               attributes:
+                   id -- ID
+                   base -- QName, required
+
+               contents:
+                   annotation?, (group | all | choice | sequence)?, 
+                       (attribute | attributeGroup)*, anyAttribute?
+            """
+            tag = 'extension'
+
+        class Restriction(_DerivationBase,\
+                          RestrictionMarker):
+            """<restriction base>
+               parents:
+                   complexContent
+               attributes:
+                   id -- ID
+                   base -- QName, required
+
+               contents:
+                   annotation?, (group | all | choice | sequence)?, 
+                       (attribute | attributeGroup)*, anyAttribute?
+            """
+            tag = 'restriction'
+
+
+    class SimpleContent(_DerivedType,\
+                        SimpleMarker):
+        """<simpleContent>
+           parents:
+               complexType
+           attributes:
+               id -- ID
+
+           contents:
+               annotation?, (restriction | extension)
+        """
+        attributes = {'id':None}
+        contents = {'xsd':['annotation', 'restriction', 'extension']}
+        tag = 'simpleContent'
+
+        class Extension(XMLSchemaComponent,\
+                        ExtensionMarker):
+            """<extension base>
+               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']}
+            tag = 'extension'
+
+            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)
+
+                if num:
+                    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):
+            """<restriction base>
+               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}
+            tag = 'restriction'
+
+            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(AnonymousSimpleType(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):
+    """<complexType>
+       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}
+    tag = 'complexType'
+    
+
+class SimpleType(XMLSchemaComponent,\
+                 DefinitionMarker,\
+                 SimpleMarker):
+    """<simpleType name>
+       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']}
+    tag = 'simpleType'
+
+    def __init__(self, parent):
+        XMLSchemaComponent.__init__(self, parent)
+        self.annotation = None
+        self.content = None
+
+    def getElementDeclaration(self, attribute):
+        raise Warning, 'invalid operation for <%s>' %self.tag
+
+    def getTypeDefinition(self, attribute):
+        raise Warning, 'invalid operation for <%s>' %self.tag
+
+    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):
+        """<restriction base>
+           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}
+        tag = 'restriction'
+
+        def __init__(self, parent):
+            XMLSchemaComponent.__init__(self, parent)
+            self.annotation = None
+            self.content = None
+            self.facets = None
+
+        def getAttributeBase(self):
+            return XMLSchemaComponent.getAttribute(self, 'base')
+
+        def getTypeDefinition(self, attribute='base'):
+            return XMLSchemaComponent.getTypeDefinition(self, attribute)
+
+        def getSimpleTypeContent(self):
+            for el in self.content:
+                if el.isSimple(): return el
+            return None
+
+        def fromDom(self, node):
+            self.facets = []
+            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:
+                    self.facets.append(contents[indx])
+                else:
+                    raise SchemaError, 'Unknown component (%s)' %(i.getTagName())
+            self.content = tuple(content)
+
+
+    class Union(XMLSchemaComponent,
+                UnionMarker):
+        """<union>
+           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']}
+        tag = '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)
+            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, 
+               ListMarker):
+        """<list>
+           parents:
+               simpleType
+           attributes:
+               id -- ID
+               itemType -- QName, required or simpleType child.
+
+           contents:
+               annotation?, simpleType?
+        """
+        attributes = {'id':None, 
+            'itemType':None }
+        contents = {'xsd':['annotation', 'simpleType']}
+        tag = 'list'
+
+        def __init__(self, parent):
+            XMLSchemaComponent.__init__(self, parent)
+            self.annotation = None
+            self.content = None
+
+        def getItemType(self):
+            return self.attributes.get('itemType')
+
+        def getTypeDefinition(self, attribute='itemType'):
+            """
+            return the type refered to by itemType attribute or
+            the simpleType content.  If returns None, then the 
+            type refered to by itemType is primitive.
+            """
+            tp = XMLSchemaComponent.getTypeDefinition(self, attribute)
+            return tp or self.content
+
+        def fromDom(self, node):
+            self.annotation = None
+            self.content = None
+            self.setAttributes(node)
+            contents = self.getContents(node)
+            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,\
+                          LocalMarker):
+    """<simpleType>
+       parents:
+           attribute, element, list, restriction, union
+       attributes:
+           id -- ID
+
+       contents:
+           annotation?, (restriction | list | union)
+    """
+    required = []
+    attributes = {'id':None}
+    tag = 'simpleType'
+
+
+class Redefine:
+    """<redefine>
+       parents:
+       attributes:
+
+       contents:
+    """
+    tag = 'redefine'
+
+
+###########################
+###########################
+
+
+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]
+
+
diff --git a/wstools/XMLname.py b/wstools/XMLname.py
new file mode 100644 (file)
index 0000000..5961160
--- /dev/null
@@ -0,0 +1,90 @@
+"""Translate strings to and from SOAP 1.2 XML name encoding
+
+Implements rules for mapping application defined name to XML names
+specified by the w3 SOAP working group for SOAP version 1.2 in
+Appendix A of "SOAP Version 1.2 Part 2: Adjuncts", W3C Working Draft
+17, December 2001, <http://www.w3.org/TR/soap12-part2/#namemap>
+
+Also see <http://www.w3.org/2000/xp/Group/xmlp-issues>.
+
+Author: Gregory R. Warnes <Gregory.R.Warnes@Pfizer.com>
+Date::  2002-04-25
+Version 0.9.0
+
+"""
+
+ident = "$Id$"
+
+from re import *
+
+
+def _NCNameChar(x):
+    return x.isalpha() or x.isdigit() or x=="." or x=='-' or x=="_" 
+
+
+def _NCNameStartChar(x):
+    return x.isalpha() or x=="_" 
+
+
+def _toUnicodeHex(x):
+    hexval = hex(ord(x[0]))[2:]
+    hexlen = len(hexval)
+    # Make hexval have either 4 or 8 digits by prepending 0's
+    if   (hexlen==1): hexval = "000" + hexval
+    elif (hexlen==2): hexval = "00"  + hexval
+    elif (hexlen==3): hexval = "0"   + hexval
+    elif (hexlen==4): hexval = ""    + hexval
+    elif (hexlen==5): hexval = "000" + hexval
+    elif (hexlen==6): hexval = "00"  + hexval
+    elif (hexlen==7): hexval = "0"   + hexval
+    elif (hexlen==8): hexval = ""    + hexval    
+    else: raise Exception, "Illegal Value returned from hex(ord(x))"
+    
+    return "_x"+ hexval + "_"
+
+
+def _fromUnicodeHex(x):
+    return eval( r'u"\u'+x[2:-1]+'"' ) 
+
+
+def toXMLname(string):
+    """Convert string to a XML name."""
+    if string.find(':') != -1 :
+        (prefix, localname) = string.split(':',1)
+    else:
+        prefix = None
+        localname = string
+    
+    T = unicode(localname)
+
+    N = len(localname)
+    X = [];
+    for i in range(N) :
+        if i< N-1 and T[i]==u'_' and T[i+1]==u'x':
+            X.append(u'_x005F_')
+        elif i==0 and N >= 3 and \
+                 ( T[0]==u'x' or T[0]==u'X' ) and \
+                 ( T[1]==u'm' or T[1]==u'M' ) and \
+                 ( T[2]==u'l' or T[2]==u'L' ):
+            X.append(u'_xFFFF_' + T[0])
+        elif (not _NCNameChar(T[i])) or (i==0 and not _NCNameStartChar(T[i])):
+            X.append(_toUnicodeHex(T[i]))
+        else:
+            X.append(T[i])
+    
+    if prefix:
+        return "%s:%s" % (prefix, u''.join(X))
+    return u''.join(X)
+
+
+def fromXMLname(string):
+    """Convert XML name to unicode string."""
+
+    retval = sub(r'_xFFFF_','', string )
+
+    def fun( matchobj ):
+        return _fromUnicodeHex( matchobj.group(0) )
+
+    retval = sub(r'_x[0-9A-Za-z]+_', fun, retval )
+        
+    return retval
diff --git a/wstools/__init__.py b/wstools/__init__.py
new file mode 100644 (file)
index 0000000..5b6f7ef
--- /dev/null
@@ -0,0 +1,9 @@
+#! /usr/bin/env python
+"""WSDL parsing services package for Web Services for Python."""
+
+ident = "$Id$"
+
+import WSDLTools
+import XMLname
+import logging
+
diff --git a/wstools/c14n.py b/wstools/c14n.py
new file mode 100755 (executable)
index 0000000..33305bf
--- /dev/null
@@ -0,0 +1,433 @@
+#! /usr/bin/env python
+'''XML Canonicalization
+
+Patches Applied to xml.dom.ext.c14n:
+    http://sourceforge.net/projects/pyxml/
+
+    [ 1444526 ] c14n.py: http://www.w3.org/TR/xml-exc-c14n/ fix
+        -- includes [ 829905 ] c14n.py fix for bug #825115, 
+           Date Submitted: 2003-10-24 23:43
+        -- include dependent namespace declarations declared in ancestor nodes 
+           (checking attributes and tags), 
+        -- handle InclusiveNamespaces PrefixList parameter
+
+This module generates canonical XML of a document or element.
+    http://www.w3.org/TR/2001/REC-xml-c14n-20010315
+and includes a prototype of exclusive canonicalization
+    http://www.w3.org/Signature/Drafts/xml-exc-c14n
+
+Requires PyXML 0.7.0 or later.
+
+Known issues if using Ft.Lib.pDomlette:
+    1. Unicode
+    2. does not white space normalize attributes of type NMTOKEN and ID?
+    3. seems to be include "\n" after importing external entities?
+
+Note, this version processes a DOM tree, and consequently it processes
+namespace nodes as attributes, not from a node's namespace axis. This
+permits simple document and element canonicalization without
+XPath. When XPath is used, the XPath result node list is passed and used to
+determine if the node is in the XPath result list, but little else.
+
+Authors:
+    "Joseph M. Reagle Jr." <reagle@w3.org>
+    "Rich Salz" <rsalz@zolera.com>
+
+$Date$ by $Author$
+'''
+
+_copyright = '''Copyright 2001, Zolera Systems Inc.  All Rights Reserved.
+Copyright 2001, MIT. All Rights Reserved.
+
+Distributed under the terms of:
+  Python 2.0 License or later.
+  http://www.python.org/2.0.1/license.html
+or
+  W3C Software License
+  http://www.w3.org/Consortium/Legal/copyright-software-19980720
+'''
+
+import string
+from xml.dom import Node
+try:
+    from xml.ns import XMLNS
+except:
+    class XMLNS:
+        BASE = "http://www.w3.org/2000/xmlns/"
+        XML = "http://www.w3.org/XML/1998/namespace"
+try:
+    import cStringIO
+    StringIO = cStringIO
+except ImportError:
+    import StringIO
+
+_attrs = lambda E: (E.attributes and E.attributes.values()) or []
+_children = lambda E: E.childNodes or []
+_IN_XML_NS = lambda n: n.name.startswith("xmlns")
+_inclusive = lambda n: n.unsuppressedPrefixes == None
+
+
+# Does a document/PI has lesser/greater document order than the
+# first element?
+_LesserElement, _Element, _GreaterElement = range(3)
+
+def _sorter(n1,n2):
+    '''_sorter(n1,n2) -> int
+    Sorting predicate for non-NS attributes.'''
+
+    i = cmp(n1.namespaceURI, n2.namespaceURI)
+    if i: return i
+    return cmp(n1.localName, n2.localName)
+
+
+def _sorter_ns(n1,n2):
+    '''_sorter_ns((n,v),(n,v)) -> int
+    "(an empty namespace URI is lexicographically least)."'''
+
+    if n1[0] == 'xmlns': return -1
+    if n2[0] == 'xmlns': return 1
+    return cmp(n1[0], n2[0])
+
+def _utilized(n, node, other_attrs, unsuppressedPrefixes):
+    '''_utilized(n, node, other_attrs, unsuppressedPrefixes) -> boolean
+    Return true if that nodespace is utilized within the node'''
+    if n.startswith('xmlns:'):
+        n = n[6:]
+    elif n.startswith('xmlns'):
+        n = n[5:]
+    if (n=="" and node.prefix in ["#default", None]) or \
+        n == node.prefix or n in unsuppressedPrefixes: 
+            return 1
+    for attr in other_attrs:
+        if n == attr.prefix: return 1
+    # For exclusive need to look at attributes
+    if unsuppressedPrefixes is not None:
+        for attr in _attrs(node):
+            if n == attr.prefix: return 1
+            
+    return 0
+
+
+def _inclusiveNamespacePrefixes(node, context, unsuppressedPrefixes):
+    '''http://www.w3.org/TR/xml-exc-c14n/ 
+    InclusiveNamespaces PrefixList parameter, which lists namespace prefixes that 
+    are handled in the manner described by the Canonical XML Recommendation'''
+    inclusive = []
+    if node.prefix:
+        usedPrefixes = ['xmlns:%s' %node.prefix]
+    else:
+        usedPrefixes = ['xmlns']
+
+    for a in _attrs(node):
+        if a.nodeName.startswith('xmlns') or not a.prefix: continue
+        usedPrefixes.append('xmlns:%s' %a.prefix)
+
+    unused_namespace_dict = {}
+    for attr in context:
+        n = attr.nodeName
+        if n in unsuppressedPrefixes:
+            inclusive.append(attr)
+        elif n.startswith('xmlns:') and n[6:] in unsuppressedPrefixes:
+            inclusive.append(attr)
+        elif n.startswith('xmlns') and n[5:] in unsuppressedPrefixes:
+            inclusive.append(attr)
+        elif attr.nodeName in usedPrefixes:
+            inclusive.append(attr)
+        elif n.startswith('xmlns:'):
+            unused_namespace_dict[n] = attr.value
+
+    return inclusive, unused_namespace_dict
+
+#_in_subset = lambda subset, node: not subset or node in subset
+_in_subset = lambda subset, node: subset is None or node in subset # rich's tweak
+
+
+class _implementation:
+    '''Implementation class for C14N. This accompanies a node during it's
+    processing and includes the parameters and processing state.'''
+
+    # Handler for each node type; populated during module instantiation.
+    handlers = {}
+
+    def __init__(self, node, write, **kw):
+        '''Create and run the implementation.'''
+        self.write = write
+        self.subset = kw.get('subset')
+        self.comments = kw.get('comments', 0)
+        self.unsuppressedPrefixes = kw.get('unsuppressedPrefixes')
+        nsdict = kw.get('nsdict', { 'xml': XMLNS.XML, 'xmlns': XMLNS.BASE })
+        
+        # Processing state.
+        self.state = (nsdict, {'xml':''}, {}, {}) #0422
+        
+        if node.nodeType == Node.DOCUMENT_NODE:
+            self._do_document(node)
+        elif node.nodeType == Node.ELEMENT_NODE:
+            self.documentOrder = _Element        # At document element
+            if not _inclusive(self):
+                inherited,unused = _inclusiveNamespacePrefixes(node, self._inherit_context(node), 
+                                self.unsuppressedPrefixes)
+                self._do_element(node, inherited, unused=unused)
+            else:
+                inherited = self._inherit_context(node)
+                self._do_element(node, inherited)
+        elif node.nodeType == Node.DOCUMENT_TYPE_NODE:
+            pass
+        else:
+            raise TypeError, str(node)
+
+
+    def _inherit_context(self, node):
+        '''_inherit_context(self, node) -> list
+        Scan ancestors of attribute and namespace context.  Used only
+        for single element node canonicalization, not for subset
+        canonicalization.'''
+
+        # Collect the initial list of xml:foo attributes.
+        xmlattrs = filter(_IN_XML_NS, _attrs(node))
+
+        # Walk up and get all xml:XXX attributes we inherit.
+        inherited, parent = [], node.parentNode
+        while parent and parent.nodeType == Node.ELEMENT_NODE:
+            for a in filter(_IN_XML_NS, _attrs(parent)):
+                n = a.localName
+                if n not in xmlattrs:
+                    xmlattrs.append(n)
+                    inherited.append(a)
+            parent = parent.parentNode
+        return inherited
+
+
+    def _do_document(self, node):
+        '''_do_document(self, node) -> None
+        Process a document node. documentOrder holds whether the document
+        element has been encountered such that PIs/comments can be written
+        as specified.'''
+
+        self.documentOrder = _LesserElement
+        for child in node.childNodes:
+            if child.nodeType == Node.ELEMENT_NODE:
+                self.documentOrder = _Element        # At document element
+                self._do_element(child)
+                self.documentOrder = _GreaterElement # After document element
+            elif child.nodeType == Node.PROCESSING_INSTRUCTION_NODE:
+                self._do_pi(child)
+            elif child.nodeType == Node.COMMENT_NODE:
+                self._do_comment(child)
+            elif child.nodeType == Node.DOCUMENT_TYPE_NODE:
+                pass
+            else:
+                raise TypeError, str(child)
+    handlers[Node.DOCUMENT_NODE] = _do_document
+
+
+    def _do_text(self, node):
+        '''_do_text(self, node) -> None
+        Process a text or CDATA node.  Render various special characters
+        as their C14N entity representations.'''
+        if not _in_subset(self.subset, node): return
+        s = string.replace(node.data, "&", "&amp;")
+        s = string.replace(s, "<", "&lt;")
+        s = string.replace(s, ">", "&gt;")
+        s = string.replace(s, "\015", "&#xD;")
+        if s: self.write(s)
+    handlers[Node.TEXT_NODE] = _do_text
+    handlers[Node.CDATA_SECTION_NODE] = _do_text
+
+
+    def _do_pi(self, node):
+        '''_do_pi(self, node) -> None
+        Process a PI node. Render a leading or trailing #xA if the
+        document order of the PI is greater or lesser (respectively)
+        than the document element.
+        '''
+        if not _in_subset(self.subset, node): return
+        W = self.write
+        if self.documentOrder == _GreaterElement: W('\n')
+        W('<?')
+        W(node.nodeName)
+        s = node.data
+        if s:
+            W(' ')
+            W(s)
+        W('?>')
+        if self.documentOrder == _LesserElement: W('\n')
+    handlers[Node.PROCESSING_INSTRUCTION_NODE] = _do_pi
+
+
+    def _do_comment(self, node):
+        '''_do_comment(self, node) -> None
+        Process a comment node. Render a leading or trailing #xA if the
+        document order of the comment is greater or lesser (respectively)
+        than the document element.
+        '''
+        if not _in_subset(self.subset, node): return
+        if self.comments:
+            W = self.write
+            if self.documentOrder == _GreaterElement: W('\n')
+            W('<!--')
+            W(node.data)
+            W('-->')
+            if self.documentOrder == _LesserElement: W('\n')
+    handlers[Node.COMMENT_NODE] = _do_comment
+
+
+    def _do_attr(self, n, value):
+        ''''_do_attr(self, node) -> None
+        Process an attribute.'''
+
+        W = self.write
+        W(' ')
+        W(n)
+        W('="')
+        s = string.replace(value, "&", "&amp;")
+        s = string.replace(s, "<", "&lt;")
+        s = string.replace(s, '"', '&quot;')
+        s = string.replace(s, '\011', '&#x9')
+        s = string.replace(s, '\012', '&#xA')
+        s = string.replace(s, '\015', '&#xD')
+        W(s)
+        W('"')
+
+
+    def _do_element(self, node, initial_other_attrs = [], unused = None):
+        '''_do_element(self, node, initial_other_attrs = [], unused = {}) -> None
+        Process an element (and its children).'''
+
+        # Get state (from the stack) make local copies.
+        #   ns_parent -- NS declarations in parent
+        #   ns_rendered -- NS nodes rendered by ancestors
+        #        ns_local -- NS declarations relevant to this element
+        #   xml_attrs -- Attributes in XML namespace from parent
+        #       xml_attrs_local -- Local attributes in XML namespace.
+        #   ns_unused_inherited -- not rendered namespaces, used for exclusive 
+        ns_parent, ns_rendered, xml_attrs = \
+                self.state[0], self.state[1].copy(), self.state[2].copy() #0422
+                
+        ns_unused_inherited = unused
+        if unused is None:
+            ns_unused_inherited = self.state[3].copy()
+            
+        ns_local = ns_parent.copy()
+        inclusive = _inclusive(self)
+        xml_attrs_local = {}
+
+        # Divide attributes into NS, XML, and others.
+        other_attrs = []
+        in_subset = _in_subset(self.subset, node)
+        for a in initial_other_attrs + _attrs(node):
+            if a.namespaceURI == XMLNS.BASE:
+                n = a.nodeName
+                if n == "xmlns:": n = "xmlns"        # DOM bug workaround
+                ns_local[n] = a.nodeValue
+            elif a.namespaceURI == XMLNS.XML:
+                if inclusive or (in_subset and  _in_subset(self.subset, a)): #020925 Test to see if attribute node in subset
+                    xml_attrs_local[a.nodeName] = a #0426
+            else:
+                if  _in_subset(self.subset, a):     #020925 Test to see if attribute node in subset
+                    other_attrs.append(a)
+                    
+#                # TODO: exclusive, might need to define xmlns:prefix here
+#                if not inclusive and a.prefix is not None and not ns_rendered.has_key('xmlns:%s' %a.prefix):
+#                    ns_local['xmlns:%s' %a.prefix] = ??
+
+            #add local xml:foo attributes to ancestor's xml:foo attributes
+            xml_attrs.update(xml_attrs_local)
+
+        # Render the node
+        W, name = self.write, None
+        if in_subset: 
+            name = node.nodeName
+            if not inclusive:
+                if node.prefix is not None:
+                    prefix = 'xmlns:%s' %node.prefix
+                else:
+                    prefix = 'xmlns'
+                    
+                if not ns_rendered.has_key(prefix) and not ns_local.has_key(prefix):
+                    if not ns_unused_inherited.has_key(prefix):
+                        raise RuntimeError,\
+                            'For exclusive c14n, unable to map prefix "%s" in %s' %(
+                            prefix, node)
+                    
+                    ns_local[prefix] = ns_unused_inherited[prefix]
+                    del ns_unused_inherited[prefix]
+                
+            W('<')
+            W(name)
+
+            # Create list of NS attributes to render.
+            ns_to_render = []
+            for n,v in ns_local.items():
+
+                # If default namespace is XMLNS.BASE or empty,
+                # and if an ancestor was the same
+                if n == "xmlns" and v in [ XMLNS.BASE, '' ] \
+                and ns_rendered.get('xmlns') in [ XMLNS.BASE, '', None ]:
+                    continue
+
+                # "omit namespace node with local name xml, which defines
+                # the xml prefix, if its string value is
+                # http://www.w3.org/XML/1998/namespace."
+                if n in ["xmlns:xml", "xml"] \
+                and v in [ 'http://www.w3.org/XML/1998/namespace' ]:
+                    continue
+
+
+                # If not previously rendered
+                # and it's inclusive  or utilized
+                if (n,v) not in ns_rendered.items():
+                    if inclusive or _utilized(n, node, other_attrs, self.unsuppressedPrefixes):
+                        ns_to_render.append((n, v))
+                    elif not inclusive:
+                        ns_unused_inherited[n] = v
+
+            # Sort and render the ns, marking what was rendered.
+            ns_to_render.sort(_sorter_ns)
+            for n,v in ns_to_render:
+                self._do_attr(n, v)
+                ns_rendered[n]=v    #0417
+
+            # If exclusive or the parent is in the subset, add the local xml attributes
+            # Else, add all local and ancestor xml attributes
+            # Sort and render the attributes.
+            if not inclusive or _in_subset(self.subset,node.parentNode):  #0426
+                other_attrs.extend(xml_attrs_local.values())
+            else:
+                other_attrs.extend(xml_attrs.values())
+            other_attrs.sort(_sorter)
+            for a in other_attrs:
+                self._do_attr(a.nodeName, a.value)
+            W('>')
+
+        # Push state, recurse, pop state.
+        state, self.state = self.state, (ns_local, ns_rendered, xml_attrs, ns_unused_inherited)
+        for c in _children(node):
+            _implementation.handlers[c.nodeType](self, c)
+        self.state = state
+
+        if name: W('</%s>' % name)
+    handlers[Node.ELEMENT_NODE] = _do_element
+
+
+def Canonicalize(node, output=None, **kw):
+    '''Canonicalize(node, output=None, **kw) -> UTF-8
+
+    Canonicalize a DOM document/element node and all descendents.
+    Return the text; if output is specified then output.write will
+    be called to output the text and None will be returned
+    Keyword parameters:
+        nsdict: a dictionary of prefix:uri namespace entries
+                assumed to exist in the surrounding context
+        comments: keep comments if non-zero (default is 0)
+        subset: Canonical XML subsetting resulting from XPath
+                (default is [])
+        unsuppressedPrefixes: do exclusive C14N, and this specifies the
+                prefixes that should be inherited.
+    '''
+    if output:
+        apply(_implementation, (node, output.write), kw)
+    else:
+        s = StringIO.StringIO()
+        apply(_implementation, (node, s.write), kw)
+        return s.getvalue()
diff --git a/wstools/logging.py b/wstools/logging.py
new file mode 100644 (file)
index 0000000..9c33b00
--- /dev/null
@@ -0,0 +1,274 @@
+# 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. 
+#
+"""Logging"""
+ident = "$Id$"
+import os, sys
+
+WARN = 1
+DEBUG = 2
+
+
+class ILogger:
+    '''Logger interface, by default this class
+    will be used and logging calls are no-ops.
+    '''
+    level = 0
+    def __init__(self, msg):
+        return
+    def warning(self, *args, **kw):
+        return
+    def debug(self, *args, **kw):
+        return
+    def error(self, *args, **kw):
+        return
+    def setLevel(cls, level):
+        cls.level = level
+    setLevel = classmethod(setLevel)
+    
+    debugOn = lambda self: self.level >= DEBUG
+    warnOn = lambda self: self.level >= WARN
+    
+
+class BasicLogger(ILogger):
+    last = ''
+    
+    def __init__(self, msg, out=sys.stdout):
+        self.msg, self.out = msg, out
+
+    def warning(self, msg, *args, **kw):
+        if self.warnOn() is False: return
+        if BasicLogger.last != self.msg:
+            BasicLogger.last = self.msg
+            print >>self, "---- ", self.msg, " ----"
+        print >>self, "    %s  " %self.WARN,
+        print >>self, msg %args
+    WARN = '[WARN]'
+    def debug(self, msg, *args, **kw):
+        if self.debugOn() is False: return
+        if BasicLogger.last != self.msg:
+            BasicLogger.last = self.msg
+            print >>self, "---- ", self.msg, " ----"
+        print >>self, "    %s  " %self.DEBUG,
+        print >>self, msg %args
+    DEBUG = '[DEBUG]'
+    def error(self, msg, *args, **kw):
+        if BasicLogger.last != self.msg:
+            BasicLogger.last = self.msg
+            print >>self, "---- ", self.msg, " ----"
+        print >>self, "    %s  " %self.ERROR,
+        print >>self, msg %args
+    ERROR = '[ERROR]'
+
+    def write(self, *args):
+        '''Write convenience function; writes strings.
+        '''
+        for s in args: self.out.write(s)
+        event = ''.join(*args)
+
+
+_LoggerClass = BasicLogger
+
+class GridLogger(ILogger):
+    def debug(self, msg, *args, **kw):
+        kw['component'] = self.msg
+        gridLog(event=msg %args, level='DEBUG', **kw)
+
+    def warning(self, msg, *args, **kw):
+        kw['component'] = self.msg
+        gridLog(event=msg %args, level='WARNING', **kw)
+
+    def error(self, msg, *args, **kw):
+        kw['component'] = self.msg
+        gridLog(event=msg %args, level='ERROR', **kw)
+
+
+# 
+# Registry of send functions for gridLog
+# 
+GLRegistry = {}
+
+class GLRecord(dict):
+    """Grid Logging Best Practices Record, Distributed Logging Utilities
+
+    The following names are reserved:
+
+    event -- log event name
+        Below is EBNF for the event name part of a log message.
+            name       = <nodot> ( "." <name> )? 
+            nodot      = {RFC3896-chars except "."}
+
+        Suffixes:
+            start: Immediately before the first action in a task.
+            end: Immediately after the last action in a task (that succeeded).
+            error: an error condition that does not correspond to an end event.
+
+    ts -- timestamp
+    level -- logging level (see levels below)
+    status -- integer status code
+    gid -- global grid identifier 
+    gid, cgid -- parent/child identifiers
+    prog -- program name
+
+
+    More info: http://www.cedps.net/wiki/index.php/LoggingBestPractices#Python
+
+    reserved -- list of reserved names, 
+    omitname -- list of reserved names, output only values ('ts', 'event',)
+    levels -- dict of levels and description
+    """
+    reserved = ('ts', 'event', 'level', 'status', 'gid', 'prog')
+    omitname = ()
+    levels = dict(FATAL='Component cannot continue, or system is unusable.',
+        ALERT='Action must be taken immediately.',
+        CRITICAL='Critical conditions (on the system).',
+        ERROR='Errors in the component; not errors from elsewhere.',
+        WARNING='Problems that are recovered from, usually.',
+        NOTICE='Normal but significant condition.',
+        INFO='Informational messages that would be useful to a deployer or administrator.',
+        DEBUG='Lower level information concerning program logic decisions, internal state, etc.',
+        TRACE='Finest granularity, similar to "stepping through" the component or system.',
+    )
+
+    def __init__(self, date=None, **kw):
+        kw['ts'] = date or self.GLDate()
+        kw['gid'] = kw.get('gid') or os.getpid()
+        dict.__init__(self, kw)
+
+    def __str__(self):
+        """
+        """
+        from cStringIO import StringIO
+        s = StringIO(); n = " "
+        reserved = self.reserved; omitname = self.omitname; levels = self.levels
+
+        for k in ( list(filter(lambda i: self.has_key(i), reserved)) + 
+            list(filter(lambda i: i not in reserved, self.keys()))
+        ):
+            v = self[k]
+            if k in omitname: 
+                s.write( "%s " %self.format[type(v)](v) )
+                continue
+
+            if k == reserved[2] and v not in levels:
+                pass
+
+            s.write( "%s=%s " %(k, self.format[type(v)](v) ) )
+
+        s.write("\n")
+        return s.getvalue()
+
+    class GLDate(str):
+        """Grid logging Date Format
+        all timestamps should all be in the same time zone (UTC). 
+        Grid timestamp value format that is a highly readable variant of the ISO8601 time standard [1]:
+
+       YYYY-MM-DDTHH:MM:SS.SSSSSSZ 
+
+        """
+        def __new__(self, args=None):
+            """args -- datetime (year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
+            """
+            import datetime
+            args = args or datetime.datetime.utcnow()
+            l = (args.year, args.month, args.day, args.hour, args.minute, args.second, 
+                 args.microsecond, args.tzinfo or 'Z')
+
+            return str.__new__(self, "%04d-%02d-%02dT%02d:%02d:%02d.%06d%s" %l)
+
+    format = { int:str, float:lambda x: "%lf" % x, long:str, str:lambda x:x,
+        unicode:str, GLDate:str, }
+
+
+def gridLog(**kw):
+    """Send GLRecord, Distributed Logging Utilities
+    If the scheme is passed as a keyword parameter
+    the value is expected to be a callable function
+    that takes 2 parameters: url, outputStr
+
+    GRIDLOG_ON   -- turn grid logging on
+    GRIDLOG_DEST -- provide URL destination
+    """
+    import os
+
+    if not bool( int(os.environ.get('GRIDLOG_ON', 0)) ):
+        return
+
+    url = os.environ.get('GRIDLOG_DEST')
+    if url is None: 
+        return
+
+    ## NOTE: urlparse problem w/customized schemes 
+    try:
+        scheme = url[:url.find('://')]
+        send = GLRegistry[scheme]
+        send( url, str(GLRecord(**kw)), )
+    except Exception, ex:
+        print >>sys.stderr, "*** gridLog failed -- %s" %(str(kw))
+
+
+def sendUDP(url, outputStr):
+    from socket import socket, AF_INET, SOCK_DGRAM
+    idx1 = url.find('://') + 3; idx2 = url.find('/', idx1)
+    if idx2 < idx1: idx2 = len(url)
+    netloc = url[idx1:idx2]
+    host,port = (netloc.split(':')+[80])[0:2]
+    socket(AF_INET, SOCK_DGRAM).sendto( outputStr, (host,int(port)), )
+
+def writeToFile(url, outputStr):
+    print >> open(url.split('://')[1], 'a+'), outputStr
+
+GLRegistry["gridlog-udp"] = sendUDP
+GLRegistry["file"] = writeToFile
+
+
+def setBasicLogger():
+    '''Use Basic Logger. 
+    '''
+    setLoggerClass(BasicLogger)
+    BasicLogger.setLevel(0)
+
+def setGridLogger():
+    '''Use GridLogger for all logging events.
+    '''
+    setLoggerClass(GridLogger)
+
+def setBasicLoggerWARN():
+    '''Use Basic Logger.
+    '''
+    setLoggerClass(BasicLogger)
+    BasicLogger.setLevel(WARN)
+
+def setBasicLoggerDEBUG():
+    '''Use Basic Logger.
+    '''
+    setLoggerClass(BasicLogger)
+    BasicLogger.setLevel(DEBUG)
+
+def setLoggerClass(loggingClass):
+    '''Set Logging Class.
+    '''
+
+def setLoggerClass(loggingClass):
+    '''Set Logging Class.
+    '''
+    assert issubclass(loggingClass, ILogger), 'loggingClass must subclass ILogger'
+    global _LoggerClass
+    _LoggerClass = loggingClass
+
+def setLevel(level=0):
+    '''Set Global Logging Level.
+    '''
+    ILogger.level = level
+
+def getLevel():
+    return ILogger.level
+
+def getLogger(msg):
+    '''Return instance of Logging class.
+    '''
+    return _LoggerClass(msg)
+
+
diff --git a/wstools/tests/.cvsignore b/wstools/tests/.cvsignore
new file mode 100644 (file)
index 0000000..0d20b64
--- /dev/null
@@ -0,0 +1 @@
+*.pyc
diff --git a/wstools/tests/README b/wstools/tests/README
new file mode 100644 (file)
index 0000000..7c1097a
--- /dev/null
@@ -0,0 +1,52 @@
+Two top level modules have been provided to run the tests. "test_wstools.py"
+is used to run all of the local tests. "test_wstools_net.py" is used to run
+all of the tests that require network access.
+
+Add the -v option for more informative feedback.
+
+ADDING TESTS:
+    1. For Stand-Alone tests add WSDL FILE to appropriate archive file
+        Need to add a NEW Archive?:
+            config.txt [files] "archive" -- tuple of all archive files, 
+            if you need to create a new archive append the archive 
+            name to the 'archive' tuple.
+
+    2. Edit config.txt section(s):
+        option -- name by which service will be referenced in test case.
+            Need an entry under appropriate section(s), this name
+            must be unique within each section it appears but it may 
+            appear in multiple sections.
+
+        config.txt "test" sections:
+          Stand-Alone --  add "option" under [services_by_file]
+               eg. amazon = exports/AmazonWebServices.wsdl
+
+          Network -- add "option" under [services_by_http]
+               eg. amazon = http://soap.amazon.com/schemas/AmazonWebServices.wsdl
+
+          Broken -- add "option" under [broken]
+
+    3. Done
+
+
+CONTENTS OF SAMPLE WSDL/XSD:
+    schema -- Taken from globus-3.0.1(http://www.globus.org)
+    xmethods -- Taken from XMethods(http://www.xmethods.com)
+         airport.wsdl
+         AmazonWebServices.wsdl
+         books.wsdl
+         Distance.wsdl
+         freedb.wsdl
+         globalweather.wsdl
+         IHaddock.wsdl
+         ip2geo.wsdl
+         magic.wsdl
+         query.wsdl
+         RateInfo.wsdl
+         SHA1Encrypt.wsdl
+         siteInspect.wsdl
+         TemperatureService.wsdl
+         usweather.wsdl
+         rtf2html.xml
+         SolveSystem.wsdl.xml
+         zip2geo.wsdl
diff --git a/wstools/tests/__init__.py b/wstools/tests/__init__.py
new file mode 100644 (file)
index 0000000..ee3ecd2
--- /dev/null
@@ -0,0 +1 @@
+#! /usr/bin/env python
diff --git a/wstools/tests/config.txt b/wstools/tests/config.txt
new file mode 100644 (file)
index 0000000..6ebbe19
--- /dev/null
@@ -0,0 +1,362 @@
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See Copyright for copyright notice!
+###########################################################################
+
+###########################################################################
+# Config file for the unit test framework. 
+# Sections below.
+###########################################################################
+
+
+
+##########################################################################
+# SECTION [files] - archives of wsdl/xsd files.
+# 
+##########################################################################
+[files]
+archives = ('xmethods.tar.gz', 'schema.tar.gz')
+
+##########################################################################
+# SECTION [services_by_file] - all services locally available for
+#     testing.
+##########################################################################
+[services_by_file]
+ogsi = schema/ogsi/ogsi_service.wsdl
+airport = xmethods/airport.wsdl
+distance = xmethods/Distance.wsdl
+freedb = xmethods/freedb.wsdl
+globalweather = xmethods/globalweather.wsdl
+IHaddock = xmethods/IHaddock.wsdl
+ip2geo = xmethods/ip2geo.wsdl
+magic = xmethods/magic.wsdl
+query = xmethods/query.wsdl
+RateInfo = xmethods/RateInfo.wsdl
+SHA1Encrypt = xmethods/SHA1Encrypt.wsdl
+siteInsepct = xmethods/siteInspect.wsdl
+TemperatureService = xmethods/TemperatureService.wsdl
+usweather = xmethods/usweather.wsdl
+zip2geo = xmethods/zip2geo.wsdl
+SolveSystem = xmethods/SolveSystem.wsdl.xml
+
+##########################################################################
+# SECTION  [services_by_http]  - 
+##########################################################################
+[services_by_http] 
+
+# no schemas
+AbysalSendEmail  =  http://www.abysal.com/soap/AbysalEmail.wsdl
+BNQuoteService  =  http://www.xmethods.net/sd/2001/BNQuoteService.wsdl
+BabelFishService  =  http://www.xmethods.net/sd/2001/BabelFishService.wsdl
+Bible  =  http://www.stgregorioschurchdc.org/wsdl/Bible.wsdl
+Blast  =  http://xml.nig.ac.jp/wsdl/Blast.wsdl
+CATrafficService  =  http://www.xmethods.net/sd/2001/CATrafficService.wsdl
+Calendar  =  http://www.stgregorioschurchdc.org/wsdl/Calendar.wsdl
+ClustalW  =  http://xml.nig.ac.jp/wsdl/ClustalW.wsdl
+CountryInfoLookupService  =  http://www.cs.uga.edu/~sent/xmethods/CountryInfoLookup.wsdl
+CurrencyExchangeService  =  http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl
+DDBJ  =  http://xml.nig.ac.jp/wsdl/DDBJ.wsdl
+DiscordianService  =  http://www.compkarori.com/wsdl/discordian.wsdl
+DistanceService  =  http://webservices.imacination.com/distance/Distance.jws?wsdl
+DocServService  =  http://docserv.aurigalogic.com/docserv.wsdl
+EMWebFunctionWS  =  http://www.eyemaginations.com/cgi-bin/getWSDL.pl?wsdl=WebFunction.wsdl
+Fasta  =  http://xml.nig.ac.jp/wsdl/Fasta.wsdl
+FaxService  =  http://oneoutbox.com/wsdl/FaxService.wsdl
+FreeFaxService  =  http://www.OneOutBox.com/wsdl/FreeFaxService.wsdl
+GetEntry  =  http://xml.nig.ac.jp/wsdl/GetEntry.wsdl
+IBorlandBabelservice  =  http://ww6.borland.com/webservices/BorlandBabel/BorlandBabel.exe/wsdl/IBorlandBabel
+IBorlandChessservice  =  http://www.danmarinescu.com/WebServices/ChessCGIServer.exe/wsdl/IBorlandChess
+IDutchservice  =  http://www.ebob42.com/cgi-bin/NumberToWordsInDutch.exe/wsdl/IDutch
+IEmailServiceservice  =  http://webservices.matlus.com/scripts/emailwebservice.dll/wsdl/IEmailService
+IHeadLineservice  =  http://www.ebob42.com/cgi-bin/DrBobsClinic.exe/wsdl/IHeadline
+IMapQuestservice  =  http://ww6.borland.com/webservices/MapQuest/MapQuest.exe/wsdl/IMapQuest
+IMsSessionBrokerServiceservice  =  http://webservices.matlus.com/scripts/sessionservice.dll/wsdl/IMsSessionBrokerService
+IODCODESPOSTAUXservice  =  http://www.e-naxos.com/scripts/enwscp.dll/wsdl/IODCODESPOSTAUX
+IPGPKeyServerservice  =  http://www.marotz.se/PGPKeyServer/PGPKeyServiceX.exe/wsdl/IPGPKeyServer
+IPrimeGeneratorservice  =  http://www.jusufdarmawan.com/wsprimegenerator.exe/wsdl/IPrimeGenerator
+IRomanservice  =  http://www.ebob42.com/cgi-bin/Romulan.exe/wsdl/IRoman
+ISMSServiceservice  =  http://sms.idws.com/soap/smsservice.dll/wsdl/ISMSService
+ISlashdotHeadlineProviderservice  =  http://www.marotz.se/scripts/SlashdotHeadlines.exe/wsdl/ISlashdotHeadlineProvider
+ISwedishZipInfoservice  =  http://www.marotz.se/scripts/zipinfo.exe/wsdl/ISwedishZipInfo
+ITempConverterservice  =  http://developerdays.com/cgi-bin/tempconverter.exe/wsdl/ITempConverter
+IWSMazeServerservice  =  http://www.culand.net/WebServices/bin/WSMaze_Server.dll/wsdl/IWSMazeServer
+IWagAddressServerSingleservice  =  http://62.212.78.36/cgi-bin/WagAddressServerSingle.exe/wsdl/IWagAddressServerSingle
+IWhoIsservice  =  http://webservices.matlus.com/scripts/whoiswebservice.dll/wsdl/IWhoIs
+Ieconomicservice  =  http://www.suiyi.com/soap/economic.dll/wsdl/Ieconomic
+IgetNumbersservice  =  http://reto.checkit.ch/Scripts/Lotto.dll/wsdl/IgetNumbers
+Iws_Verify_NRICservice  =  http://www.rightsecurity.biz/NRICWebServices/NRICWebServices.dll/wsdl/Iws_Verify_NRIC
+KRSS_DAML_Service  =  http://digilander.libero.it/mamo78/KRSS_DAML_Service.wsdl
+MBWSSoapService  =  http://www.extensio.com:8080/ExtensioInfoServer/mbsoap/MBWSSoapServices.wsdl
+SRS  =  http://xml.nig.ac.jp/wsdl/SRS.wsdl
+ServiceSMS  =  http://smsserver.dotnetisp.com/servicesms.asmx?WSDL
+TemperatureService  =  http://www.xmethods.net/sd/2001/TemperatureService.wsdl
+TxSearch  =  http://xml.nig.ac.jp/wsdl/TxSearch.wsdl
+UrduSOAP  =  http://www.apniurdu.com/SOAP/Urdu2.wsdl
+WSFindMP3  =  http://xmlrad.com/WSFindMP3Bin/WSFindMP3.dll/WSDL
+WSGenerator  =  http://xmlrad.com/WSGeneratorBin/WSGenerator.dll/WSDL
+WorldTimeService  =  http://ws.digiposs.com/WorldTime.jws?wsdl
+XEMBL  =  http://www.ebi.ac.uk/xembl/XEMBL.wsdl
+XMethodsFilesystemService  =  http://www.xmethods.net/sd/2001/XMethodsFilesystemService.wsdl
+YIM Service  =  http://www.scdi.org/~avernet/webservice/yim.wsdl
+YahooUserPingService  =  http://www.allesta.net:51110/webservices/wsdl/YahooUserPingService.xml
+convert  =  http://www.cosme.nu/services/convert.php?wsdl
+dns  =  http://www.cosme.nu/services/dns.php?wsdl
+eBayWatcherService  =  http://www.xmethods.net/sd/2001/EBayWatcherService.wsdl
+finnwords  =  http://www.nickhodge.com/nhodge/finnwords/finnwords.wsdl
+pop  =  http://www.cosme.nu/services/pop.php?wsdl
+
+
+#simple types 
+
+ABA  =  http://www.webservicex.net/aba.asmx?WSDL
+AmazonBox  =  http://www.xmlme.com/WSAmazonBox.asmx?WSDL
+AustralianPostCode  =  http://www.webservicex.net/AustralianPostCode.asmx?WSDL
+Autoloan  =  http://upload.eraserver.net/circle24/autoloan.asmx?wsdl
+BNPrice  =  http://www.abundanttech.com/webservices/bnprice/bnprice.wsdl
+BankCode  =  http://appserver.pepperzak.net/bankcode/BankCodeEJBHome/wsdl.jsp
+BarCode  =  http://www.webservicex.net/barcode.asmx?WSDL
+BibleWebservice  =  http://www.webservicex.net/BibleWebservice.asmx?wsdl
+Braille  =  http://www.webservicex.net/braille.asmx?WSDL
+CEqImage  =  http://www.quisque.com/fr/techno/eqimage/eqimage.asmx?WSDL
+CFRSearch  =  http://www.oakleaf.ws/cfrsearchws/cfrsearchws.asmx?wsdl
+CFRSect  =  http://www.oakleaf.ws/cfrsectws/cfrsectws.asmx?wsdl
+CFRToc  =  http://www.oakleaf.ws/cfrtocws/cfrtocws.asmx?wsdl
+CodeGenerator  =  http://www.esynaps.com/webservices/codegenerator.asmx?WSDL
+CreditCardValidator  =  http://www.richsolutions.com/RichPayments/RichCardValidator.asmx?WSDL
+CurrencyConvertor  =  http://www.webservicex.net/CurrencyConvertor.asmx?wsdl
+Currencyws  =  http://glkev.webs.innerhost.com/glkev_ws/Currencyws.asmx?WSDL
+DailyDilbert  =  http://www.esynaps.com/WebServices/DailyDiblert.asmx?WSDL
+DotnetDailyFact  =  http://www.xmlme.com/WSDailyNet.asmx?WSDL
+EMBLNucleotideSequenceWebService  =  http://www.webservicex.net/EMBLNucleotideSequenceWebService.asmx?wsdl
+ElectronicProductsFinder  =  http://www.xmlme.com/WSElectronics.asmx?WSDL
+EncryptionWS  =  http://test.mapfrepr.net/Encryption/Encryption.asmx?WSDL
+Fax  =  http://ws.acrosscommunications.com/Fax.asmx?WSDL
+FinanceService  =  http://www.webservicex.net/FinanceService.asmx?WSDL
+Fortune  =  http://adrianr.dyndns.org/Fortune/Fortune.wsdl
+GetCustomNews  =  http://www.xmlme.com/WSCustNews.asmx?WSDL
+GetLocalTime  =  http://services.develop.co.za/GetLocalTime.asmx?WSDL
+GlobalWeather  =  http://www.webservicex.net/globalweather.asmx?WSDL
+HCPCS  =  http://www.webservicex.net/hcpcs.asmx?WSDL
+IBANFunctions  =  http://www.bitounis.com/IBAN/IBANFuncs.asmx?WSDL
+ICD10  =  http://www.webservicex.net/icd10.asmx?WSDL
+ICD9  =  http://www.webservicex.net/icd9.asmx?WSDL
+ICD9Drug  =  http://www.webservicex.net/icd9drug.asmx?WSDL
+ICD9ToICD10  =  http://www.webservicex.net/icd9toicd10.asmx?WSDL
+ICQ  =  http://ws.acrosscommunications.com/ICQ.asmx?WSDL
+ISearchSwedishPersonservice  =  http://www.marotz.se/scripts/searchperson.exe/wsdl/ISearchSwedishPerson
+InstantMessageAlert  =  http://www.bindingpoint.com/ws/imalert/imalert.asmx?wsdl
+LocalTime  =  http://www.ripedev.com/webservices/LocalTime.asmx?WSDL
+MSProxy  =  http://www.esynaps.com/WebServices/MsProxy.asmx?WSDL
+MXChecker  =  http://beta2.eraserver.net/webservices/mxchecker/mxchecker.asmx?WSDL
+NAICS  =  http://www.webservicex.net/NAICS.asmx?wsdl
+NFLNews  =  http://www.esynaps.com/WebServices/NFLNews.asmx?WSDL
+NumPager  =  http://ws.acrosscommunications.com/NumPager.asmx?WSDL
+OTNNews  =  http://otn.oracle.com/ws/otnnews?WSDL
+Paracite  =  http://paracite.ecs.soton.ac.uk/paracite.wsdl
+Phone  =  http://ws.acrosscommunications.com/Phone.asmx?WSDL
+Puki  =  http://www.barnaland.is/dev/puki.asmx?WSDL
+QueryIP  =  http://ws.cdyne.com/whoisforip/queryip.asmx?wsdl
+Quotes  =  http://www.seshakiran.com/QuoteService/QuotesService.asmx?wsdl
+QuranVerse  =  http://aspnet.lamaan.com/webservices/QuranVerse.asmx?WSDL
+RSAFuncs  =  http://www.bitounis.com/RSAFunctions/RSAFuncs.asmx?WSDL
+RSStoHTML  =  http://www.webservicex.net/RssToHTML.asmx?WSDL
+#SMS  =  http://ws.acrosscommunications.com/SMS.asmx?WSDL
+#SMS_1  =  http://www.barnaland.is/dev/sms.asmx?WSDL
+SQLDataSoap  =  http://www.SoapClient.com/xml/SQLDataSoap.wsdl
+SecureXML  =  http://www.securexml.net/securexml/securexml.wsdl
+SendSMSWorld  =  http://www.webservicex.net/sendsmsworld.asmx?WSDL
+Shakespeare  =  http://www.xmlme.com/WSShakespeare.asmx?WSDL
+SportingGoodsFinder  =  http://www.xmlme.com/WSSportingGoods.asmx?WSDL
+StockQuote  =  http://www.webservicex.net/stockquote.asmx?WSDL
+StockQuotes  =  http://www.gama-system.com/webservices/stockquotes.asmx?wsdl
+TAP  =  http://ws.acrosscommunications.com/TAP.asmx?WSDL
+UDDIBusinessFinder  =  http://www.webservicex.net/UDDIBusinessFinder.asmx?WSDL
+UKLocation  =  http://www.webservicex.net/uklocation.asmx?WSDL
+UNSPSCConvert  =  http://www.codemechanisms.co.uk/WebServices/UNSPSC.asmx?WSDL
+USWeather  =  http://www.webservicex.net/usweather.asmx?WSDL
+ValidateEmail  =  http://www.webservicex.net/ValidateEmail.asmx?WSDL
+VideoGamesFinder  =  http://www.xmlme.com/WSVideoGames.asmx?WSDL
+WebChart  =  http://www.gxchart.com/webchart.wsdl
+WebSearchWS  =  http://www.esynaps.com/WebServices/SearchWS.asmx?WSDL
+WhoIS  =  http://ws.cdyne.com/whoisquery/whois.asmx?wsdl
+WhoIsService  =  http://www.esynaps.com/WebServices/WhoIsService.asmx?WSDL
+XmlDailyFact  =  http://www.xmlme.com/WSDailyXml.asmx?WSDL
+XmlTracking  =  http://www.baxglobal.com/xmltracking/xmltracking.asmx?wsdl
+XreOnline  =  http://www.codecube.net/services/xreonline.asmx?WSDL
+ZipCodesService  =  http://webservices.instantlogic.com/zipcodes.ils?wsdl
+airport  =  http://www.webservicex.net/airport.asmx?wsdl
+bork  =  http://www.x-ws.de/cgi-bin/bork/service.wsdl
+chat  =  http://www.x-ws.de/cgi-bin/eliza/chat.wsdl
+country  =  http://www.webservicex.net/country.asmx?wsdl
+eSynapsFeed  =  http://www.esynaps.com/WebServices/eSynapsFeed.asmx?WSDL
+eSynapsSerach  =  http://www.esynaps.com/WebServices/eSynapsSearch.asmx?WSDL
+engtoarabic  =  http://www.dl-me.com/etoaservice/engtoarabic.asmx?WSDL
+fWArticleService  =  http://www.framewerks.com/WebServices/fWArticleService/fwArticles.asmx?WSDL
+fax  =  http://www.webservicex.net/fax.asmx?wsdl
+foxcentral  =  http://www.foxcentral.net/foxcentral.wsdl
+iifws  =  http://www.inkostar.com/wsdl/iifws/iifws.wsdl
+imstatus  =  http://www.x-ws.de/cgi-bin/msn/imstatus.wsdl
+periodictable  =  http://www.webservicex.net/periodictable.asmx?wsdl
+piglatin  =  http://www.aspxpressway.com/maincontent/webservices/piglatin.asmx?wsdl
+unitext  =  http://www.dl-me.com/webservices/unitext.asmx?wsdl
+wwhelpservice  =  http://www.west-wind.com/wconnect/soap/wwhelpservice.wsdl
+xmlserver  =  http://xml.redcoal.net/SMSSOAP/xmlserver.wsdl
+
+# complex types
+
+AddFinderService  =  http://www.lixusnet.com/lixusnet/AddFinder.jws?wsdl
+AddressFinder  =  http://arcweb.esri.com/services/v2/AddressFinder.wsdl
+AddressLookup  =  http://ws.cdyne.com/psaddress/addresslookup.asmx?wsdl
+AmazonQuery  =  http://majordojo.com/amazon_query/amazon_query.wsdl
+AmazonSearch  =  http://soap.amazon.com/schemas/AmazonWebServices.wsdl
+BondService  =  http://www.financialwebservices.ltd.uk/axis/services/bond?wsdl
+BusinessNews  =  http://glkev.webs.innerhost.com/glkev_ws/businessnews.asmx?WSDL
+CarRentalQuotesService  =  http://wavendon.dsdata.co.uk/axis/services/CarRentalQuotes?wsdl
+CupScores  =  http://scores.serviceobjects.com/CupScores.asmx?WSDL
+DOTSAddressValidate  =  http://ws2.serviceobjects.net/av/AddressValidate.asmx?WSDL
+DOTSDomainSpy  =  http://ws2.serviceobjects.net/ds/domainspy.asmx?WSDL
+DOTSEmailValidate  =  http://ws2.serviceobjects.net/ev/EmailValidate.asmx?WSDL
+DOTSFastQuote  =  http://ws2.serviceobjects.net/sq/FastQuote.asmx?WSDL
+DOTSFastTax  =  http://ws2.serviceobjects.net/ft/FastTax.asmx?WSDL
+DOTSFastWeather  =  http://ws2.serviceobjects.net/fw/FastWeather.asmx?WSDL
+DOTSGeoCash  =  http://ws2.serviceobjects.net/gc/GeoCash.asmx?WSDL
+DOTSGeoPhone  =  http://ws2.serviceobjects.net/gp/GeoPhone.asmx?WSDL
+DOTSGeoPinPoint  =  http://ws2.serviceobjects.net/gpp/GeoPinPoint.asmx?WSDL
+DOTSLotteryNumbers  =  http://ws2.serviceobjects.net/ln/lotterynumbers.asmx?WSDL
+DOTSPackageTracking  =  http://ws2.serviceobjects.net/pt/PackTrack.asmx?WSDL
+DOTSPatentOffice  =  http://ws2.serviceobjects.net/uspo/USPatentOffice.asmx?WSDL
+DOTSPhoneAppend  =  http://ws2.serviceobjects.net/pa/phoneappend.asmx?wsdl
+DOTSShippingComparison  =  http://ws2.serviceobjects.net/pc/packcost.asmx?WSDL
+DOTSUPC  =  http://ws2.serviceobjects.net/upc/UPC.asmx?WSDL
+DOTSYellowPages  =  http://ws2.serviceobjects.net/yp/YellowPages.asmx?WSDL
+Dispenser  =  http://www.blackstoneonline.com/webservices/dispenser.xml
+DocConverterService  =  http://telecommerce.danet.de/axis/services/DocConverterServicePort?wsdl
+FOPService  =  http://live.capescience.com/wsdl/FOPService.wsdl
+FedRoutingDirectoryService  =  http://demo.soapam.com/services/FedEpayDirectory/FedEpayDirectoryService.wsdl
+GMChart  =  http://service.graphmagic.com/GMService/GraphMagic.asmx?wsdl
+GeoPlaces  =  http://www.codebump.com/services/placelookup.asmx?wsdl
+GlobalWeather  =  http://live.capescience.com/wsdl/GlobalWeather.wsdl
+GoogleSearch  =  http://api.google.com/GoogleSearch.wsdl
+HPcatalogService  =  http://www.lixusnet.com/lixusnet/HPcatalog.jws?wsdl
+HTMLeMail  =  http://www.framewerks.com/WebServices/HTMLeMail/HTMLeMail.asmx?WSDL
+HelpfulFunctions  =  http://www.framewerks.com/WebServices/helpfulfunctions/helpfulfunctions.asmx?WSDL
+HistoricalStockQuotes  =  http://glkev.webs.innerhost.com/glkev_ws/HistoricalStockQuotes.asmx?WSDL
+Horoscope  =  http://www.swanandmokashi.com/HomePage/WebServices/Horoscope.asmx?WSDL
+IACHSOAPservice  =  http://soap.achchex.com/exec/achsoap.dll/wsdl/IACHSOAP
+IP2Geo  =  http://ws.cdyne.com/ip2geo/ip2geo.asmx?wsdl
+ISoapFindMP3service  =  http://www.agnisoft.com/soap/mssoapmp3search.xml
+ITeeChartservice  =  http://www.berneda.com/scripts/TeeChartSOAP.exe/wsdl/ITeeChart
+IZPOP3service  =  http://www.zanetti-dev.com/scripts/zpop3ws.exe/wsdl/IZPOP3
+LookyBookService  =  http://www.winisp.net/cheeso/books/books.asmx?WSDL
+MailLocate  =  http://www.maillocate.com/soap/index.php?wsdl
+NavBarServer  =  http://ws.xara.com/navbar/navbar.wsdl
+Online Messenger Service  =  http://www.nims.nl/soap/oms.wsdl
+OnlineMessengerService  =  http://www.nims.nl/soap/oms2.wsdl
+Option_x0020_Pricing_x0020_Calculator  =  http://www.indobiz.com/OptionPricing.asmx?WSDL
+PersonLookup  =  http://www.barnaland.is/dev/personlookup.asmx?WSDL
+Phonebook  =  http://www.barnaland.is/dev/phonebook.asmx?WSDL
+PopulationWS  =  http://www.abundanttech.com/webservices/population/population.wsdl
+QueryInterfaceService  =  http://www.transactionalweb.com/SOAP/globalskilocator.wsdl
+QuizService  =  http://java.rus.uni-stuttgart.de/quiz/quiz.wsdl
+QuoteOfTheDay  =  http://www.swanandmokashi.com/HomePage/WebServices/QuoteOfTheDay.asmx?WSDL
+RateInfoClass  =  http://www.xeeinc.com/RateInformation/RateInfo.asmx?WSDL
+RateInfoClass_1  =  http://www.xeeinc.com/RateInformation/Rateinfo.asmx?WSDL
+RecipeService  =  http://icuisine.net/webservices/RecipeService.asmx?WSDL
+RenderServer3D  =  http://ws.xara.com/graphicrender/render3d.wsdl
+RichPayments  =  http://www.richsolutions.com/richpayments/richpay.asmx?WSDL
+SBGGetAirFareQuoteService  =  http://wavendon.dsdata.co.uk:8080/axis/services/SBGGetAirFareQuote?wsdl
+SMS  =  http://www.abctext.com/webservices/SMS.asmx?WSDL
+SalesRankNPrice  =  http://www.PerfectXML.NET/WebServices/SalesRankNPrice/BookService.asmx?WSDL
+SendSMS  =  http://www.webservicex.net/SendSMS.asmx?WSDL
+Server  =  http://addison.ra.cwru.edu/orc/calendar_copy/server.php?wsdl
+Service  =  http://www.ejse.com/WeatherService/Service.asmx?WSDL
+SpamKillerService  =  http://wavendon.dsdata.co.uk/axis/services/SpamKiller?wsdl
+StockQuotes  =  http://www.swanandmokashi.com/HomePage/WebServices/StockQuotes.asmx?WSDL
+TWSFissionDotNet  =  http://www.sidespace.com/ws/fission/fissiondotnet.php?wsdl
+TerraService  =  http://terraservice.net/TerraService.asmx?WSDL
+Transform  =  http://transform.dataconcert.com/transform.wsdl
+UPSTracking  =  http://glkev.webs.innerhost.com/glkev_ws/UPSTracking.asmx?WSDL
+URLjr_Library  =  http://urljr.com/soap
+WeatherFetcher  =  http://glkev.webs.innerhost.com/glkev_ws/WeatherFetcher.asmx?WSDL
+WeatherService  =  http://www.hkwizard.com/WeatherService.asmx?wsdl
+WebServiceOfTheDay  =  http://www.webserviceoftheday.com/ws/soap/wsotd.asmx?wsdl
+WeblogsSubscriber  =  http://soap.4s4c.com/weblogs/subscribe.wsdl
+WhoIs  =  http://ws2.serviceobjects.net/whi/WhoIs.asmx?WSDL
+WhoisDataService  =  http://wavendon.dsdata.co.uk/axis/services/WhoisData?wsdl
+WolframSearchService  =  http://webservices.wolfram.com/services/SearchServices/WolframSearch.wsdl
+XMethodsQuery  =  http://www.xmethods.net/wsdl/query.wsdl
+XigniteEdgar  =  http://www.xignite.com/xEdgar.asmx?WSDL
+XigniteNews  =  http://www.xignite.com/xnews.asmx?WSDL
+XigniteOptions  =  http://www.xignite.com/xoptions.asmx?WSDL
+XigniteQuotes  =  http://www.xignite.com/xquotes.asmx?WSDL
+XigniteRealTime  =  http://www.xignite.com/xrealtime.asmx?WSDL
+XigniteRetirement  =  http://www.xignite.com/xretirement.asmx?WSDL
+XigniteSecurity  =  http://www.xignite.com/xsecurity.asmx?WSDL
+XigniteSimulation  =  http://www.xignite.com/xsimulation.asmx?WSDL
+XigniteStatistics  =  http://www.xignite.com/xstatistics.asmx?WSDL
+XigniteSurvey  =  http://www.xignite.com/xSurvey.asmx?WSDL
+XigniteWorldNews  =  http://www.xignite.com/xworldnews.asmx?WSDL
+YourHost  =  http://www.esynaps.com/webservices/YourHostInfo.asmx?WSDL
+Zip2Geo  =  http://ws.cdyne.com/ziptogeo/zip2geo.asmx?wsdl
+ZipCode  =  http://www.ripedev.com/webservices/ZipCode.asmx?WSDL
+ZipCodeResolver  =  http://webservices.eraserver.net/zipcoderesolver/zipcoderesolver.asmx?WSDL
+ZipCodes  =  http://www.codebump.com/services/zipcodelookup.asmx?wsdl
+ZipcodeLookupService  =  http://www.winisp.net/cheeso/zips/ZipService.asmx?WSDL
+certServices  =  http://soapclient.com/xml/certService.wsdl
+check  =  http://ws.cdyne.com/SpellChecker/check.asmx?wsdl
+com.systinet.demo.freedb.FreeDBService  =  http://soap.systinet.net/demos/FreeDB/wsdl
+com.systinet.demo.ftp.FTPService  =  http://soap.systinet.net/demos/FTPService/wsdl
+com.systinet.demo.newsfeed.version1.NewsfeedService  =  http://soap.systinet.net/demos/Newsfeed/wsdl
+com.systinet.demo.rpmfind.RpmService  =  http://soap.systinet.net/demos/RpmFinder/wsdl
+com.systinet.demo.search.w3c.W3CSearchService  =  http://soap.systinet.net/demos/W3CSearch/wsdl
+com.systinet.demo.search.zvon.ZVONSearchService  =  http://soap.systinet.net/demos/ZVONSearch/wsdl
+dic2  =  http://www.dl-me.com/webservices/dic2.asmx?WSDL
+eSynapsMonitor  =  http://www.esynaps.com/WebServices/eSynapsMonitor.wsdl
+ev  =  http://ws.cdyne.com/emailverify/ev.asmx?wsdl
+getQuakeDataService  =  http://webservices.tei.or.th/getQuakeData.cfc?wsdl
+getSessionReport  =  http://sandbox.grandcentral.com/services/reports?WSDL
+pwspNoCentrbankCurRates  =  http://server1.pointwsp.net/ws/finance/currency.asmx?WSDL
+sekeywordService  =  http://www.aspiringgeek.com/cfc/keyword/sekeyword.cfc?wsdl
+threatService  =  http://www.boyzoid.com/threat.cfc?wsdl
+xmethods_gcd  =  http://samples.bowstreet.com/bowstreet5/webengine/xmethods/gcd/Action!getWSDL
+
+
+
+##########################################################################
+# SECTION  [reader_errors]  - 
+#      unable to load file
+##########################################################################
+[reader_errors] 
+
+BusinessFinder(UDDI)-WebService  =  http://www.esynaps.com/WebServices/BusinessList.asmx?WSDL
+ColdFusionTip-of-the-Day  =  http://www.forta.com/cf/tips/syndicate.cfc?wsdl
+ComputerDictionarySearch  =  http://dotnet.cyberthink.net/computerdictionary/computerdictionary.asmx?wsdl
+DynamicChartingofXMLData  =  http://webservices.isitedesign.com/ws/chartWS.cfc?wsdl
+EmailServices  =  http://soap.einsteinware.com/email/emailservices.asmx?WSDL
+ExpressionEvaluator  =  http://www.onepercentsoftware.com/axis/services/EvaluationService?wsdl
+FonttoGraphic  =  http://ws.cdyne.com/FontToGraphic/ftg.asmx?wsdl
+HolidayInformation  =  http://wsdl.wsdlfeeds.com/holidays.cfc?wsdl
+Html2Xml  =  http://www.dev1.eraserver.net/REFLECTIONIT/Html2xml.asmx?WSDL
+HuZip  =  http://www.c6.hu/ws/huzip.wsdl
+HuarananetPresstechnologynews  =  http://www22.brinkster.com/horaciovallejo/netpress1.asmx?wsdl
+InfosVille  =  http://www.dotnetisp.com/webservices/dotnetisp/ville.asmx?WSDL
+ItalianFiscalCode  =  http://www.pinellus.com/cfc/Cod_fiscale.cfc?wsdl
+LinearSystemsSolver  =  http://www.cs.fsu.edu/~engelen/lu.wsdl
+LiveScoreService  =  http://www.freshscore.com/service/FreshScoreLiveScores.asmx?WSDL
+LogFileParser  =  http://www.bitounis.com/W3CParser/LogFileParser.asmx?WSDL
+MP3.comMusicCharts  =  http://webservices.mp3.com/MP3Charts.wsdl
+MachNumberWebService  =  http://www.cgi101.com/~msmithso/wsdl/mach.wsdl
+MagicSquares  =  http://www.cs.fsu.edu/~engelen/magic.wsdl
+MysicSearchEngine  =  http://mysic.com/Webservices/MysicSearchEngine.asmx?WSDL
+NASCARWinstonCupStatistics  =  http://soap.einsteinware.com/nascar/nascardataservice.asmx?WSDL
+OpenDirectoryProject  =  http://wsdl.wsdlfeeds.com/odp.cfc?wsdl
+SchemaWebWebService  =  http://www.schemaweb.info/webservices/soap/SchemaWebSoap.asmx?wsdl
+SlashdotNewsFeed  =  http://webservices.isitedesign.com/ws/slashdotnews.cfc?wsdl
+SpamKiller  =  http://soap.prowizorka.com/spam/wsdl/ISpamCheck
+SpellCheck  =  http://www.worldwidedesktop.com/spellcheck/spellcheckservice.asmx?wsdl
+SpellChecker  =  http://wsdl.wsdlfeeds.com/spell.cfc?wsdl
+USAZipcodeInformation  =  http://www.webservicex.net/uszip.asmx?WSDL
+WebEvents  =  http://www.bitounis.com/WebEvents/events.asmx?WSDL
+WebRTF2HTML  =  http://www.infoaccelerator.net/cfc/rtf2html.cfc?WSDL
+cp2ville  =  http://www.dotnetisp.com/webservices/dotnetisp/codepostal.asmx?WSDL
+src2html  =  http://www.dotnetisp.com/webservices/dotnetisp/src2html.asmx?WSDL
diff --git a/wstools/tests/schema.tar.gz b/wstools/tests/schema.tar.gz
new file mode 100644 (file)
index 0000000..d6fe7db
Binary files /dev/null and b/wstools/tests/schema.tar.gz differ
diff --git a/wstools/tests/test_t1.py b/wstools/tests/test_t1.py
new file mode 100644 (file)
index 0000000..5c33899
--- /dev/null
@@ -0,0 +1,20 @@
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import unittest
+import test_wsdl
+import utils
+
+def makeTestSuite():
+    suite = unittest.TestSuite()
+    suite.addTest(test_wsdl.makeTestSuite("services_by_file"))
+    return suite
+
+def main():
+    loader = utils.MatchTestLoader(True, None, "makeTestSuite")
+    unittest.main(defaultTest="makeTestSuite", testLoader=loader)
+
+if __name__ == "__main__" : main()
+    
+
diff --git a/wstools/tests/test_wsdl.py b/wstools/tests/test_wsdl.py
new file mode 100644 (file)
index 0000000..5f617b9
--- /dev/null
@@ -0,0 +1,164 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+import sys, unittest
+import ConfigParser
+import os
+from wstools.Utility import DOM
+from wstools.WSDLTools import WSDLReader
+from wstools.TimeoutSocket import TimeoutError
+
+from wstools import tests
+cwd = os.path.dirname(tests.__file__)
+
+class WSDLToolsTestCase(unittest.TestCase):
+
+    def __init__(self, methodName='runTest'):
+        unittest.TestCase.__init__(self, methodName)
+
+    def setUp(self):
+        self.path = nameGenerator.next()
+        print self.path
+        sys.stdout.flush()
+
+    def __str__(self):
+        teststr = unittest.TestCase.__str__(self)
+        if hasattr(self, "path"):
+            return "%s: %s" % (teststr, self.path )
+        else:
+            return "%s" % (teststr)
+
+    def checkWSDLCollection(self, tag_name, component, key='name'):
+        if self.wsdl is None:
+            return
+        definition = self.wsdl.document.documentElement
+        version = DOM.WSDLUriToVersion(definition.namespaceURI)
+        nspname = DOM.GetWSDLUri(version)
+        for node in DOM.getElements(definition, tag_name, nspname):
+            name = DOM.getAttr(node, key)
+            comp = component[name]
+            self.failUnlessEqual(eval('comp.%s' %key), name)
+
+    def checkXSDCollection(self, tag_name, component, node, key='name'):
+        for cnode in DOM.getElements(node, tag_name):
+            name = DOM.getAttr(cnode, key)
+            component[name] 
+
+    def test_all(self):
+        try:
+            if self.path[:7] == 'http://':
+                self.wsdl = WSDLReader().loadFromURL(self.path)
+            else:
+                self.wsdl = WSDLReader().loadFromFile(self.path)
+
+        except TimeoutError:
+            print "connection timed out"
+            sys.stdout.flush()
+            return
+        except:
+            self.path = self.path + ": load failed, unable to start"
+            raise
+
+        try:
+            self.checkWSDLCollection('service', self.wsdl.services)
+        except:
+            self.path = self.path + ": wsdl.services"
+            raise
+
+        try:
+            self.checkWSDLCollection('message', self.wsdl.messages)
+        except:
+            self.path = self.path + ": wsdl.messages"
+            raise
+
+        try:
+            self.checkWSDLCollection('portType', self.wsdl.portTypes)
+        except:
+            self.path = self.path + ": wsdl.portTypes"
+            raise
+
+        try:
+            self.checkWSDLCollection('binding', self.wsdl.bindings)
+        except:
+            self.path = self.path + ": wsdl.bindings"
+            raise
+
+        try:
+            self.checkWSDLCollection('import', self.wsdl.imports, key='namespace')
+        except:
+            self.path = self.path + ": wsdl.imports"
+            raise
+
+        try:
+            for key in self.wsdl.types.keys(): 
+                schema = self.wsdl.types[key]
+                self.failUnlessEqual(key, schema.getTargetNamespace())
+
+            definition = self.wsdl.document.documentElement
+            version = DOM.WSDLUriToVersion(definition.namespaceURI)
+            nspname = DOM.GetWSDLUri(version)
+            for node in DOM.getElements(definition, 'types', nspname):
+                for snode in DOM.getElements(node, 'schema'):
+                    tns = DOM.findTargetNS(snode)
+                    schema = self.wsdl.types[tns]
+                    self.schemaAttributesDeclarations(schema, snode)
+                    self.schemaAttributeGroupDeclarations(schema, snode)
+                    self.schemaElementDeclarations(schema, snode)
+                    self.schemaTypeDefinitions(schema, snode)
+        except:
+            self.path = self.path + ": wsdl.types"
+            raise
+
+        if self.wsdl.extensions:
+            print 'No check for WSDLTools(%s) Extensions:' %(self.wsdl.name)
+            for ext in self.wsdl.extensions: print '\t', ext
+
+    def schemaAttributesDeclarations(self, schema, node):
+        self.checkXSDCollection('attribute', schema.attr_decl, node)
+
+    def schemaAttributeGroupDeclarations(self, schema, node):
+        self.checkXSDCollection('group', schema.attr_groups, node)
+
+    def schemaElementDeclarations(self, schema, node):
+        self.checkXSDCollection('element', schema.elements, node)
+
+    def schemaTypeDefinitions(self, schema, node):
+        self.checkXSDCollection('complexType', schema.types, node)
+        self.checkXSDCollection('simpleType', schema.types, node)
+
+
+def setUpOptions(section):
+    cp = ConfigParser.ConfigParser()
+    cp.read(cwd+'/config.txt')
+    if not cp.sections():
+        print 'fatal error:  configuration file config.txt not present'
+        sys.exit(0)
+    if not cp.has_section(section):
+        print '%s section not present in configuration file, exiting' % section
+        sys.exit(0)
+    return cp, len(cp.options(section))
+
+def getOption(cp, section):
+    for name, value in cp.items(section):
+        yield value
+    
+def makeTestSuite(section='services_by_file'):
+    global nameGenerator
+
+    cp, numTests = setUpOptions(section)
+    nameGenerator = getOption(cp, section)
+    suite = unittest.TestSuite()
+    for i in range(0, numTests):
+        suite.addTest(unittest.makeSuite(WSDLToolsTestCase, 'test_'))
+    return suite
+
+
+def main():
+    unittest.main(defaultTest="makeTestSuite")
+                  
+
+if __name__ == "__main__" : main()
diff --git a/wstools/tests/test_wstools.py b/wstools/tests/test_wstools.py
new file mode 100644 (file)
index 0000000..0e0f958
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+
+import unittest, tarfile, os, ConfigParser
+import test_wsdl
+
+
+SECTION='files'
+CONFIG_FILE = 'config.txt'
+
+def extractFiles(section, option):
+    config = ConfigParser.ConfigParser()
+    config.read(CONFIG_FILE)
+    archives = config.get(section, option)
+    archives = eval(archives)
+    for file in archives:
+        tar = tarfile.open(file)
+        if not os.access(tar.membernames[0], os.R_OK):
+            for i in tar.getnames(): 
+                tar.extract(i)
+
+def makeTestSuite():
+    suite = unittest.TestSuite()
+    suite.addTest(test_wsdl.makeTestSuite("services_by_file"))
+    return suite
+
+def main():
+    extractFiles(SECTION, 'archives')
+    unittest.main(defaultTest="makeTestSuite")
+
+if __name__ == "__main__" : main()
+    
+
diff --git a/wstools/tests/test_wstools_net.py b/wstools/tests/test_wstools_net.py
new file mode 100644 (file)
index 0000000..880cff3
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+############################################################################
+# Joshua R. Boverhof, David W. Robertson, LBNL
+# See LBNLCopyright for copyright notice!
+###########################################################################
+import unittest
+import test_wsdl
+
+def makeTestSuite():
+    suite = unittest.TestSuite()
+    suite.addTest(test_wsdl.makeTestSuite("services_by_http"))
+    return suite
+
+def main():
+    unittest.main(defaultTest="makeTestSuite")
+
+if __name__ == "__main__" : main()
+    
+
diff --git a/wstools/tests/xmethods.tar.gz b/wstools/tests/xmethods.tar.gz
new file mode 100644 (file)
index 0000000..4521a9a
Binary files /dev/null and b/wstools/tests/xmethods.tar.gz differ