wstools-0.4.3/0000755000076500000240000000000012136000550013660 5ustar sorinsstaff00000000000000wstools-0.4.3/CHANGES.txt0000644000076500000240000000110012133773257015503 0ustar sorinsstaff00000000000000CHANGELOG =========== 0.4.1 (2013-04-18) --------------------- - Added tox for testing and disabled the tests due to broken binary assets. 0.4 (2012-06-26) ---------------------- - Replaced print() with logging.debug() or warn() in order to allow users to change verbosity. - Added release.sh script which runs tests, pep8 and allow you to release only when these are passing. 0.3 (2011-02-21) ---------------------- - fix url - proper release 0.2 - (unreleased) ---------------------- - proper release 0.1 - (unreleased) ---------------------- - make wstools as an egg wstools-0.4.3/docs/0000755000076500000240000000000012136000550014610 5ustar sorinsstaff00000000000000wstools-0.4.3/docs/license.txt0000644000076500000240000000461712133745461017021 0ustar sorinsstaff00000000000000********* 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. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. (2) 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. (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory, U.S. Dept. of Energy nor the names of its contributors 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 COPYRIGHT OWNER 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. You are under no obligation whatsoever to provide any bug fixes, patches, or upgrades to the features, functionality or performance of the source code ("Enhancements") to anyone; however, if you choose to make your Enhancements available either publicly, or directly to Lawrence Berkeley National Laboratory, without imposing a separate written license agreement for such Enhancements, then you hereby grant the following license: a non-exclusive, royalty-free perpetual license to install, use, modify, prepare derivative works, incorporate into other computer software, distribute, and sublicense such enhancements or derivative works thereof, in binary and source code form. ********* wstools-0.4.3/MANIFEST.in0000644000076500000240000000016412133745461015435 0ustar sorinsstaff00000000000000include README.txt include CHANGES.txt recursive-include docs *.* recursive-include src *.txt *.py *.tar.gz README wstools-0.4.3/PKG-INFO0000644000076500000240000000402512136000550014756 0ustar sorinsstaff00000000000000Metadata-Version: 1.0 Name: wstools Version: 0.4.3 Summary: wstools Home-page: https://github.com/pycontribs/wstools.git Author: Gregory Warnes, kiorky, sorin Author-email: Gregory.R.Warnes@Pfizer.com, kiorky@cryptelium.net, sorin.sbarnea+os@gmail.com License: UNKNOWN Description: WSDL parsing services package for Web Services for Python. see https://github.com/pycontribs/wstools.git General ======== - Homepage: https://github.com/pycontribs/wstools - Mailing List: https://groups.google.com/forum/#!forum/pycontribs - Package: http://pypi.python.org/pypi/wstools/ - Docs (TBD): http://packages.python.org/wstools Credits ======== Companies --------- |makinacom|_ * `Planet Makina Corpus `_ * `Contact us `_ .. |makinacom| image:: http://depot.makina-corpus.org/public/logo.gif .. _makinacom: http://www.makina-corpus.com Authors ------------ - Makina Corpus Contributors ----------------- - Sorin Sbarnea CHANGELOG =========== 0.4.1 (2013-04-18) --------------------- - Added tox for testing and disabled the tests due to broken binary assets. 0.4 (2012-06-26) ---------------------- - Replaced print() with logging.debug() or warn() in order to allow users to change verbosity. - Added release.sh script which runs tests, pep8 and allow you to release only when these are passing. 0.3 (2011-02-21) ---------------------- - fix url - proper release 0.2 - (unreleased) ---------------------- - proper release 0.1 - (unreleased) ---------------------- - make wstools as an egg Platform: UNKNOWN wstools-0.4.3/README.txt0000644000076500000240000000121612133772537015400 0ustar sorinsstaff00000000000000General ======== - Homepage: https://github.com/pycontribs/wstools - Mailing List: https://groups.google.com/forum/#!forum/pycontribs - Package: http://pypi.python.org/pypi/wstools/ - Docs (TBD): http://packages.python.org/wstools Credits ======== Companies --------- |makinacom|_ * `Planet Makina Corpus `_ * `Contact us `_ .. |makinacom| image:: http://depot.makina-corpus.org/public/logo.gif .. _makinacom: http://www.makina-corpus.com Authors ------------ - Makina Corpus Contributors ----------------- - Sorin Sbarnea wstools-0.4.3/setup.cfg0000644000076500000240000000007312136000550015501 0ustar sorinsstaff00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 wstools-0.4.3/setup.py0000644000076500000240000000157512135774546015430 0ustar sorinsstaff00000000000000#!/usr/bin/env python import os import re from setuptools import setup, find_packages url = "https://github.com/pycontribs/wstools.git" def read(*rnames): return "\n" + open( os.path.join('.', *rnames) ).read() long_description = """WSDL parsing services package for Web Services for Python. see """ + url \ + read('README.txt')\ + read('CHANGES.txt')\ from src.wstools.version import __version__ setup( name="wstools", version=__version__, description="wstools", maintainer="Gregory Warnes, kiorky, sorin", maintainer_email="Gregory.R.Warnes@Pfizer.com, kiorky@cryptelium.net, sorin.sbarnea+os@gmail.com", url=url, long_description=long_description, packages=find_packages('src'), package_dir={'': 'src'}, include_package_data=True, install_requires=['docutils'], tests_require=['virtualenv>=1.8.4', 'pytest'], ) wstools-0.4.3/src/0000755000076500000240000000000012136000550014447 5ustar sorinsstaff00000000000000wstools-0.4.3/src/__init__.py0000644000076500000240000000000012134006404016550 0ustar sorinsstaff00000000000000wstools-0.4.3/src/wstools/0000755000076500000240000000000012136000550016161 5ustar sorinsstaff00000000000000wstools-0.4.3/src/wstools/__init__.py0000644000076500000240000000023012133745461020303 0ustar sorinsstaff00000000000000#! /usr/bin/env python """WSDL parsing services package for Web Services for Python.""" ident = "$Id$" import WSDLTools import XMLname import logging wstools-0.4.3/src/wstools/c14n.py0000755000076500000240000004005112133772265017323 0ustar sorinsstaff00000000000000#! /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." "Rich Salz" $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 is 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, "&", "&") s = string.replace(s, "<", "<") s = string.replace(s, ">", ">") s = string.replace(s, "\015", " ") 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('') 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('') 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, "&", "&") s = string.replace(s, "<", "<") s = string.replace(s, '"', '"') s = string.replace(s, '\011', ' ') s = string.replace(s, '\012', ' ') s = string.replace(s, '\015', ' ') 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 prefix not in ns_rendered and prefix not in ns_local: if not prefix in ns_unused_inherited: 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('' % 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() wstools-0.4.3/src/wstools/logging.py0000644000076500000240000002012112133745461020173 0ustar sorinsstaff00000000000000# 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 import 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 = {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: i in self, 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) wstools-0.4.3/src/wstools/MIMEAttachment.py0000644000076500000240000000646112133745461021320 0ustar sorinsstaff00000000000000#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 wstools-0.4.3/src/wstools/Namespaces.py0000755000076500000240000002164112133745461020637 0ustar sorinsstaff00000000000000# 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/' wstools-0.4.3/src/wstools/tests/0000755000076500000240000000000012136000550017323 5ustar sorinsstaff00000000000000wstools-0.4.3/src/wstools/tests/__init__.py0000644000076500000240000000002712133745461021451 0ustar sorinsstaff00000000000000#! /usr/bin/env python wstools-0.4.3/src/wstools/tests/config.txt0000644000076500000240000005622512133745461021361 0ustar sorinsstaff00000000000000############################################################################ # 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 wstools-0.4.3/src/wstools/tests/README0000644000076500000240000000334612133745461020227 0ustar sorinsstaff00000000000000Two 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 wstools-0.4.3/src/wstools/tests/schema.tar.gz0000644000076500000240000020641212133745461021735 0ustar sorinsstaff00000000000000‹üK?schema.tarìýÛ’I²(ŠñÈd2cÝ’L2½I¦\ÕÇNÏ,ÈÄ­ˆSÍÙl6»‡ÚÍËb±gÖÖ^ÛhY…¬*ìF5H€ÍZgé;ô2éô$ÓOÈôÇô$ÓƒLŠˆ¼ÅÅ="223gØ$€È¿¦×ÉMÜ{ÔæÓïû“шüÝïOÆCáïüyÔŸL¢h2‡“ñ£~Æ£GÁ¨U¨òg›nâu<:_}NÖ׫K¬é÷}Òlý/Vë¤5"¨·þ¤]8…Ãnýwñðë/ã«ä&Ynü’B½õ'߇£q4éÖ²þÕ??ݮ֛O›»ÛäéÕïélá0F?ì÷ÇÃ!²þQN"qý£A}ﳞ¯|ýOÿüåf©¥óÕòûãðiÿ8H–«Ù|yõýñ¯zrrüççGG§tí§³är¾œoHÓ4XÆ7É÷Çï®Òù›’XŽå!è½J6oIëô6¾ ¯\o6·Ó^ou•ÆO¯«ómútµ¾’IꊀºL§›ejÝIþ"ÚUÙY¶Ò§ä§tß²iƒÞ1ÞÁ—tV¾ÿûï¿?ý}À^‹úý°÷Ïo~9c]jÞ§™ ŸtP4Ì0®ÖóÙåj½½a}, ä§°AüÿÝÏg¯q_ x±ê6øÛÙ¿¼ú²I–”˜R¼{:BdÚy³)”¨›{Là¸N<’žf_³®ÈÇ'ôwÝ2ÌêÍ6MÖŸçÉñ&F§iAWdc‘·OÿáÉ“àý>Á“'Ï‹®5ÏÏd¢ÁY6™àõ åõ©®={¸®[‚zÎ «‹xÃøÒÓ§=òJæì?ÙQÄc~©ðûmÐ{^utÏëAŸä¬5/ÂN׃ÿi†%öŸSÂö¦Yûãïþ¸{ÖüؾE ¥ß7|r–ýqÏÛíÍy²V—ÅLƒ‹ÕÍí"ùPŒk—»øè²æP*ÌÏø ÌÄW †|ú;‡¼Œ"’¿o ËNxœ²’켿%ëïÅçx¾ˆÏIÑñ1›ô÷Çô…9YÝžU7?&·‹Õ]2kÖË‹‹ÍüscHbÛ^N{º²o9üs;®}büÛ:¾½MfÁ›$MÉ òu6p¢–‰QÄó|ù9^Ìgg›x“üo›ÆÓ„wL_Ëm¡ö $¿%‘wþÚËÕ’ˆ8•& É'8SÒ'=¨¦ÒBKÝAD€c$Y„¬À£’ô;´[¡1â¿ÄËÙ")̦•}%Ï‹ŸÈ `/~ȇ}~d3ßä¸!B¦~Þ½ü´Z?,¬Úι%Ì2æH¶×¤WÛ9˜mŽ×Yò5bV™µªmùˆ ØiPWÀßé»]þ¶\ý¾ÌV;cUY²Èkûrʲç¨ú5»ßÐè¨WÞŽîûÒNž\H«ME-B¡é&—Õãš0t“/öém¼.(Žü“ü½IÖD`Îi1£2N¶a«zÚË;gûU3’ÀjU¾-©Ÿ^~p6˜c&n¨£š†l2Y± 3’–ËÏ×rtù°2ÎXÏç]Ƭ1cà˜q˜³zDgm:ßÜÆÕͼàÍŽ‰w]²ïÆ’[q]4„BD¦zðäü\F³„IüX4®¤rµ¬;6zëXÔýL¢!( úÛ¸k—íé‹§%Píê}þ#Á>1fi.[¼]mæ—óL}¶Úްïó-õózµ½­Öru›¬YSÃ}¾¼Ý–C=„2&è9*ª¤VÛ¶»|×ãg¥Ø]¶k±½S¨]…Ñð-Ö{`}ç[C×7CµÚ΢ó|Šdù~$[Sa>„Ø9Ö®¢”ËŵXn@} ]sA‚^ø½Îꋇ¼AŒ8|:°Y%í…X%D(W ùݼJØñl}•°Î«C®è[=Lë€Ag*–D€¶0“.§¥ÁŽê‘Bñ ÓK•6|ÁŽSÚo$mVñTzÜ0†¼z3_¾»¸Ø®‰l¢mâ/æ6ÛM|>_Ì7wß³/ð!W3"¾ÐTº\¤hÃå|±à›)Ú¼âÁ¿>í‰rV¡X;:íÉîCÏîÛó©{ècöÿË·ÉSGç¿G&ÿ¿~È|þÿ¿h0wþ»x,ýÿNž®~ …+Õ±µÓçóVus>_f [Žš·Oüðž«þIÜ~)zfæpL²­÷ü4GˆòRt9eÀ”3•Þ}÷âýÙ/Çèm˜ôL'²-Ÿ_ÄÑ€VV½£úà.ýfý¬¢Um¸„t °žÉfyÐ;¤irYø4)äe³á…Y‰]>˜eÈëeLN˜¾ø×ùjÁ–mk5Ò›REg mk7ÕðÙ…·¶ãùáæQ€V–LÐì:¹Øü5^lu\AmfÕÿûx½™Ç‹Ÿâùb»Æû‡šÝÛ™Eï™oÞ%÷Ù1¯`^‡,ny–´ÁšÅ¿‡ºXcàî.„¥˜o=P—ê£×‘ؽ“˜j Æ;ç-Æ.Dxx +Èù³#¬¯Š°Ö™'ÈÇd}Cnô‡’K‚q/ô…vþ`ÈŒ›ÚÇùMòëòâ:^^iÎý ÷& ͲƒÏWÉúz0‹Ü®,ÑöÒª›ðÅ%é ¥ žõý`–þP÷waº:d³vü_®`½ýw0Іrþ¯h2ìì¿;ylí¿v鿜“ÁÔµýÖIúä3¥U³|[’M¹¤ sOºå’§^^µz™zºdYԖɲ€ã»K‘åê6Rd¹ï¿.÷UmøºÜW]î«.÷UùÖ¾dåÀ1Òå¾êr_u¹¯ö³]î«.÷ÿ°Ó Ë}Õå¾Òߣ¾’ÜWX¦«üe.¥OãÜW¦LWæ!s_i3]ÙŒêûÊ*Ó• ’­ò@i3]9ÌØ&”U¦+—[æ¾2dºr˜µ]î+ËLWV3/¸C³cå s_Y¦»*|R™•ÌWî+»tW†¡»ÜWÔ´w»¬WÚЮlÛIiQ–iølzV4zM,–7×BÒîÊf%UÊ2úÕ€ïØ¯ÊˆQ¿ÁˆÂ–e†S½p9¾3q eèÊh ûë ãœK¨"¯P ¦ª/²ä¬\B3ð Íp  9t³^Ø/Ù+Ûj„¦ÓŠ^å$F5Oy@Ôý6vÍ#VÂ2v„EY¿‰&S‘ÆÑ°€cR 3ȉ&«‘Æ'±äĉÀ ŒFÆ7„k Z¬{Ü„‘¤J£"ü{ì•3Ž£vø÷ØS˜? |ìÆ2Q°†Ó—ñŽ‚“ ÃåJØÜXk€7šbqslxx] ÛÈ+ѧoò¬[ÀiâñJðÆ^Á›LÕð<ù\â÷Jh&^¡¡üT‰æS*ïWÂãÆR1xžMè? 0>°„§‰Ì«ž{}¹eâ̬ë…"GʈWbSøL9‹Wà™2ÉÃ"‡ÊÈëþÛ9TF^·ùhâçLyÝf—‚‘×Í>zf³¹FÎÚ:8R{èC©¡1%s ußÛ- ¬—Kººüû’ý£f²äMö4“ý›HgHCckcÐD^æl õ:Ùv—é34á²V_Îã™?Êﻦh®6ë>TV: ›mƒ°Î~Ô]5Ûa“ë ÔÏ ŒÇó• °S7œTKöáP“eÚ`’©¦1ôGü£&‰©+ˆ\ÕQên€:Ê(­Mn7¡ÊÔI£ pâäñŽÈ­aòlšEðmaâ×xpÒ/†Cn '^M'¡¿“ùÄM)–­_d#ûž8›#¢1ZM? ï ÄÓäʧÏdŒŽPÓÄëmn2™j£å+”>B²„± €<±ÒU8_áÜBp1Š e¯ ÕD1 T„ Ñ“WãÜdÐ =9Þpp.5±2ÛMü˜ízeá¹`”CI(ƒ¹÷¹Óÿ%ת Dˆ%%€j`­!v£ŠÙK@X•ºèêDòS'þûK:sÃPÿa<ê+ñߣI¿‹ÿÞÅcÿÝE3vÑŒ]4cÍèLŒ]4cÍØE3úÁªíœ»hFo˜í¢»hFþé¢#šï+<„Éû¾Á5{øûÿ’Skõ<ŽÑïû“Ñ»ÿÓG¼ÿ‡ã¨> Fa@Ÿ¯üþ®?ÿ¡AíOúô?ýá`"éÂÑxÐévñ¸Ô³U±¨q‘ªX&2@Oc›þðj¾3T g¶)Á¦ÂÆìZGb*χ¯|¶Ûÿ®šßì1ÕÿÊü? “I·ÿwñØîÿšº_`{ {]«†Þ~,²\ \Žo6ëùùvCïÂ7?&Ì´øýñß·ñ‚t›.Q¶Ì…D¤Ýó2²8à ¹ðŸ‡ðJr¦b ü¸º_”8Ï9‘SÿVf¯”ºú×JAY½½ÙA}lQº4ôø¡N{’pn?îRqZ®fþJn#fMrÿ‰Òežº™zÌ×dõz „Ë Nî{ÿÝ÷ƒòÿÿr“~Šgñí&Ys&À+IÐÀÿ'ýq(ñÿÁxuü{ýßÿÝ›³}d,·–YP=,ƒÈ‹¢#7Šcúf®?Ù¤¹mMœ€øÛNºß£*£­³ñ>ÿm¥Ë.ê‡uÖ«›Û,ô©8êÆâ Iyw¢¢Ù:6TÂhÙa÷S|±Y­ï²3¬²7¦äØ[^¹˜«‰¨šXln•ûvuæ×ÀÎêVû"òŽqPìe†ºw„lZZóZ€¯SBnò~9­—«%á̄þ\Äizo”QÂã¼PFȉáy¡h¿™¼øëzq–áãžÖ«¹äØÙ“õ.WËŠä‹yË”GRF(Æe[v°4PY .S—˜*c¾=ÚùM‘ö ê]ôŸ­×}9cquœÕ­ë$4Ìauë<Ž™êgPîi·iTœW;—²™ã„TΣŸUI¹n³ªØ”vVe3ÇY©ûKž•ä}, Ü¥±Ñ/9JIÉ1²c|¹b~ìekŠk5 Ù : ¤=¦‡AÞ4V ȼStÀ To‘|RBÁT®ƒ¡X"À3ûkÓˆ]OMý“!P¯ÿ‰Âq8õ?ÃI§ÿÙÉãEÿÓ@+P·ðוñª§»øjô<»Àtƒìt9.§ÓåtºœN—³º@s£^‹Ýu9Íi˜ºUsS{¸.×ÜÔI—ƒhnjÎŨË1hnêÎʤËA475geÔå47æY™µ8@&_‚†æƒSwžmÚ7Éã VДoÀö³M즎¡MÜ`RyÐ4Ô01Øf™¶œôÓ“Øf‘®9q“ö‰&wƒ÷6²­ÉÄG˜ÊÊ6¥x͉›+ œ˜ 4I©"à™±¬€×œ®a¿¢¡×Faè'hè5£w5K ê˜zƒf`“ž$t®dà#ß{d—ï½Qá  T‡e¾÷v+ax«×á7ß{ä)ß»c‰ ,ŸùÞKi ™€"ùÞ#¯ù #ßùÞ#¯©Ã¢†ùÞ#¯  £ÆùÞ#¯) £ÆùÞ#¯²ÁÀ*ßûà~󽇖ùÞC¿ùÞCÛ|ï¡×ý¶”ï=ôºÍCOùÞS“bP5Ì÷zÝì¡U¾w÷¢ezq ÷w )Î=”mþrËKÞp’2“û¸ß™¿ ‰_~¿M‰_‡^ïMC‰_‡-$~Z%~îIâ×AÝį¿‰_µ¿¼²õA+‰_~¿¬¿ö#ñë fâ×ßįƒº‰_^5ƒV¿¼'~X%~Üwâ×.H­Ä&—ÿO=ë¬ü¿òì³®i LñßáHñÿ ûÿ×Nž{öÿê¥E™ðš~`„›J±~i¥E¢Ð<×Ðû>¼º§ñƒÊWéüÿŧÜië6ûPG#`ºÿG9ÿÛx2uòß.;ùïH@ui‚BŒ#\%`­¦sV PM¬°Õ³r@ Ú2+ UÚ•‘uº¦ØK€îkjÖ$\5W%|Ó…!{zjðÿùò77M°‘ÿäü¿£°‹ÿÝÍã“ÿùJùþ7æp]™óSJ՞΀oކs€¬¦:FúŽý“&Û?ÌÇJÿ+ný1ôü? é¿Eýo4ìò?ïä¹oûŸÄv] €õuÉ ~bj'‹å{9TCÏy3íL5ñ)KÈXpÑ_º¹£ÕMf«‹m^úe/S ´vþlêôw œ˜ TýH?Òâ{œÁÐ8õo‹œn™—Nüjvç„È,õóbN/Øq’w›yáøï¾è×2¼Ñ Êø$emœÒß ÚøŒum\h¥Ÿ ZöwðhË\̬\ôs j¼¡Ë^ÖŒZ¹Þ›FãœômFÜé §{›ÞÑ }Ô dôC¹b§ùù¢‘…À8x¢ÍHhœ 0Sh5ô¥ ´bj0Ä€?›Þ¡Ø< {0„Ϧ(Öè É»/¦ÄÍyá`r¯ ;v€'0‹Mót¥c®œTÝb˜¼ íþà‘*P¿6°Ú.úH”ûÚ9e¬£—µ/z{PK†9+‡DÞ×£áe-mö¬÷E‡º× íôdàí?YÙEÿcÔ«ÿLÚ…Ãñ¸ßÕÞÅ£®?ïç×°ðsþÔ«ÿLÖ?êItö¿<í׿é©úÏÅ~©Sÿ™󸫝ÿœáävMOÀÍpaÿÇý,ÿ‡á¸Ûÿ»x€ýO·{ÍTŠ(}$ìomÆûIÊòøæŸV뛦ϥ<"^Ì/çÉì˜Å­Ÿªi!Þ—CÿÈèýUÖK™Ã‹¼¤Ä^‹‘Ô·R| µÜ»(¡Çw|Úå퀭å’ 4σ̃›ùòÝÅÅvMÐLÿMü¥øVqßR7›ùM²ÚnŠžˆ”P½S_E2´¢³Ê‚áz"»x#°ŸÑ/²¾]+ÑüËü2¹¸»XÓ/_Ò/˜%·‹Õý¢¥²ü>Þ\{Xóøb3ÿLðùnyF¶·eþóÕj‘ÄK§N“åìv5_n<Àw/g Ä=Kò©2ãVÖÝbåØÙ:¹šXî<Àu™eáI$O$ñ‹Ž'Ó/8†#RbÞËšÚfçÌÖœÇi5· üåö¦0Œ~¦`„u½úpöúìã«·ùiªí>~xñöì5×ì´Ç—Á]‰€]mÿ& ¿xùñõ__éÁ}ýVle­ÊÖÕÅ´ãéó%a|äç÷ÞiÑã™OþQtZ“_^Wtÿ£ÏÝ[túÁyk7Ûi¯¨¯w¯òŸ*ÿÓóÌüæÖ›ÀxÿÆÒý2uú¿<;Ñÿ_Τ»vt¹ÖUûgP}åºÝþozï/Sþ‡ÉXÖÿM†ýîþ¿“Çóý?ßSòõ¿ ©'¬¡c7>5ìxVŰ œŠa ÄPN³”nÄyOËîÅìnbç’œ`‚´6”,ä6»±sROûNü™ˆ¥ñù"y“Üp׋77×ë$ž½þQ’žô/åià_®–›äËæ/q*Ë ú×s»Ê¯~©õZéøWï­õüj¾äeoz¥µš_½—ÈuY·j2ÂÄMƒÿ——#ÀÄÿ‡¡bÿ »ü¿»y|òÁ›ª¶Xó¶_°´¯Êʯ?ÜqÁVÿÌn¾*û-/‹*¯•¹¦_^O)ýÆçR{õå–&Ò¡^ÐB&Nã‘qÚã’‡ãÖW/gêìfýåVÕ*`m+³¹õý{»<_m—3²ÚVJ¯=¹ˆßÓ£÷ÿñs0ñÿÁ`$ËÿãIÇÿwòxåÿœ/M}ö¿,e~¯åWÓî‚>ˆŽb4Ý8F«å¦ñRÁùö[ò…;—£n§<ßn ƒà¦7e´{Éug\õÝü4Â^ÎkáVZÞÒJ4+y/ÒÊ ª@ÊBŒ¬{ØOÍêa<˜ÿ—/Ý}ŒõŸ²þw<:þ¿‹Ç'ÿÏ|«js~è5¿ŒZ’)Ù€õ¤ò¢¯@⸅d ½û EêŽÎÃóÿxF`Oâ?ûÃ.þsºþì¿\^Ë«Æf`Ãù?•ø°?ìâ¿vò¸ç}A鄞µ’¿ÆÅ[–¥ÀÄöFâ²k«áPöÈÓ­3ë„mžzýÐ;ûñ—<AxÕ]¶7õF4ÍvÅÓª©×É 奙Œ‡T›c¼LQ¼WÃËSÓP|ѾY'L¨q)wWâ½®4pï”×aD¤åšZÙ9%½ôõv3[ý¾¬ßàú\`m.Ùír=“oè“lÛäUÕ(°e‰*èГ*õ§É†fgùL•ŒàäQ€hˆ‹nõvC&|àÐ!!ƒëBEÃ-ïóhÆ#ø~íÕ¨n§½|ƒæVPe¥YBœ¢ì7{û6^—ÃÇkò÷†œWÇñg7¶’¬ò¾Ÿã£d3p¦D~8~É”yÙÉ®ýhêüê‡LR7# ·e»Íw >+žàœºGg!•zçÄšü _yÕŠeà&'UYGƒ‚¢¤Óyl–Ê©ªn/’€¸b÷di”°WDl(õìAöB…¬0œðÎ9È Ð8–Á—ˆ^T öÄÕ§:/Zc±”á~LJshAúFî)éJ¦¿þåUñ:#(}‚3±¡i¥²16-õòQë­ˆRÝŸÛ”Ø 'ÇÐôüëõ¢<ÜÇ|ÿon 0éÿG²ý7ê¢.þ{'íý?¿ ™®&† ‰ËeÊú ÕÝvºÛÎ~Ývî{s[<ÿw*ôˆ<&þß—ýÿiþ¿Îþ»“§©þ×£;xdoË5ãx.å›m c_€”нü­S)ÍÈÏ{šµT 2²ïÍU›a¼oŠíŸÆÿ–|CýÇñ|‘ÿ‡]ü×NžûàÿÎ5Ùëöw? ß±ÂcaQ’J;ªì;“¢Yó)¨|ÜEIGO¸Ø,êÁkèE³`¡$ªìz±5µBDjD{ì ÊÃCtGçÐÔ94uM;߀ƒ5Tÿý†áú`ºÃY…o ÁMFÃÃ7ŒÁ¦ak…o¨Á5g¥ßÀƒ5̳° Ü0›:Á°ƒeFÓ3¡åkÚ¢˜@~fÒfn‡TŽ^ Ý ¤Qß±&ê°ƒÃfw8~ÔáT¯ä(Ç·Óˆ”P  00GSX;¢S¡”`ü‚5ž¾$)h• ÃM¨%lc7ØPà&SÌš*Á†]KØ&^‰îdú&H±Nc¥-Á;ñ Þ³©j´• ‚¬º%4Ï|Bõ§€W´ðD}¯ð„SÀ&,ÁZKxB¯ðDSyLgfmgFŽ’A8Íí­ð2h‚ õ DÅpÈá1hrf)¨ ¦˜]YZ Üü\Bæv¬eB,ßІ4nG•‰4 q Ë´Å–õ-¯¶˜DFV¬Êz7[52õh0ý¿o“¨‰Ì£"&bÃ#Û&r&c¦Zݼ|Ôèõø%ŒŽ¢ ¾}¢±gu[L$¥ ^¦C(0¸„hØlÛŒÀ0â²÷Q[[¦0a{fmö²išÈlÀ¦9AÇÇvW©,zÖÊ®q“ÕЧouâ8 dõ¬ÿ%‘m-¶„éÇy_ƒä3‘EˆÆëEã¤Ë­Wº~æçfëõÎö§ª«€¼Á_‚R¬ðzãC›M:‹ÒöŽGkÖäΨ8M³Si Ë´Qq[cˆ`¡/aÈiQðÔ'ZÅ[¦Ä§ìz¹6¬Ògˆ2Ä$jž 0•E|µ®_ej Ìþ_YÙšaŠÿËù?ÉöìwõÿvòØÚÿÙÖ$”ŸfYóЧn’Høý«:ŽQ/ì3G€œ£¥x¿‚ƒ®la†½Üræ#¯ u†@½õú¨œ‹µ5Úçs@é³e^ŸŸ††TÙz#®6]µ÷iñæRaå{ò.ºÿƒ„çÿ‹ÕÕ­Óé{ŒúùŸGá0êò?ïâÖ?ÿÛ[(ƒÿßh…rþçaØùÿíä±=ÿ¿PÙØáàÊi©®—Yy@EbÜÓxò$ø¾áŠqŃè—ÕÕÛËËd}6ÿ×D8’Y{͹–­ ê… I‡ÉTú+P¦’½ÙÃzÏ~ èõ¸ÑžÍìg¯x1AXèÁÃñô¶Xųª„Ó‹BÜJw:X«3LãÏÉGš9`îk‚åÖ³Õyf*ߟ׫í-€ãl¡«EÌW–aB®@hc?Y/‰=V¦*qø] C^7;YurC$ŸûÙ:ô¿üé{æú\<ìdÈá×úÃÆüNÈ#~·@?ßë~ñ´ÃrlÿvÉ=±*²øm£ÿvIMµ´4ªbO=Z•1ÔCç6íí8XÎ ÞÔcÞ@_Ôƒ/÷péê“ÏÉ¢no0  Ëo‡wˆü¬1|ÑÝ[Æz>¡¿w9`¸WÈrš3ƒknŸ=F²ì[:ÃÕÍ6“ßþÑï“_”‹‹ôPgç ÷ ^³t4æ ×uKPï(OæÀ}¯ @Ÿš×È,í½L$TH-ÕÞ9È(‹Ñ | ~»sédü Ú9Èt2ƒÌ-Oç ³¦ÿÎA¦sA†îd:™½Ø%5Ô9ÈìkFç ³«ôÖ9Èt2ƒŒ%Õt2ƒLç Ó9Èt2ƒŒƒƒ ï.’}Y¦å߃]áñbhÆ=í[LÅ”ôuUº@ªzË.Š<–B{º\U{:{=—ùeÏ ±h#ÝžÓ”âçxA8=+íªZT9à®êEµRÁ±våÐOE÷ºR T£f%nå0h¬J Ú) dãï§ò× zQ«¡P±G=±¼¿šZ¥ñÐgØUBZrĆ VC´ä£È9ßWï0,Õï` ÄfP„àC9ZÔ&`ýC/'‹]UÛ¡\_>c†~K­ ‡vUm‡^™åpÔÎA3ô[>v8ösÒ +Çb`Mv“§Q’²ª‰D]þ÷]<~ò¿»Ü) 2ÐOÝläÂѨvd›¦íà‹U‚füýZIÜÑ^sÑ£ýùI”¯tÝ4ôhguÊà5HÇujAW]F{jËŒöâ— cérÛû€šöî=·½²aº\÷5áSUîC®ûà’Ý=x<ñ>í+Û½÷ÑÚ£K¾{Ã`å:Ö´Ý™îÆûVv•õ¢xHyߘ0ö<çýÞ,cºâÆë³‡YïÛ\Dìf(ïýýï=m–ýÉ|Ÿ;å¾Ö!徿ŸõqO~ï°8û’ý^EuãÄëàêR¾eæu±»šßg{´@w~sà7Ywi¯wIð»$øÖ¬¢K‚o’ºÄøXâ¸r+¬u¹òíQÖ¥ÏGÓç›×eÔwCY—d¿K²/C %ŽJdŸIöƒ=Ì²ß &¿iö-`i;Ͼ»I´oÈ®3í7ÂM©ömèe¹öíȶ…dûv·œmß}ÓúO·ß|Ó´—oߚѶp߉\½fÜo@·-¤Üo´0­äܯGÆ;Jºß„Ù¶‘u_§K»_¢6{å2 ?ª“ƒßWÚ}#ˆÅOÚ}|t2Ã_Ú}Ýøš›½¿´ûF4÷ä.í>1í] ¾ÃãçÅå•ëé%Ð]/_× tׇ–Ö‘äÍí øP\?x,‚¡ã`h9†r·”ÚS­VJ 3Æ}åˆØ|7¿¶Qϸ„_›— xêxÖhC•®5gÀl±Ú5sZÜÆêRÔZjoe&t ½ÓåpKÂgÐ>ÖÌÁg^Ÿ)õîqÖ³áR:Ô"Ð] ë”"°8²¼TÀ×M¡)@a¸UiVH‹ õ¬^”´Tá‰Þì7ºËõUÊv—û¨ëdd¯­ïDD7O«kµ gÉ=èMh¹|öé@a]¨]6P«õrLàôœ³#ë ºôwå1æJÑ¿.%ý€ƒ:’‚ÞP2‹èÛ[æ`É^Ô; ÖÀ ¸ïô‹ Žx ?eªµ[Cêõ'ò±C‰ º\=Ý“=ºü?çó%ÍÅ‘>m”ýÇ”ÿ' Ãa$çÿ F]þŸ]<~òÿ8¥…èôul>…Ë~“wb™¨~îçjR5/ËJÂðQ#Fïùi>s“gï^¼ÿ!û±pîç&;…kS€§E—éæŽòõÙêb›Ç9Pá’‚lξC§àÙ_a³±«öôã‹‹ ]¸øí™@Áäýbv«Ù]]<[êJŽKÜ‚vØË{Ìä}¯=]jo¼ùˆú[±Ò5kþ¼IÍᦋ¤ïþᬔ¤úÅ—KÖ»¬¦4lºXH¿`•0%°N¨>[)½²׈Øô jABƒ4”NtVÕœnLZeW€š •Ù€51Õr³Y¤âši4®6›Í(@5`¨ÖšMïh‡Í¸¡PlÐäûëèu¯é­ ŒƒW›µ ­ Œ„׎µ /î ¥)kµãÕ¢­ÐŽJ»Úˆj VðLJµÚôÕTúK¯ÞÇRê£6eYr‡ÏÚkžugl·C¸Û@½‡zßp,ýéG•ó@ïžíÜÛîG[¤ZÙ=°±Ç]q·ÂgM±‡ö| X¼g}æDÞ˜È‘Ž»Ur:‡°‚¤Í!¤ç°Nú*qø©/+w_’hY©¸éb=€µE«¨Ç+ß׊¢Å!ýoç¬ã°âe7›Âˆ<š»ÛE³ÛE«Οã¹èvß.Ý£{tþ_¹dS÷/ƒÿW? cÙÿ+ê‡ÿ×.žûôÿJ‹Ú¨îî_9Ëqò7óå&¸IÖõ«€"\5GàVD«Ÿ2ÊY«˜à]Äg2¥Oê@Vœñl¶NÒ”›\:ýbq½J7Ó“þIŸÍ¤X¶´Ç ù+ÿ²ãýöðüÿv½úr×ó?F¿?ìOF#ŒÿÓGäÿápLþ FþAQŸ¯œÿ«ëÏþûé2¾Ø¬Öw^dÃù?'ÒùEƒawþïâ±;ÿ¿9:eñ¼€ùŒ~žR‚z*|µOú:¥>³7È)½¨ `º¬ÓÞ¥’i½Ê³UëZ¥bk•TüæèÈJÁöžb¢Ìn–7/ßàr1!OÞÇ›k0%}1\OÄÅ:‰7ER³f½ø ô~¶!^-g·«yU`Àöíl}¡0ü4_äu3jvA Ølo_.â45½oE j¬…”¹‰uðSvªT± òlÝRµ„ÇA™”îÝz–¬Õ Ä” Ž8J(¤HcÊ#¦1ádÖƒz•ÃŒÕn¥Œ&ÀOÂú÷D”‘É­¤™ÖˆÎJ쑆"m“_1 ä*†äðÔš#åŒðY)4‡jEØPˆÎúöÂ%:§Î–°­QÆÂ«“­¹ GUæK¦Å9x–MºÆ±©^¤³ó™&|WÎg~ru%ë*™)³êUÓÒP£ù :´çšgO Ä7ié厃B´iéÎÕC›±5iåß–‹Ú/ˆ­–j0E´b [†88ÊçQÊ6+É$ýÔ9pN1 šˆŸB›F¿Í¿âÕjßø ·Wïÿ³íÍÍݧL àÇ`¸ÿGÃI_ÖÿOúÝý'õýŸ&{¥$ú"`P’.‰Xl®ãMp±Znâ95¬ˆ ·¹^ÍÒ§AðzÌSÊ@fÁåjMZ&i³‰—d32Âcb{)ŠÒþW—¬°wÈ«ì'J”Éú ñr~EIsÆà Ãý%¾øíîOÁ9á±›kªTfóÙò;rH’›`³ ~_­ æ— d:ØïqüBÁ"¹$ d/{úÍÑ“'ÙÆó¡ò`ÈÚ#½µòCxéj@¤«êtºŒy¾Æ¢êÅpÐ ´v}°?ˆUðêÃz, ˜òw«]ïÛ|Ïþç:ï úöŸQ8töŸ]<Ðú_¥ó]ÚÂp2í?aÜÉ»x¬å?Àäç³×å%è|µžÏˆø·½a‡QyMé¡<è‘ÿÓžyƒÈn ö£zö yú´GþOAbÿ1û†8@ª:ŠT ¡s)’ÊìõÒ?”Ô2îIvñÅo’gw¯ý! '‡wyðá®sHr·×Ä|¨ìÐ’ Ùé›Mö¿h,× Â.ÿÏNž=Òÿ61šÎ}°âFÿ¦Ah- !Žª©vY;`g><´GëÿQ0®ZôÿB9þ+ £IÇÿwñxóÿ‹;žR/#Þp‡ËûWBÜ‘•Ã-mð·³ɯ,dÒ`ÏR¨‘±[®¬4ØŸ]ìô&|}Ÿb¨+‹s÷¹øÚ‘Ñ :cÕh Ü ÉùpttJyRJÁ8Í ôDo4ácY¥ð¼ìw‘ÿ…ÜÈÞ݆%zN/Vt¤/,PŒ«®MÎnr{[’ƒ]ê‹…D¦Ü2wKÕEOîã´Ç–×ËûÔA[…žP÷ °mºŠüOœ¦ÑicÖ‘Û¬¹6å¥3å ]™½œ10_^%/xÕ•4ueâ§ù~%¬º—S1‘ÏÇlÅ_Ó«|^ žÍÿ”K!T†â1X(j_wPäûAFxÇîó®Cp)]ŒCEM&YÐh22™£Ó+(ð”IŽÔÒ%#Hèù3K3Á}ú3ácE,4ûâ/ñr¶HÈ«9Á³ïòŽìƒ ¢¯¶ëâEáûùò7àÛ̹â–i¨Ø¯ùÈ,3´úͫ妔ÿúCr5'ÔœÛÎ)òd=™È5O¥ C•Ýæ\áTNĵ®—m’JSƒÃa‰jÂYÀBstUœ@åòQEŸŒ[ãÈŒxáf¾|wq±]Šzþ)~ß.ÏWÛ%Mùµ+­²ß³/ VJçQø •“Ëž‚tgt~tfO 6 ½Ã¾å0R"‰6¹àæÊìœi>z±õòß‹Á?®nÚ6h• ‡õ}Ú÷+ÝÁÂ¥ô`ƒ™Ðû_ŽúOWt7Sšõ²ÿÿ`Ôïw÷¿]<Íî<«¿Wý_þ.#Ö=Òÿ‰›¨e ´ÇÕéQ-;à=vöŸfŠ@ÿErý‡aÔñÿ<þí?"°SîLˆ^£Áé>íçvœr¨·Kù_ e©9†!þk4ËöŸa¿‹ÿÝͳ?òm¹ß;oÆ7=ð‡Ã­^ª;G&Ýœ9l™'.ò N4Œèá–éIž\PÃåH!¿ºÁˆ’uxËôY>–¨Â+{Ö`°ÌÅŒ+ìOõ.™åðvþ›aß+”ávåÔAÇù{–P…^¡Š¦oâÍÅ5 L€±%ÿÌAy…d0ýÀHEùƒeà–á_sè:f­À2d7úÓ3¡%¼­GMˆXÝ×£PÙà#¯T:ŠÚÙá#7 00~¶øÈ˜Q°†Ó—ñ=“ Ã#ÔJØÜˆ>@M±`5™¡1m%l#¯D7ž²Ð¶¹p𠏼±Wð&S5&N‚ š+¡™x…æd „ÐIà€Av%ýé‹Ù ¦¿rjHBh«hÚ’dê•l=‰¥^¯XD&]-Éx›—´VO°kÞ°‚Í™”Aáaúë2-æMrsN¹á1—¤@CkN¦ðÊŒ(‰H.i*éWnß{=]­¯]E¬(vx€Ó#{9½š,pzÑñ±ãËëZ'­_n«‹ž]'Vg—óm¡^äeƒ³hPÆ<ŸwéŠ=!†‘X–-í¿€쿃PñÿŒ]ý<ûcÿm–ÿpj¼üŸ:¯ o @MšUP+S¶õ …]×K ÖÕƒIxò³÷(–W(ùžbµéY5¤]ÖÖûÊ ÔåÚì6„Î:ÙåÚìrmê›u¹6÷‘Yv®Mfèð°¬´Ÿ³¤úüÁ Ð ¤1[ØŒf²*C 6ãÊv`Å´°?é"»t·>Yd—î¶ãÏ]ºÛ.Ýíár°CKwÛ=ÒS7þ» ûϸ¯Ø&ý.þo'Ïþäÿíâÿ¾‚ø?¤òœUå-ăe0šþ¨6‡½XÎÞ$z­~U×u=+ÚìÆi´®sÝg×Ñf5~]E¡á¥[Ú>vq‚ÃÈ*NpØ„lOµ]œàÐ/e [rTs zÃÀyòTs#,¬±Ç8AGß>4¤a8ñ'èÝÃÏoœ w—¿Fq‚î΀ ¿ì7Žô6 Ç úŽt¶9PÚ 3Æ ††8AïÁ^ú8AïÁ]Þâ„tígPWÝ8ÁÑ ^œ cÈ;š¦`X3NÐ1¬jo!ÐÂ1¼]CR£±·qvÓ÷¾3šÔ ßiâÕIÝðï1ímP•ÛùŽr©û t· ߌíÂwMTTL,Ãw^ÔÁI;·¢Wê<ó¤jð*¯û • ^¥Õah¾ã,†9…ïxÊiRwîU¾HËú__Ò™ûÆü¿Jþ÷pÜÕÜÍckÿiXg©«¬t••´²Ú1áôóÅ‚?ùþøØPŒ…úOäšî{Ë Oíü¿ àMü8™Èù£agÿßÉã?þS47wùß»üï÷‘ÿŒpm®¿W‚»§G+ÿ7Šú¯=ÿ'Âþ`$Ëÿ£.þ7·ú¿÷öO¨ô£ýÅ;²×ÿ ³Hh5ó©\6rü ƒ–—À@µß–¼-»|Dj—½=¿õ.cŸ‚0.h„^RÕ~kN²bÙÙæ‹õbA–nv÷ê kSt ¼q—"£#ã.EF—"£K‘Q'²Koõ²ª.½UÀÞm‰¿;•¦V$[ôuФjÚç?Üý%^Î~Ž;¡ÃƒF›(ü0ÊΦ¥‘”VV»àíêÅlÆT²ñâCr™¬©;}ñ™Ô&Žgõž%õÆm:ÚÙöâ:? 4£(­ê÷~¶‰µŒo\çŠþ—8-\[Í逯vœ8¡ªëx=_ÜýºŒ‹¤kn)²ÌæTÞ×Ý9”6÷ÌõC\?'ë컟â‹Íj}T=q®‘¢fXFr.«_ó‰šùR+‚»æ;˜ úlÊyÝ7ñ—wÛ5AÀvy¾Ú.gÉì8 §Sñí3ò©4¹}Ìþ½(—KHþ¯OøŸS¿H¼š­Mé{\à}Fá¢?-Üœ¡wØ·ÜLž“£M.¸Á™})ÍG/œaóß‹Á?®nÚ6h• ‡õ}Ú©êæÅþZ)þ‹­kÏóýþ°?°úôã¿ÂÑ`ÔŒ<Ã>_yü¸þ›Õ­¯ÚOô1Õÿ «.Çÿ‘o»ø¿]<¶ñ_èy`εXoSA XÒ”KXŠÒYÝ q]´âÄÀ†ÂœÔn…P'öçõ—} D>qc&£@I]rAPÖ½Fí£H‰k?@€X]üÁb«[Ef«¼`¬À!cýpacj· v¤¸´ T¨äT.R9r LІœW»WŽ)Gù¿§Úô1ðÿ!ùŸÄÿû“pÔñÿ]<îñßv*%`þÎUsŠŸ$ Á\öÕ$²DòT4Yì±²ÿï þŽÐï—$Ôð”4ú=RçÃâ~ۺû£ÒPïù`\ Áû({¹aºÿ GŠÿ×8w÷¿]<î÷¿‘ø¼¢÷û ÚÅW¤<EÃ}Íë5 €ËÏ=ª0+?ˆ{T6ß÷¨ÂUáðïQÙL|ã§ð}i?—‹UÜ6†²¹4Àå¥í £T·»N鼡¹ëdý;Þu¤md(j0‘Èb"Q“‰D¶ù­¼ÛDJ‡-cÿŽ‘ÈÒêöIÔ_(… ÝãœèîqÐÕ{t-'Àß‘¹ûs1’~n¹Í غ¹AªÙÜ¢&s‹Ls“7¥anʳ›Û!^çj?ö¿Æ †ûß$)ö¿p4ìî»xîÙÿ£¶ßG]CWEÃ<”ýr³µ9™D=W ƒírûcë,p®³ÀíÈÙÛTq¿NgosÈ®BYFÙNýÛO¤Iy2!þ§–½LO ºÌ@rR“ƒr÷2}ÊÝå(Ïj¢uî×Þõh1è²Çó3ç0/‰\ *¿³ÖÞ—éºV™+ýÌyÞËQF~g¬µÍ²ìÒêG“Kë#ó° @ƒ.š]Ú8ˆUÐ 2ß>dG³€ºÍ7-éhÐzóµHSdãPqÅ£–2 ·ó{gØ ”$ýÏÙDñ›È!ÿϤ?éòÿìâAÖ½ÊØÞ§ü‹Fê@½þ/”üÃhÜùìäq×ÿ}ÈIäeF!õ•€2­ÕÖæX´ä‹5·¡´QÌ!³¶VX*¹§O{äÿ4S"û®’«•RRG嵕ypÿüúŒÇu0qÝý鱕ÜÀz"ÊÊ6¼RSÐ^)ª«Çº~$}˜mˆZIR–°B6º˜lJ5U1¬w+MŒ8Sã0’FbŒ°2ÀENÅG{œBLºë€›·9x¨†…çSÝùFÉxKü‰±ê¼÷z5­dÄö^¹ÆK>lÿ5kšTXó^¡Æ¦.L;u ÉÙ…ÝIãéÏyxëÐÈæ™”c`åë â£Bðð™UàFµÿågß®<°ß£–Š;–ÄJ/Œ<Õw¬éŒ‚5ðXرì3Z®b4ôWرÔ3ÙÈsQ`Ç‚Ïxãf% {P½n÷ᤡ€êu³­ ¸Wg6Ýõ6ëø¿ééÅ‹5q5K›ÔQ9†³UÚ^@5˜ŸèëË5* ¨Î}üÌP]α¼Bl“¾¿ÚrŽ3&ŸU5l÷~ËZ‚uÝtU-•uˆgT·¦¥cÅ6dµÆã6ÊÊ9ÖvÃHj<±!)÷r£^kŽûõJŽ›ÜT‚ k*{ÕÕŒ£VèÉñBŠs©±U­a÷*’î5 5 =ÔMMB³#~F[¸å/ Êd-¾IÉOý‹T·—ózAìÿªõí‹{ Sþ×ñp,Ùÿ£(œtöÿ]<;¨ÿSlÔúæQç`ÕŸiY8‡¼:î÷Ÿ™Î I°v¸Gaûl¶ëe×UÞàG ÇWñ°¶§{¥²¬V¯Ê³Å;(o“5.Þk6ºdlÁ,à–1&EoÖ0éŠöx|êûÕ? ü¿?îËüö;þ¿“gwü¿ó“Áýdîoý‘ýºb¹–3øF“‰¼ÿ']þ¯Ý<îþŸÀÕÉÝ”§7÷š`|/>œCë ü=¹3J5Ä€eЉ˜ÒÞ…wPeŒókP¤BÒ·U×ÍJöÀ´ÒFõË‘Š!2•’•i.‡§Æê°ìeͨ•ÉÍ4gœ³0£#@Æ6›ÞÑ }عlù&ê²çJÓuܼ3 ±ÿŠá¯…k Ž\À8¸Ó—ÍH¨[0îÂe5îc¥ñȲ⻪ïÄw+›Þ!W( {Ðcʦȵ èô€º¯sCqSòÍâä:wÐ<îð$£ÊW¨q?ëùÒ´›Ó‰o,£#=P¬ ûHë*m<½oÁ}íÁÒ{Í7q´€z¶K…{ÁÝ Eí³‹l ¯€B ·`®B¾é‚vú@iàÊkÛÚ]m?ô1؆ýñ@¶ÿô]þß<{”ÿÃÝîSôP׿#[•Ú{ Is\l=\œ½dç‘ÐØx$¬LñlíÛv2ƒ»‡³£˜Ô·Y—~NŽŠÚ<54£Xé¼½ÚÇ*ØkéWZxÕ!?÷×ZüU)¥©O>æPSrd¬)Ù¤vÌ|l®)éµìÌ`ÒRMI¯åh'~Jöx¯ÇÙ¨d÷º›Ò˜îU5›Õ”jJú)Ùc¨)é³dmMɉ]MI¿¥i‡'v5%Ý 8ØR}ZÇ‚›h}DOj r¢`…>kJ:–áÔÔáôWSÒ¹¬-RŠÓsMÉ&¥øTð† kJú.ÀÙ´¦¤ïŠ›MkJú®¹iq†º×ÕôSS2´¬)Ù¤ p¦D¶5%½îïá ¥CÅë6=Õ”ô]³YMIßu7mTç ݬ¦dd¨)é§T•¡¦d“zUÍ Fu 6Ùè@·AíÂ^÷ðxØJ¡7·VyYUysÞ¸ÍÊ”Ž eJ›ÖÁLLeJ½*!Æ'þÊ”:lÍJaðYÄv/Ý굦äè¤^MÉFµÃUÁ³š5%Ý눃KÕo…ո׾Åà´ª|ë^oÓ¹¦d¯)Ù÷pP‡šš’aƒ3ºFMII}Pµ¼P\ãlµ]_”K¢Ó|3My«%­ë?61ØÿFãI_ŽÿLºüo;yî/þ£¶™oìu-Ö°Ü™‰ïè”îë”qº®ºgWݳ«îy_Õ=ÉŸoÅ£Ó\”Éû•E%öêmL¸C>~6[¾ì ò#ù›ì®”™ñ{yÏžE) ﺚT½!@­ 2LE ncXÍE¥œzƒvPd¬’¾ǰšJv†Á· ³«EgFæFVöáЯª-ØÙ‡C¯ ¶pØŽ*?ô«b G~tù¡›kìÑ>ºiâPEA8ñg½jíÂÏöáЫÏDø¬™}8ôª—ŠúMíѳOØÔ>51‚ªðD6ú°È™Y7Ò¾B½ö}à×<ˆ Ú÷W;ð`àOû>p;Ö´Ú÷ÁІ4Îö]¯Ú÷2µi´ïQ9@%hXSûy5‹G£6´ï‘ãq“Tde0:ƒ¹‰ýÚûD“š>‘_GÖ2j}"¯âYô¬Váסu`åù=hÇóÛÖkmlç´æ×C`b鲿õÆqÒÎ-×+]?ósÅõzùûͼÕB¯WÐÊôú1}[Ø…™ý÷gÒNÍǘîíˆÉôÿ§=±³öÌÀÖöß+w°ÞþÛ…ÑH¶ÿŽ£Îþ»‹§¹ý·´\±ÇÕ|(öRß–¼oaY>zÌ çh† ÷cª†z~èfdqÎ ¡31w&æÎÄìfbVl‰ªÑÙÕÄlf³a.UW³å v2˜˜ cqYV˜˜-A&t$ËÁ°°«$ö* “LÄØ½2o+Î\ê¡ñ J5Ú1HŒ:H`ʲÇ`•§½ƒ¤¤¦ œÝ]Nº§õ¹ÿ±+@2+Sÿ¤ÙÞtó6øÿú“Hºÿ †ã¨»ÿíâ±»ÿ}#^1á>§¢§²Œ.QÕqð$¥—’º70ú²pOhK‘Wè “œêvœÉ[ºîé¼Hcí”Y†öb=´s§È©“á^Ó•ŸÛ+Þ¿ãEïÐòz'uðõ9{±ÙEù›#ÚK~Yþ¦’ò ÷åzÔVN¹(¾X¯ã»w—¹0öC¼¹¸Î.Œjø7_®H3r'áÇ~_SóüœeF Îcš|/'æ)AAuöV¼!/o7I°N.«WbúJ&–ÿ,£i²Í7•!þOÿ™¡” 'ÁÅO©§™“zƒ5àЈ¼ò†¬ M¼Ñ•"ã¦ð/¯äÉ*õ¢.OY¤ÑdÏ6ñf›6šò9³ÿ!jû1õ20¾ZsâÂ%™mÐ^±CvÈ—©êzÇlfø›ºTÕ­)gb×ìì½Wå> ô]µ¡z§aª["7› ãP±Ÿ=„Pµq† îf¾aûE³xçôw©ÆKÎé0­Ž®[»ºU3~¢8Ëå¶>‚ðÒÿ*;œjÜýóÞíú)æ†_ŽËíÜe ­¸x;Ô ©îÆ@Ly¡ƒ…Ûê0DÕ˜5•úB†ÌÌ °ª­:Dh„IÞPf°”m€AÆÙosB+3‰”—Sé\¤:Mt\•º\´i}{áR¯©8 l¨¿NmÍ#4K'ͧØ4©tq9Ë`¯qsQ¥gëËî\|â`í‰À9°xZ‘.­ØH·¨{|ýE8s·²{¼ÓÊG\·¸{¼aq«Úùç\®*É›j˜ò÷!A*0±¢#r*¬‰øÃ)±r´Ð/׫tÃ|&z9Ìi·’(À¹p”¿Ã¦-Úx¾éŒ<â©SÿÁÕdðÿ á@²ÿ GÃÎþ³“gë?\§v‚¾“¼ã’ïºÀ!(nZ@ÜSBm‡b ĽçÒ¬VyPIGŽÆ)Ô _´+;¼N-¬éIÿ¤/ZtJÙÑTK]%ŸÆÂÿ¯Ò¹Û?}Œü¿/óÿhÜùïæéøÿÎø?¿§:¶Ÿ³ý«ŽïßëcˆÿiÌûécâÿa8‘øHþÕñÿ]<÷—ÿÑ™Í;²vÏÜÜ'Ç™·aKLgÌ Þ=؃ðÿõŠ&{Z$@íÏú þ?ˆ"¹þçpuòÿNwþÿ!'¯q %á5 Õvd _³Î¨XÔêðÂD7 ­QRË"…Š¥ÊÍÇpâÚzƒå¨÷±Ò¯ E’¹lÙ,¬H d|¬ë§òO«ÕÓ(GìÑCÙ$õñ€´‰k0 0wiÐ…NⲕÝÑc¥ð€¶†îd¡æ—ŠhS© gSc<ÖÅŽÝçc¡ÿn8.c˜â¿Fùþ?è‡ýo'Ï>꥛z#°ÛÞ¢ÁO¬¨ÒTõKoä¢ÇªIW›ù«˜Ò§ëêlu±¥‡›‹ÿª®¦û©ÆAÊIßV]“q3G¥ÓÊ©á d^E‹9ù!^0 I>BîTÔæHÅY, ó—š•,‡§Æê°ìeͨU¾1Óh\f2›Q€$bÀPª1›ÞÑ }Ø•W÷MÔ\²‡IÓuRô{gbÿÃ_ ×@“êãà ømFBSä#áéô­FÂóÝCci²ã[ñ]5=Äwl÷6½CiéîÁìõ6ýCiæþÁlô÷un(ÉY}³8y€ŽÇ4;<ɨ |iAÜ/òz=HšvËîËèHëÂ>Òf ‡6ž>½ø}íÁ²0‰oâ(:þ h-Z,^àä¾(­?Ò>»Èú (ä¡p ì›.²4¢’„<ñEážùKÙúÿ4q5ùFCÙÿ0ž :ûÏ.þ?}œB‹CËñ={ˆ*{¨±«¨ ©â3*­ æ; £l*¿­ú’þOé?uâ]}@Mü8–ëŒýÎÿ'_ûÿQ×O£»dM“~Wä¾j‚ÔpõtrôD|åt â-'µX.:¾‹³ÕvÍc<äüæÿª.¡²§éï·c0þßï#ü@ÿ£ø&ÿßÅsÞÿëë7ÁøiH?üÏ=úþOÿ›??ùÿü×þ·ÿcû)—òÝSö¯Ùê&ž/v½ä %§Þ6]g2a/ÛæOOûOÞu)2B‰ÞñÐ=Ýó5<ƒ(ì{üÁÿòñë}ƒÒ=ÝÓ=ÝÓ=ÝÓ=ÝÓ=ÝÓ=;~6·ÿÕ£ÿùûDþü¯ÈŸÿ*ÿÓ=ÝÓ=ÝÓ=ÝÓ=ÝÓ=ÝÓ=ÝÓ=ÝÓ=ç‰gý¿þëGþã¿{ÄìÿTðÿøŸ=z4 þùóÿþŸ>zô'þÏäÏÿ‘üù/äϯäÏÿšüùŸ?ÿÃ7ý_ÉŸÿžüyIþüI?ÿOòçÿFþü_ÈŸù¿Ëúþçwß3ížîéžîéžîéžîéžîéžÖ)Yï£2ͯ˜ X)«ÎÔΪӳŸóÒóY/BZàGY$„šAØ4â1ÚB©knÞ ÿ¶Í{|…w§iDµvÌž„«‡Ù‚Ñ@ýåEù-8ƺ‚¦‹6š/7Å(El¡7y•Š Binþàüì B6ù½ë'´?®ìcØ%Ç䘳x÷¼±‚~ØŸŒFXüŸÿŽGQô(yƒ@ó|åñšõÏÿý‰~h}aŒÿœDrýòmÿ³‹Ç6þ³Ïãhªß^«QÀ9…”zGÜ)À3GÂRž\è%Ôùçx¾ ‘ñ¿.7óEqä° B¾9KfdÔ„æsÉc}W«ÕÌcW?­W7Í{Z­çW41Íj-ôõ—x9[Øõ ÈéfMÓ pïIÇûÿ­²L¢0h·òRñ ¬ü„Ú·(Ñ>ˆÇ–ÿ7)`ÈÿßÇrü礫ÿ´›Ç=þŸg¥MÎ÷tÿ\'.§IÃô.ˆxT7¬LIæÏáHâϽ:åZî4i¿¿DÜÜlüæß–¾´`ú¾¶L´]¶í.Ûö¡eÛö‘•ç_~ÒŸî!ã:ìd§òÐÓjKÝ>ˆEßuÎXŸí¤ŠÝÄ>Äİ>‹ð„à·FÁÀC–¯"A+¹óõŒÍKÊü=$ìCålû𦹵ÇVÿ×fþçp¢ä»úŸ;yö@ÿçšö™ëÃ6å³FíèUèœéPIòÌ«‘Ï*rx!ØYR vI¿Ž§¾ý¿þ `°ÿ ûÃP¶ÿ‡£~ÇÿwñÜ?ÿ¯Í÷ý$Rn–ày7~ ;KØ|tJ·vÊlܧGçéÑyzðžG§½|{’­ÆW… öBí5½i¿É°Ooc²yón3o\‰2ó~H;ò7¹™¦,}z/å¹~¼wìâ]kÀʳØ0ðÑ©”\60›ìÁv™ "Èûþþx™žatƒa.g¤•3cCfJ ~ÌgÒ˜"öÊAŸ54S:pc†ý©^aWo§Ý+€ ûNP˜áÖôéÀãÔ%X¡_°¢éËx [ˆ%ÈpCr [ä Ü`ŠÙ”%ØpÓs ÛÀ+Ñ §Ì=·Nc«.Ázo4UM×Tm»„fäšñ°tKà€¶ðž±Wx&SÀ2.ÁÚÎKx&^á9™BÈc:³j;³zV¨lº´¸¥½„ÌíXË„&¾A߆4nG•‰4@ _¥þT#¢•0ök]Qp §:­Ú-õ‡s³Ç#H ŸM?À¯Àè ›ÈDÀ6êcÃcÛÊ™„ÀmNµÆ&ù(Ò¦JE|{E‘ÍöŠÜÄšú$U˜B1V<5{áÍMd€¨†èøUy•j¢Q+Tå&ë $5¶")g¦ž“FIÓŸÄ–ýx%Ÿ<(B4^õa;—C¯t=òs3ô*³§ª×ˆ¼¿·’¯òúÄfG9‹¡¢oB¡‘y.—¿+kÛ1ÝϤÿÂ,¨SÞþN{bWºÖ]÷¨O}û_½Ú¯ô1ø Æ}9þk8ˆ†ýoûŸX•´‰ÙªoZ׆ÔÕ}}8fDqÞ^¢31v&ÆfbTŒyZ££c­KÉ&F°Š3$«j$\““vªÁ.vYT_‘¤î{-d«òßœ\™þ•Aî+Pýü?“°?êòÿìâÑ®¿ðÉÝÜäÿ…Eþwõ¿wò4’ÿ+âpò)Íռꥆ87´?pnÏ4q‡`„\Á«vgpKSñMÐ!¼ú½s  O þïì®çÿQØþOÅ…ŽÿïàÙþÿø€+“}Èêu²ícéº|—\›ëu|W>ì27ñ—wÛ5»<_m©šã8¸™/‹oÃâÀœ/7¯²—Ô„³‚ÞAÓEÀ39cŽö'“iR€ùÈ*–æSzIXðêÆa6ÖhGÞÜùÛ_¶ 6Ž4%ª±à\wZ˜,¹¸^‘•zÖ}Îi—äÂÞmW³WÌȨÓsŸY–êúç—1ß‹–æ½ró’9ÕΦÖÒê¥Eïòî~¢Ùfö½†y¯ ׸‡©µ´†Eïò¶6QLË/«Ý_å¬Ç&†¨d¼5£‡Š1¬â†^Xz2ÜÊ7£í| ¶ä0¥üUëYÉl¢æÄr µX±W&–¿j=1yïè&Åw‰êžf^adâ啃:`삼¼Ü´ååæÉ‡F,yòås ÀAÁûŒòrsîã¼&£¼|æxŽòò¼>kåå53ê7òòƒÐ0ÊË1Þ`c Ô[ äŽÉ€C¸~Iç ŽŠ€#xÑ» B}°Û  EHˆ Ánƒ&G·L6ðì6p;Ý3Áƒohìæì{oÜ!âO%áNÀ€¦;d4EåSÀåßשçÆ$iâÜv’¤s{ÀIøÎº× ¤úqn“ºqnÞcëÆ¹ùJnƒªÓ1f}Ÿ‘ɶqnc»8·&ÑJ*ùL,ãܼ^UNÚ¹{¥ëg~îÆ¾³Ç4 ts̃AZe‚p–Bgk‚ýA j½+Ù'œÂzÔzáýZÁ§Y¤¤Æ;bÇïQñ×=âãäÿU³¨Áÿw8¢>_¢ÿù»óÿÚų›úŸ?PçÔùuþ@Âl: ÎhSëü: ýïËè¾…¿GŽòÍ ¦úÏ‘’ÿcvõŸwóøŠÿàöYà”íËõ¾ôÒe9Ø0q枈bO®”¦{‹$ÙƒÒÿÞ\)í®R;žRw¥¬#鯋m,]w¥ì®”Ý•²»RvWÊû 1Qâ “F!&BD‚!à¤aˆ‰Å| FÕ4ÄÄ0+™i4 1±˜X±{›†˜&&ï$Ûü^€…ºn†/ƒC…¤w]V/˜xÕ|^8zæbˆhM@äg —Þs´¢9\ õVþ³¯ý©¡ÿrÕàÿ 'Åþ?Œ;ýß.žýÈÿ"'árLææQà5á—˜$É9ãÍ–•ãLôŧëÊDLáõ)â–%ð*:N7w òælu±Íïûëx™ÒhÑÁ0A§@jÂl¸ª ýøâ¢þJ}+ kÙVLf5»sÆr°MÉ+‹9£âËP–wxí Qô­ów”£l½aRêøÁ 3sWµr Ωñ†:,{Y3jåls"¶ ÆÁÃemFBZ‘ðàW«‘ðèTh,M,«Íh@Ô)0 ›jÓ;D tÆšÚô…ýƒ±£6ý£=ú°ŠlôÆÅŠ$ûBUÁƒ!ïk±…§W RjeCÕ²qà®Âää‡Ñ&U:)][°óºÎBôLAøn~ð˜¨lö«ÏàËÅT›Vð®(L@™8qK¹Aº¡>MÅÛ7ñ2¾¢6£8(+X]?§Á|I~ù<_o¶ñ" PÆË\ð"‰Ð÷ó-ªÓ€ûvúû–7w Èúþ˜o–©É\Ü+Õ ¬V .È x‡ ´\«Ý«c1Ó·Ügì7 èjã6fPÕê|DvòHò_RÄšø¼8Èÿ;ùºþ«í’Ü9?‰'ŒÓMÀ(ÿúrüùØÉÿ»xÜåÿ—ðç‚ÝE §µ§ò…@O{NW‚¼#UªÆ`È_(!hé2ô+œÔ>nÀžn³Àe”jkc¿%îpîä¦òIw¶­æ*­T/HÀO¾¼Ì’˜ N.lî›2WYjw7ê4S#æ-îAŽÞgú î+´ïQþ»Ù.6óÛÅ{ùO“ü7¢ÉÞäüo“~'ÿíâi,ÿ½ÉéC”ý¸§¹¨’ 1E„ïÎí„N$ûÜ µR&xœ]»¸«‰v¸ÀÑíÌOÒ&®ÉüLßm1_ëÙBr2[¯'ÅÎÉAÍÇ¡Vpöt¯TU¤ªWå‰â¤Ûs"^lê\¼×lt²iX34´·ÇãÐ3\ôÖ ¦b¯ÔÅHñžõè¶áÀ/f3·SJOÚÀRÒ³> T×µ]”ìYN'n3(©Ój ǹ(¤¬ìçœÈÜ&T¼ÕŽRöv°B.p›P¹[¬Æpœ²µ %ò$‰GÈ®­øfÑ]‚GÜÊ›Oi«l'«Èß’Æq ÀM¤ƒÞVàH,Ü:p`z¶§¤/€uàÀÔ…CïQ ´ýýÏÝ Èpÿ‹†ô7ñþM:ÿŸ<-ÝÿüÝ\v£÷×ÐýîLžÕósrÖÔÛàëp”á Û+‹ÙÚ+Áƒ?‰€þVži°Y„û,gñzVö\¾úÇjórBFõ9/Ìtºè¯Omþ;§ÿ0Öÿˆ"Ùþ?vùwó”þ—?2%cÁ év~’éHöK¼»ã]JBâ÷|¯PÃT1K‰æüÉõVÊ2Ég–x­ö°ÄpU7‡M|¾ÍµcÕ­²ÔAVALBHO}ÿˆ²ÿ7@+ÕâhÕu•ÍÑÊ«ì¾nÜÊš”æ¸åµ‡_7neµ;nú-¯ÒlËÊ;·f\óU¼âW%kïã#Éÿ¿'ñæ:Y{ ÿtðÿÇ“Îÿ{²þùßC?Ùc¸ÿ…¡âÿNºü»yÜïËH¤~æÇ‚ÆÒ¢rmÝ”5îQù+ù¥ÊÂò¾ ¤},vˆ¨¯¬‘Ÿ@¤÷\ÒNæ.µ’’2MœÔ´hÍ_¹„~2­Y…Y¥`ê²J[V;ã@÷ìÓcàÿîJ?î1Ùÿ¢~(ñÿ¨‹ÿßÑsüß=ç¯ó` ›Žá»gø-:WrûB¬;Ó–å¯L9¾«L¾i²ùßÏo_®fÍÒ å3ø–ë®Yê ‹­$ Â;·I.F¶ÁÇäæÖ ¾Š¾ ²ÜsuO{áüwúà“ýoØ—óÿGƒ¨;ÿwò4?ÿí¬}&b èpxßÑL×^¥NåDëDT”uPÁеh£R ð"7¦’jYbf‰Ôúp±­…·Ð£Þ1vdC¥.rär±Š½ÅŽ”§@ ÚÉPÎ_ÂÉ£4ž,+¶ýkÞ«¦:'ŒF‘Vè¨&dÆÔ¡zøÓ÷dŸ{[÷þ\àÔ‡ÐFŠÃ},#Àælq6¼ÌÍCݽº»qO,÷ÿÆ>À&ÿŸþXñÿ%Ÿºûÿ.ž{Öÿ:û—]Ô¸ŸúVøº{W0)¾À°Ú6€ËצÀ©Û¾G°ÀḸ–ÓøV”Ïš8ºr«Ü†««®{¨hÕ^EÊ#þËKN·ˆÌV~-B—ÅÌå /«X\|ùÌ´_ ̉¿ºÿ8úö§¦p÷Î#ÉÿB©_c8øÿLÂÎÿg'nýÓùò·O V ´Q(ˆþþNÂ|ÿŽÆ]ý·<î÷¿3B¿äÄQÿ (”v«{ùó¢ÑÌ>\Ã> ÌÕ¾2ˆuÐFSs°k°8W,Þ áÎÑ0ùáXÝÎ/²Â^®‘´CFÀ†4Z2×áöze“欭(Rtà×8¼NngnœØ‡3|–FÏ*àй$F59Åp#gБèl¨*"¥æTZÌO^¿FãÙÌYlóÀ’á‹?ÿ8-\²œlNX¦ý)FEqд_ËaƈeNQª( Ó^ßgÉbþY,:‰"cðlú£ÚÆÊà™ÛYg7Ýž§ëù9¬²L‡´FÖ†lè ¸h£j8dÉF ÆËÜpãi.&ÏóÍY‡³í-%ÿd&XÒJ•µ®qãØ'ŒFºûŒ°•*˜&>a:™~dâÒëåçx1q¥6¨`9ñ ˳)4¼4b;ÛGbéØЦšÃ¢1òÅøSÝYQ±ß¼/M6¹Ïþñ&F±Ži0×FGØd§ÁD;¼î“ð¤ºí ó™ºm3 ¬¨?}¯É=¦ãýu¾Z0rƒ {Ã͸íÕwƒ .œ¾Ùnâ Zذfl¡O¢‹¢é›ÕŒì\+àhKx‘WðSJ:Z¨Ô4¯Ð §¯—«õ:¹ÈrCCà-8xšH@*<£é{"ÉÏãÅOñ|±]ƒð-8x¼JHÑØæxœ™µöD¹œ/gVGJú“Ø9SÜxz¦„ò°Ø¡âu‡QK‡Š×m<)^·{8l&¹†^7{8²Ù\¡ó†6¯ß&éæc²¾!G'ýᇄô‹_!,áwKk²äÚq½Rô€\ÕÀç7ɯˋëxyo;m[F7úÆÄ’ÁІ¤ÎdlÐ~¤›õ «5I†MÌš Óäèf\ ‡Ñ‡×‹ú€^1ØQ@˜l>0LX32·kH¦ÆÅà;±" çËyMnóâr£»3O€ÍÎ^AnÏMîm*éD'Øð)E^uѳ6XMäx=ÓTߊ¤œÅ.ÑO§ô—3 ‰)…xmjf ÔïçËßÊÝê·Ø¤N{âX]N}\ü?¾¤³Zc˜ò¿ 'cµþ_Øùìâ±õÿðæpNÑ·³òwV~V&–·ñß÷îmþ¸ðÿ«š€ÆüÏ*ÿŸ »ü/;y<ùÿ‰NUN±+G†tR;ôCÌmïNX޲n…~}ú&v>…´±ß>…ŠwîeØÔ§Ð~(4Ç„8¤O¡aPiE=øÖ°^nhPóâªÍ³˜é0¢Òe:@ÉÃ*ë‚•çFC:èt+ %dxøJ.{ùß= „!þgЧÿåÿÁ°ßÉÿ»xö!þ§A%8CA N¡†¹ truòAH.…RJÓ@V SÄ+½õÜÂ‰Ò <›ÇÄ·¢TÒ$>[\û6B´ #عƒ^ò¾p u¾ œZúÙûšfÕå!>zßà²Äû›œœlÒ` ®H¦Ñ8§%«dª{”upB²é½|,²°ê‹„•»Êƒ ã:.òÞ˜ØïÁä×ÂPçu`ÜÑÝf$Ô w[· ÷+‡ÆÒx¡[ñTÕ_â©€W¹Mïû7Ð=è%nÓ?äÎ ôz}ß×™ ¸fûberÇ/;(^vˆÒ æíOÎAFx€´­wEÓËi}ï‹*JWf×Ҭøæ¨;3°$¸ëóþìÿÜ3¹½íŸ ð)áPw—ðÁ?öö÷2&ÿßhÉöúWgÿÙÁ³ö×*¼åö¤ÎðTÏùRiL¦®ö ¾(ä‘TR° …!3ó·XÀÔ5+Á^¥ëJа\dîèÂjFUE#žmý‰ÿ9cã³ô3{êç…ÃI—ÿu´þô? ܽ•Çàÿ†‘´þQEÝù¿“ÇýüI¨¤¡ß÷…Ú…½3/örm?ï‡èæùÍÕôô¦OCoïlàÒå›óuutüVÙ #ü›Ãs²y±˜Ç©Ô²l Ö›TZAµ'f’3Wë¡-ôÖ£”Gá -Т¸/ãèé5ïÊ~¼ŒSc^_ײ/gœÃóŽ1sSDÍï;nvµ+’/ÉÅv“¼\ÝÜÄËÙ=¬ÈE>ò~®ˆˆž{ÜÒ%Ûªhí®‘¥bK +Êw—<¬& @£äK97bˆ:Œi°%ÆÒdßñ4ì“R~JÖ¼7›0R®(Õeð‡fDµn«Ã|̉yè+“½ª5Ù+“UrK<—<¼ÙâVÇdÝq.±zY ²Ê&s—Î$‡Ñaáù*Ùg0ÌŠ]`¥°ªŸÉE'W¤¢œ_y€,~N€Ç#$èB¡N ’å (Ø æ&Bi{! ‘É!PA&b*Ì jƒŠË­0ba.`‡[d'׆Y+ÜA`kö° äºM¨Þ G÷Z§µ?°ÇBÿÛTýkÐÿFƒáD®ÿö»úÏ»yêÝôy¼Þ7Çv4¶û©¥í4²F¶ñ8F¶ÓÈvY Y§‘í4²FÖBI ë_U…’G¬^ÿê8´•FÖ¿6±Æd•cÛÏvYTÿÚ|ÜZK¬ž8ŽÔÐÈêô¯^F¯„ÃÛ€‘}†t±jŠ¥î‘Ö’·K*rì cR Ñ*rð.©Q?N©a)JZ_A_‹Ò±j†‚c5J÷j ”NÿY;•}Ü«‡BPû~*û ›TcR¡ ›Uöz­Ñ4Œl # ëÓh·R-M;-‚ªáÍ@MÓ†Œe¢ÕÌõJý°»â”ÃgVÅ)‡M6Põ¶oWœräuÇŒÂv¸ÍÈm'a5FF‘v3rÛl(XÅ)GŽE Pà†þŠS޼–<<§y-w87+N9òZQj4iZœr䵜íè¤iqÊ‘WyidUûyÔNñgÛâ”Ã]qÊa“B‡ê™2Z§zÝßÃQ;‡ÊÐë6Ž=‰°^·ûpÒP„õºÙ‡Våâ†í”‹³wp!  .”¾„×îSñ¿’«©¨âøD_TqÜ„åª?3U{½lNúþŠ*N\K€Óƒ/´Ù%ç˥߮ãaÍ®ã&‡@<£º%\Ç^ùþxÜF]űÛ)€‘ÔxbCRcgfïµNç¸_¯NçØo î1T“XW§sìUÍ3ŽZ¡'Ç›*Î¥Æ+’r–@Mª;Ývv.áëû:ÅCÄe¸*¹Ýì·­TÊ9aÖæ„-J¸LÿøJ?°ÇÂÿóKÍrŸÊcªÿäøÿþ$ìêîäÙAýÏÎ9°s육ß;çÀÎ9°s윱wÎ¥sà}ËIõAå÷roÊcªÿ*õŸIóA'ÿïâ¹ø/÷zoôíZ÷†õÝ€HÈ:i¨ýǽܛò˜ê¿õÇrþ¿þp2ìì?»xîÅþãZï¾l[ç 24y19u!"œR¬åÆÌBE 7©|7ñ©b>ª^ÏÊ´5,Ï–Wf« ³u,û?2ÿÏ1ü–€s¨ÿ6žtõßvò ëÿ–Ü2’BAØT0œÿ“Ñ`(çÿtþ»ylÏ Á µþœ²–Œ²jý1ä:ÎoòÃÓìkÖœ||B/“¯ç³â\#_Ërƒu((D™/ <–“°J ái*$@6"‹~¨#ê88çgaÊWl––zfÃóŒO]£´ti@C!"ªó² ŠÌSòTõ÷|½\&ë—‹8-ëO%ßÉSÑsλaΗU–ÔŽ&œ/ñ9u'Ú¬·Ÿm*ýqCžödMìwÛM-°_gm˜h–9È”.çÉb†Lß4‡Ó^™xò´ÇÓÓQIß7B¶<"7/ò°ÊãÒs7kÈÕ,™/ûü8¬ä™·óÁnø„xøp¥{t­W×I¼IDXë¾+ M´h¸Ù®—®3–ÒæÇûü[ypY×–ApÌ{Qç|I-›!¢ÄSOíª !!F[ÕW>y®œ†~NdÕƒ2ƒâ»õ,YgDe7MŽF²µ"Oz9š¦—Ë…O–ã‰Î‚ yF¤äI¡ÑSä+¹¾½pq“t£6šÂw¶hø> €Þ™b¶89β9Ö>jè(¤Ó)wYOÉLšL¯š JTzŽr@ÓÌuÚMö´WºN¢B§Õ¨ÇßaMÒŠfòo óÇ'Eí À'•CYPR–‰³UmwfÍDIy(vªL­¨¾±ñé½'ük® õ7aå” )Í ß#E€Vÿób½Žïš+ ñ?ý¨?ô?Ñ uúŸ]<;×ÿÄ”¦YýSG‰ÃO¶Þ¨Óþ´­ý©ò^”?Œç¨ˆÁî¦J´Š ¬ãËO2rZÐ áóR‡Ï{Ì_x¹ZnªøÓ5uF›3>8©°˜3€l6e$÷i¼!íÎ Ö‚urYµb$Éî»#÷ò3‡’ –ÿôŸ+dô¸¡‹I0Þ“BL&’¯D+V{Ú’jŒ—Å:ýØW£ã—QYPF§#kAË \Ef;ËNC¶sšÙS™WŠ©§ãY¡YI†òVs—†k+—„>[O&M塪Éì#éÿ6É ÛÛuâϰ¾ÿßx4uþ»x4ëÿ…Ø×«Yú‰ûÒ),Ààÿ7E²ÿÿh8wúß]<€þv÷ÿXAÁîQmUœÔót™lz鬧¾Ÿkµj½÷ké/ …² …µÒ\Ò;ûñ—<ò{µe3Ö(é—ùRUp‰*¤8Pï ”Rß )5g×ç•Q}áÛ{ä eÂŽN{…Ϻd3 -GÑË÷%tÕJ†Je§›9aYÉìãj/²)¦/W[j@¾M]½N_}‰/6Ç`¶óÌèèUNFà<3¶7ýÀ·Ì"ŸíøOÛd}'õì‚Dz¾o^/gɵ³ºH\ΰŽêv•ÍñãüVFžÓË]ü²ÜÄÇRoùrü(µ¼«³$´Î†ÜÙj{¾îöÜ>®õú].Pý žnonâõ©R9ðæ¯~qx+]Îoocmu¨$û|³Ð$d@ß» 9Pfgóuy™He”Á¼®¤£÷dogvÆÚ\ú—Pæî‡LÁå‚atkßãG3^m¨ÔtŒN"V0ODõ³mÂþëçŽØ2—<‚›½’EBAŒ4“Ú[aF{Œ6â½A]}Îx¹],þ:O~§)Wwmz›\ÌãÅ«üFÐ|“q*Þ2IPètƒR²ÍUf™p+_:ŸG nI“?å²/ùû6Y,HÙíO  Ú­¨­~N6/Y7ï ¨¨ý-…4÷Îv½°x‡×˜š“Ô÷PŒ:Ý%ãáóe\°QÃg9êζWWT\-=!äöz§ c „¨Ð9a¥Î¨7–¦˜ø»å]‡I¶ÇÐ;|¾#î…›øK~‰8¶{á’ÝŠ@EÅó;®3•ø²À§ý@‹5vÁš'õßYY½SƒfjÓg~úÞÞs0 Ê,©‰b” „ôqÎáh8¾e„£ …cö"C·Wêa‚¼0»dH‘›—'Žjx_àU쵞œ´€åˆ0w,`Õp·:ƒJ£|k„BÄ€'µ Í]"‘óüÃû— ¨çd@•ðÁ_>~|_ê¨DK9~QÝC92(±FE‡8'M²§tß¡¬1˾ç)8,€Å yɱ‡euö4 CÌ¡ë à–!’‡=ocÆÑ­ú@_{ÕaîÛ­÷@®7ýȧ¦bgÙ«åìvEjø0Ëd,1ŒŸD[ÍFã U¬ŠƒV϶*šÇ±S|Xu’Wyø”N‡ÿüµ?¹ÿ³E¶5F}ÿŸÁ(wþ?»xøõg9»/c¦VôhÊÿ?¢>ÿ‚ÿùÔÕÜÉãîÿÿSF(ùyõózµ½5&†Ê¼GçhÀœPóƒèŠŽŸÿ[3œ§Ø@~§èBåÑ•ˆ@‰@  >ã)ÐÏ.â³´µUðºU¼œ„”oÅN¬£å”•m$gî¯èè2¦>4Çö·«ÍÙö–¢4™ý3”lÔo¨Ã²—5£RUŠÝhUK«Qò%y± 4»{õežnRt ¼±ÕXh¿†>taœiRTÿ1ÞÄ®ä)õÒÑçÞÐç˘0¶˜¾ø×ùjÁÖmk5Ò›í&Î@4„¶µ‰œ¼—öcá­­F£7´²êý5‘#ÖÔLý×x±ÅIjfÕÿûx½™Ç‹Ÿâùb»Æû‡šÝOšQãØÊ:¶\fFÅëÆ…rJ¸ÉÙ4üm{ok¶Î².~LÖ7„‡Ð^\R³¨ãbÝØšrðS¿À_—™Ó>¾°úîmuɽfæAL»9°Õ|ÈrÂGv}½üLÎ yª­öˆãüu¾b¡ý•*ÏÙß *Šþ/×(|š&æ'ý£)ÿG(äÿbú¿þdØéÿvñ¸èÿtißQJiuFö2Í™hµ«e…W`¬ ʱ¬Ü¶Á—t–…²?ã¬Ym~¸ „‡>ë„ÓæÆY>Rö•{Êÿ´Zßü˜0™àûã¿o‰py9OfÞÙÇD³¥úÅÌϨ֩#PŠ·Ê¥VK!ç8 ‚Ö»‹‹íš`œÑMü¥ø´]ž¯¶KêÐñ¼‚óÌ1 ˜/IòÛo³ ‹Bè…úªZÞ•})äÆQæ$ ÖbV¹y&M¹ `žä&@† ™î/¸æL÷•ª!³Uí…3äù `å×ja žˆ9 g0™`Ð`T.Ÿ rÁ²½2ˆUåÚû槇öÈò¿·CŸ{ôçÿpÑ3_ÌÿEü¿“§æù_Î!ÇšáÔk&?@õŒ•ž•Yª—ØŽ:ÿÑÐÿÕ2O½ðäIÁ`Ó9eWÿdò áo„u&T3Á¹¤ùvIu;7ÉÍy²f]åQ¯—ìþÁ`Á,•c‘å(…+ª2.ßC5C™E¶p'i²ÜÞZ§Ï”ÙÓªËYg¼¯$P{\«Š¿Ì ºZ­f?­W7jÎ8EÅ\¡~]næ ÷.âÏñœÕŸ·ïGêˆù]ä½ý2¿dM߯©ˆ´™'iî•Qžˆb *§ÂÓ*­XÚ¦V2½•Ã\ç5y¾à»§=qvÅœå(òËdMgkŠo´ó²D|Nˆ%¾ ÉãÖÛ²’¶øÞ= L/‚£j{Ó$GYèn– iž²o~=ûœ“rÓ‚%fœà÷ùæš4JÉæZ$PÈöâz¾˜•­‹ U@ö¢÷GX¬ôiRÜòý ,­.ˆ¼B^P?®ÛõŠðÝ4–t°ˆ¿Øˆôå\*®Í^E)cA¼œ-Dªý ûJ Y…‹rm4<” ëׯaöäé$x˜jµú%ûÝD9¿áZU>ßP¶ÎbDø.#ŠäÕžÉ1ÕóÐUÅ-Ü{Ë£9‹4bÂþ‰Šüq$b@!‰jgÓÆ ¿ÏTÇ7†m5Ÿ©熻«Höfñ7½™a~W\Õ5ÀËg"3ê¹WÛpÎ# É,£‡!fÖqãqlqdýg¦/nÈäqs[ôYJfuŽ‘üf?úᎻ`3¡Y€š-D*Ê"Ròq“4î&M6†>Àü¾ØêsúãTY×ËøÆ`œ Ï4!ï.)“†ìB Ç¢ŽK?Wnù_}¹¥Á:L¹fÀ5§WSŽL€ŸH0ýtÔÑÎÓÊÒ†ÕÉ Ýx2²hã9loéö®±6íÍæ°Wwr°œM€Og#0Ö—Bijï¹Õ^1Íó«5S…ÎÙÖgš;Ç=ÄEdS{xkX:¥êÕè@‹µ#äÈe~5†7–™{ÉpEªÚè„õúB—"¹´©Ù¯€~Áë3™Ðz~EWÜ|¬Ù1.¨ú3 €-ÇE¼MtM‚ðJýr%â«n_’oëc¢ì¹T!Øžx—4ŒâJP+ªcг’FQ—†#¦d)±”ë²ËebóueHË÷ ªB€’.*ì¥Ìe›C.æ×mØ´Ú×õ^ʸv*$hê â—ª£*›ik^Ú³I®¿Øä•¦u¿|ÿÈØ~Ãüñ­ü,pDH9¿±Û™‚ûü"^cQTÜŒ_b­ «­o¦ƒFvq³Ç¿ŠU瘨ŠíË÷Oê74Üln¼LoÞ?è45Sÿ¨ül˜1òÂûŠË㦠Äí™XöÊÌöŠäf Dšf‹½Òx¶‚@ˆXêä Ó|‘:J\ÌuâÓüí%}t~ [TãË;¦?òp—ß̘ËójAÓZ«œ¸Ë^õ þˬ{gÉÆæÆYë¦u•®_}¹Xl)î,û÷¤Â.ÐãEKZY/ m€ñµ·Ð}Ì3Á0{âÊòφyaXòrõb6›g¶³ÒU!}Q¸­¨skñ‚Ö]ì~,±âŒ‹}ÇÀ:ɪ<€¢ØéGÃlÁæ;=ªµ<¦ýÓx¹:Û–ù!2’6’ø‚#Jí¨Ø×ôÏhjuHy‹µªƒ å½}ÅI®ØûKœ2”F³§¶²Sí!ïí+N6 Ú‰×óÅݯËg»ñv¦+¡áÍv0SÿRQ‚¦3'xæ[•èôvµ™_γ §àlµ]¢ä²PçÔhsnrŽ9«ÚÑM ´v\-ÀI†5–õÿdß¼¦^pDˆ+—a[„d!LpGñ¹£L™S}¯ êN«ˆù¦Ë#•}ڹóָÃctcuå*I¾Hp.‹ WÞÙmæWß—Díp¾ü­æå­PæßÎ×쮵©Õ-­*´å|å±ûz™nbÒü;×Z°?¯fúšWD‘^É*ÛQë,YÌÉ’Ûƒž /ýОŸP5ï<¥eM'×—|2I£k=flá:‹bÖ–ÀõÒRŸåÄL_ü›a 7eÌ<ö­hMJÚ€ÊÌú$Saև͉tô>^“ÏdT ‡WW¢7¡Ô£ŸŸe€øns.Xk+ó;– h—|ÇB—â9OÕë‘ÚÊîz„¼·SÍD± ZJ¡LólCC­°§âPÈ"îêo˜™^Ïoó±>låX;·0ÄË*“SͬÛÁÑ*=sópGÐñj¹Yßå˜0bAŠÿÒ‚kŒvaŸ1H«ß=®XÊ¥Íf}—²âr¾`7Ü"@¥x`'rÉIØ,¤:!ÑMÆ>åuç’UX¬º?$WsՙɫÖò›xsqýCùWßÎÉ5P )}ƒµ2ðRý{5©È°I­Xëˆ ‚­ä³x6ó :¤¶$ª‘ :´¼æ¶*úð¢&Xö(¨ùÀöÎo­ëäfõÙBƒ¸§°›lÎîíšýk!eæ›æe~!ù‰¹rnwh³òˆB/ÍúW‹š®¶Ë´pý|#JW*~Õ·5"ÃæýûÇc6—Û¢„âoÆCíï†l¿cÄþFúÑ8Gð…ûŸdÆX°Y~5N~cçóT\VÊ»P%P_Æ‹*^„Sæn3xB6ÖÆª—#ÅŽÝëë‹öR?WU¥ãÜý GM'wõ¡Ê ¢ëMN©¢ÜŽE}Ç §2Š„ÚÒ Š0§ëc@·ÂÕê,™É€RR™¾C§š«1{ƒjZgÉT÷´·3úÇì,mÑbÏÒ@Jð‹ô5_þlS@í>…ïÕÎÝ•W]À„JR>é;ÅõUJï ßDøm"S´DÙ1ç­ó.Û£·­ÿ˜Ÿ¹>RÀò?‡ƒ(’ó¿»ü»yÚ©ÿȤÂE SáEëBUkM5ȲH²M J®ÇZ%"‰´©-)–SåÒò.mÐSÂÇÄZ±v´f˜$Í€*q`DTIªH)L@«T])X‹P繋Ô\ꇘ–gZÿƒÑƧ5§«r. l¨ÿ;˜Drýß!ùÔñÿ]<îüŸßë¼Js§E€yFÆÓên+ã{¦Qq`† ÁtL±E/±öëF×jVìí«]õ+¬ÉÚU°V©¢ÏµéYQhB]«î¯æpå$ìXn¸ì £ï½¡ïë ØÕ¾~ØÙÕ¾îj_wµ¯ víkæ:æ¶^ôÕ[«}bànDЙ¡q:²ÍàE izÅj\É—Gn²GUž»ºò‡[㹫+ÿµqÔûvuåÏi^Wž·ÿÈ5b}Ù öÿÁ@©ÿ8žô»úÏ;yvYÿ.Xì³æ²umÈòÜO «WY¶ƒ _n—|Egjžk®X¤JQ&Èà‘…²`Õ—«åÛäŠlùÏ ‰¯h5$¹¥˜éCùyV@–AR@þw<œŸ¡ƒs’qŠÑ¥@?ìµfÚÛ7ßý‡WoŸK3€ÊO*E‚ËÆjÊâpJ=6'.,KjÄAÌ|䚈ºøÝ²tÞWË¥‘•]ÆUÔÏÛ—¹(Ü”6£KÕNsï=ŒhÏW«E/Ý)5s T†ÏöK•–ΦùÎBiºÔGʹpªq˜ëûŒMEOøERa:ÍŠ}Ë¡Zj»‹KU²1·ä`6¶e8‚ûÃHö~ótÍU ¨rÙ±u\Þ½:)©~µ¹¦Œ / iŠ2ބܕd徭sâÿ³ä\DÇgWPƒÿû&’ÿÏ€|ìä¿]<îþ?¼ÏïWRÇñ§¶¿ÏýTOR¤ hÅÜWEˆSÚ°4¸$š žŸO¯0×û‘ÊŠ´N²@Q¯õGȺb×é ÏãDŽñ´_å¸{M{S¤xÊD¸AÎFË.Ûüpa5\–ÓE/l0^v憋 HÊÑí”OŒ‘OH‰lœªª€ià¦!P¸D‚Rl° }Â2šBÃK#Ž\G¬åFì¡!¡j¡%¼“†MH[ÝJÃHÙPC¯Ô:´³¥†nô``ý쪡)£`ðú8d¸ÛF ›Ñ(pc´|îèQÂ6öJt]Q8gH ÞÄ+x'@á™]ž$%4'^¡yÕf‘À=OJxžyåß}¨zŠè©R2÷&2ˆ Ohu 83k;'ä(÷§¹ï|†ŒýŠcã°9<Æ^űq„Ö”Vw…)!s;Ör©o`CcgQËÍ!•Q4ý¿“Ψ‰ ’Îh€ ÒÈ«„:ê«ÉìWo±+at<îq’Ù‰¯íȯ†ÛMEÍÞAˆªÉ9Õ£*¯'ù褪r;ßQ’zfER·x=¿„’„îŦ0 œ)$ ÁX¡›Wu0içV4ðJ݃Oª¯òêàY3eÃÀ«´:ìÛl®¡³&:pš½ç§L_8-5}™‰§¨t hü²š|ná\ñjвèq)XºNü¯kJcþ‡0”ãÇQØéÿwñìGüo™ÿÁoøo­Ô»–Ó@ظ°ñ٤ܦ%x.e°C% Å[’å€h˜ü!ÏüP%~pöyëžê©ÁÿÛ²ÿN¡œÿœÿßNž½àÿØÉ l™ÍK¾€ÜÛN¦Yø¶ÖDðW/kÏò±+šW¹>ì·sC ½ê¦ÃÐÏ -ôª¶ #%ã®lsQ²;”xµ>†5+® ¢„Å«2ÚÜCg[¸m¢ Ì(=4yv8Ú6Q{ôÈèÛáh±Äí•혢}Û.ýX¢½›,yxø6YÚ(]ÚÑhZ:yŒÆVNþâVNÞÕà­ì,ÿŠp/[ËQUŽZû< ¾¸‰3ôçäáÝ8ì×ÉÃÑBŒÛ†9y¸[Ah†M<Æ^ôÆ£¦Nc¯²ÁxleÉwfÖœ<&#½“ÇįMl268yL¼ <“‰?'‰Û±¦uò˜œØÆÄYÀñêä1† â'q9ð:©éä1ö*ŽŸµaŽ;÷’²2Nü Á=¨wÿ‹Ùl7~ýd Î£Ÿ=úñœ>²PªIþÓ’‡¿!ù 1UPã¨É•µ$¨”ÄB%¿ñzºZqšýð&›„5½É&~#$&‡¤Ö›lâ•Lm_¿‘+5æ¤5¦­7Ù¨oçMÖÈSо„–ÞdîÞâ ?lékä¬Béé˜óïÜD±éè¼Þ¿¶Þd˜á0@|ÍLn‚Áò2ëž}} ñÿóåoÍ+A™üÿ¢h ùDãqÿ¿“ÇÎÿãÈ„2!WbÁ½>¬ùš³3 %_ÙãÏ~Ô6Þ~à)N€Ð2`€ЦÊ;ª[ŸÜd~|ßt§Å.™ÿgþJ~ÇÐòÿ0EcÉÿ/†ýŽÿïâq÷ÿc2¨èט÷Ký5òσºòã7õÜ$!Ôß©C8!ð¦Åy©x.ÒdY)»MøHó(cß6)£üЇ…wÄ%œì±JõÈ5ÕçÌo…ÿðäIðrusCnX,cbðäI¡©r®ñ—åY2#”‘”µ¬Ëm@òEvg{½dÛ“Ý®ƒYþ*— Ë소Ë÷Pg‘óÊ97Ï;«’Î)ç °äsW«Õì§õêF,a ¡KH{øu¹™/Ü»ˆ?Çs–ë0ï§œºE‡R|VÐ_æ—¬éû5½ÂoæIš'q*PVÁÁÒ²q*„ˆù {Æ·ràë¼&O|W͆™}-W€.ëÛkÊÙgoª©4ÅvA|N5›¼fû= LÌEUûüãuäµÂƒÕe°¹ž§lÆÁ›_Ï>çIL5èªX/¿Ï7פ¹z_-’¬m!ð_\ϳ²5a"¾è%⬜mÞ87ñðÛ¸8àò,jRÎcZ`s eæ6ñ.÷i(ä> uù!Kz*h§½rzG‚P/¬HË×ñr¶ ù/ì+Š˵ÑðW‚¿_?¼†YwPØŸExEq{´–=F'|«&haË«-R`ALT+­¦.o-Ú3Ç+|užMw^X Å·MFQ’ïÊôSq†Ÿ‰ôä7óJÀ–F-|¯Y 8‡°%ì+Ìâo*§ša~W¨¢5Àˇ+³ô¼Ê–IZ Î{„"™Òô0ÄÌÇxœ‹”a>N²ÎÏ3iK½oÈëä{s[ P u.q›RYßýpǽØM@˜[¢T”rÄ^fäã&iÜMšl >PL ºN ÜÈvœ/{mßìŒ{`¼#·%ÉûQ6¬Š¨àˆÅÔY¼úrKÕ~ÔУžÜ†µ¨^Ph€%‘3;ßf~ë£ÌæIæÇŒs&Êò80¡É¶·”4Y¬ö¦wØKeð1Ï&À§³-‹EC®°Ò\ß“­½„šIæH„½ÍÇ:s‡N÷Ö§Îûé>ôefsý VYtÔgØ«ÑUjGÈÁμB Çz,3<ö’ážWµÑ]"”™]¬çL|„.é¹6ø]+æË›Íj=¿¢´°jߨÅWVh+ԀÉÝ4• ÞŒ.b"èR­¿¿• ôP­fV@½$í<¦ÃR©ù1À£JÞ`²¸€c»¶DPµs™FȬÀƒªÓÈ×G¶VL9WÞjrTv}¤'Ã|-íLH—%]5ÙK‡XtÉÃB† Pûš£rT×N©á A\$­®¹6ó×¼´g³ÜWXPÞ©ƒ øåûÇÊpR…0¡:³ ÷mH9¿±Û)ƒ¼à ›†§ŽFYÖ_ÿÞàá ½†Ð€jtÀ Ú—ïÜàá2ð Tazóþ‘A§iƒƒJ;ÃÔ‘ö`'Ì¡vhÎ@¬»‰b¯ìÁ´o¡HyhÚ@H½iÚØ+§-ˆšˆ}X¾+ÐÐB^÷‰[¸N|Zx¡m¦Ãwœîãyl|yÇING‘®ƒIºZ|NÖöö‡î²W=hz3kóY²1Y¹Á·¯Òõ«/‹mÊòâ}(Ö¬kˆ(¢d$Ûê¡xlyÑWÖôÂ; À8)Xh¬æ™˜™½©åßr;Ù…¼°Ì{¹z1›Í3ÃkéA“¾(¼©4Hxkñ¦-Ö]ì¢ê¢Ç)ûŽŠu2›SQD/Ø}Z¦ 6ßé1¯åCíŸäËÕÙöâ:—/´„%·3ø‚#níèÚÎ6±A¹òk^+Ê{ûŠœ\ù—8-1 ¤ÚÜN‰¼·¯ÈÙ$4´-^Ïw¿.c ýÁt 5¼ÙŠê_ZJП«¶ýP©Æåy-›žDëE‚V[§ª³êÚÊDã@kÇe´Xc¥ý|ɲ¿a°\m憨ÈóˆWÜoüEî7Ó/UßË÷ÇÕÅB2ä‘Ê>í¢@ $h¢@0z²ºáɈ¥‹ g´¸ßåÝfÞ£¨Û·ZGµÿ,8¹þÕ1ùr;_ã 2@vž®×ÄåÕ1 ä;ãþõ2ÝĤù/…O9>_[ Zº~àÊ”¦ˆÄh„¥Ï’ÅœÜvżúÑ~S¤±%j÷yÿ_•º«éÎÍò%ù;—îz|ÝÂIÅ J[ Ä´Lÿ%¹æJ$¦‰,ÁüáÀrXß¿ ÈÎä}f؇ÖîΡž)ä¹åÞÇkò™À”Öãõn`x`ÁÂrxteµ â‘é›Ã§ ·Y ¬}\/u‹Y((óëß‹YÙÙÝ«/ót“š¯js»ëòÞN•2ÅšæË«ª` åµÂ‹s) ­wJe Óëùm>Ö‡­egôu¼“HJqõdÇŠú«p¬?Û9ªåa£MFïëG¯‚‚ ýÕr³¾ËñmÄ5œ†ÛVô@²Ï¤ÕoÈ5é"åž±÷ ±["ï.¨êÏËù‚)òàgÁ½˜BFÚù–Ú9ø7Ñ‚ÚÅ.øCD¦1ÀØÐÊž„Œyöš—Z!釂‚^ý};'÷ú,Ê:fÞ`Í ÇŒþ={Ò7É#®ã)æMƒu+Á¹J‹Ýèr,î'ñ¬ tgíŽÈì>â+bšÙ›Œìc-î[À$äk¤Q {߃öÌÛœœÃb4{ßB¢¿Ðä/‡x-žï<Ç .U^m,Ö7vÜšR¥C1äW7bÅæýûGM,çkç0 år7Îjÿ3¼Qª2¢¢F¢_¸ÿÙ®ÕÂÐt•úÆùÂoì|§yçG§=:Ÿ”fZþ$;@ÆOs%sQ¤I¡ÎF n§•j©™ËñœÏ@N-У噆ӧ;Y,Ú±5åúìG–bók è:W$\^;²U™{Ð(w' ì‘o;`\1£VOj‚˜,,Üïh£¶µ AåÇì±P„MÛ áº äÈjq,€ñe‚¡–ýñ™øeHòÈj‰í°/ {^_êÌj4R;´¾ú‚ÕÀHà¡ý°hiŠúë‘Z0ð*VCáuÚáŒ=­FÅÜ´ƒ›*vZ•é‡6—ä´]È¥_ç;¬è¦Ý:+¯ë‡ÓTÕ´ XÒ¨)›i5 *¤G¨MI";Üje Ž]†8¡$¨°ZÄD4TË £˜Z´l"£*AH0\j¨CM’TB0°y<› ÈbaÍ Çñe—úZ£*Nþ¦¹ÊNæ5ç¨8¿×wßvVð5׎úG»HR€3·~tóqM–ˆûKKAÎ@àþ<±y¡èG¤uø¦®Üv”ء諡ÃêCáB"€Ã]áõ7ßå°ô .š®ê†Ò«Vë(š9+WRÝä‹ 0Ú©ÿ¨ºI:ß/U‡K<´™L¹ ®=XÔ¢¨]¥n­¾ínî™–ÓY}ÁLM¸ÎC2üÔZ530—°9€Êœp ‘7–ejí€0™fL¤•¢µY¶|è/€’¶¿ÞPŠ Â )“4íõS-F­­iŬ‰¹Iu³Áè ¼®cÓLý¯QÁQ¹)ô¢¸?‹7Z¬Ô4œmDÇN»c°Ò%Y ¤h¯  ²Hç¸rê8ïKB¸ò_l"0/HË„–t†{  u"FxSD‰c5w•íÓ¹¡ã˜”tÐ1íZÍaÌ 9pó¨z0tó 7¨c@a†ö¬Q®A]ª1´k­Å-¯O î}ƒ”– X™“@°Ìk#Qƒº0sCÝ!šµ•ç4&Ñšˆ†ì™»ÇtkhÓ!ƒX… ‡Z$‚f@g˜! E—Éò&f¾ó€®tV蘻oÖ×\5»|!/bÎû¦ØØ›jWž>ÌކqB¡Aa‡Y0)z‘‚@UNæY“>îlŠõ°Å08aý{ç —™ ÷¯Ç|U›ü{+(ȃ P \Uw¤ÜÞ¢GW kGäÀlͳ]Q‡ÒÒ`@‰XlmG¬b—›D¼Á ¢¿5š áá‰ðÓÍüb7 uÓaɈ%9$Bȉÿ`X¯Œ †cŽDØ]*-d0À[R ‡JšâK¯åxÂJõøÞ5ªí.åå¥k´ØX.ÇéÐvÚ•~4ô 2ÓÛh ÅWŽƒìÌ)ò³X饤™¨t0jaøZ¯ÉÛ(ßê1÷¨cçt¨:ž ðàCº ¤É<'€™ù#©Ü4ýCf}ÍmL› ¼Žáö{ø¢l“^L¾&MõÐP²çÚ?æâáQi×ó8˪rh¯Sy­Žƒ‹ËÀz€÷wÅ9nÞ´6«…Òª€¼÷½Ð2[Å×a¬A}5L ÷îh{^  â¥!SÒÁ ߨþÜB2Ÿ‹­ª¾U{×Ê9ç`ãè‚£·«Áî4_ÇNÝ'³*ÓÖ»sÉȇR¨$å*Çr+<4 AÞ°•‹1Œ4ý­U“1Õív‰¤"¬ËAiFKÄŠƒ¥+D,:z·=õü´:¸¤’µ§Æ½›³"Ã9¡Û!rNQ“Dåš9íŠB(Ø0aVxwC–SȆ•—Œ¢ ˆ{ËìK5Ò/íû* ²5šÜL›óéð`C†u)ÐD|Ù£#A)?4e1ðÌzèÛ¦µQVH+7¨ëU¶¡‘7·Y·ìÉ7{`\&ŒØà®Ê6gƒ+PþÌ[9ãJCÛyãF˜²¡sÁ»»&Íóï‚B€°A@A¨Ê >r €ö†¨¸öwCÜáw·4úèC<ëÑÑ,=ò¡!_Èù!W{¯7^ÕáË R/¢%`(¬ãë a9€÷¬E"`kžlH6' 1 m«3 ûÛ‰nÀ›ödwÞÙfxhš“Öü tiu&x®SGIo–°Ò&ÿ¤Å£îñõdÅztÕØ>-9=̧ܭñS¾žÒu©?F?ì÷ÇÃá£>y&cñoò„Ñpô¨?™DÑd2'ãGýh0 }ÿÓUŸ-¡Ãu<:'¾¾^]bíL¿èsúç/7‹€LªÆ³zÚ'wÍåa(Ë+"”}üéÉÉñŸÉžäö °Ë°U&yf&`:Ò,ç÷õfs;í1 {J=#/WëíÍÓÕúª—ï8–iÙ2£Ëô)ù6]Å·¬)¥¾^ÞpÊÓiN¦yWçóe=>hÞ$-:£C˜‡¦ŸzÇÏOçÔý`° É køÆ)b;ç˜aΈ‘¸ÞóÓ|Jf”??e0•s7¢gŠwvöîÅû²VÇÆ‘é­ŽŒNqCÃJ©’•ÃL>KúÅâz•n¦'ý“>t\,|Ú£óìQ蟟_>þ}o’ü(ü?'–§Wn¼zLüŸ<"ÿ'ÑpÒñÿ]<îü_dL S0#Qú´? ¤3Þ̱|é÷ß—ØeÉSÓ^Ôïzäÿ´ÁßÎ~üåU‘5{¦ÔëøÝÏg²·•í±õ\|í>E²mW ¡öpöŠ„Ú:¿)–¹ðÂRµm½N& ^þç-¨î—£ü?Jòÿp8tüÿÿ:(“Á­ö×·æ_Î7JÆòÀ~Ô6®Æ[€xÊå@»òõ€ÃÑ{Ép à›îâðMwÄ´ðXé^ŒòÿP’ÿ£¨õ;þ¿‹§ýO'ÿï¿ü,Ÿ,ÿ^Þݽà= ÿÏßOWtA?­9' çSÀÈÿÇcYÿ;þ¿“Çÿc¾:Ý!p8‡¶†‚ë¸ÆíJðïBÎŒîPØç‡çÿŒDž~IgžÇ0ñÿI8ôÿ£ÁxÔñÿ]<¶ü?£“fZ€#W^$­ðy‘/Óî~°~ÈËaïŸßü‚¬cé…©ÎÍ“§|3ODûÓj}ócÂ<º¾?þû6^n˜ÌŽi¶wêÇ—±{•¡ãƒP‡úªXê®àãEÍÂŒ»¶Uu9 ˜Ð(wò+Ø)õë„#D¨ õI•Òß«Í5Í‹a[¨À5èãÍf=?ßnŠiæ‡Öq°e•÷n³” |šÎé´«z‚å rªóMróñ®*¼ÞÞíA/ŸöJ0xЖw/è$l”ŸöJŒç ”ÊVEö¿"ÿgIˆàŸe!hèúãþ?”ý¢Á êøÿ.ž†ú9ßM§õ¿­¿¸˜G¯ò—ÞPýbƒÝ¨÷;ÿ=<û¯  ºÆzþöGã‰lÿtþ?»yüèÿE/’ÞŸßusÿÌ©”©N±Ô–³§°KjxzR¦˜#À€SCg?†€)º.9/FL7w4`¶ºØÒ‹éw/S:Y-r^èïzMz–l°ªýøâB81TŒ|ËW?Ê¢ž ¨W³;;¤fÿ‚Hïk"ïÓs'ï'‹ŠòÐ_ÑQ¨©|Ô:qVʰìeͨUè”i4.ÈÊf ? 0”EƦw´CC\”±‡#1Ê•¿;ŠÜŠDK€ãàåBlFB‹€#áC¬FÂ+@ciê†Xíiµ´§š!6½C%A€îÁÊ!6ýCuA€þÁò!÷Å“ÊÚ n̨*¹~P\-ê /q_k†Vwq[C¬»[S}qˆè«¡Ü×ê*UÛÜVUîæÀVó!Ë ‡'¹â…Ž|±œ¼¿£ÒCå9…†c_#yý_Kæƒþ/"äø¿Q;ýß.“aߤˆ³0®×3ª› éG%Sx¡À*ò\>~,½oæ_’YžqœþH~½^Qû‰h‚mάýcfĶhÝ˺ïeýgxÓ/û]²Þ’Ïùlè¿Uc}19Ò}ŸSW«‹ 7ÎèNG+³N.sc½ˆq9<\ p*@„µ-ÉŒRœx¹\mò¬4 axÎ|þñzž2=f@þÎ,õÉ,8¿ :âjI'AN«W6« ^,V¿¥…= .׫›€ÙЫ£&¥-Ï“ ž‘.ŸVïþ+Cç.Âë›m ÿ*Î%ê®Bó›R}Ÿ;bñ~KîʤY² l&)铺l_nãÍuõkN±dc-fÅoÿ~™û:ÐÙ¥ŽKǸçòg‡®ó“é¹øÕ¡ã¤ w\üêÐqf@úÍD»e´ G´âž¾±kŸÈIö=a›r â¸TÎ2…î¥=öŒì…1 h'ÂÍ Âo8jå}Õ¬Ÿ‚¦vSºLÔéÆÎ3Ks,ªìþ±Êò /ÎÓó‰=*ŸE ñeÖOOéœ~u—ÓWO$0ÅûKg Ö™×K‘|ÂzH'låo¾}óñÝxõ~UðÛâÎøjç¸|±8Ÿ}UòR`Ïr-šnWhæ8âèr¾®ˆIy½0–Ú¼Ýw9 àPǾÑa®jà›Ï5ØU=´£§dy‚òï* l—s2fqòÄèÉÃ~2ž8YoæCG£Õ‡Áî¾ö𒹈¶CáäÜúÑ/tô]þ^«¥lß”ü˜óUþ'UÆsLX¼ÛÍ’Oµê팡[ôˆn›KíÒaR3>‡çò[¢ÅFM1ÍÝÓÉ'æ<Ãcy™<ù=¾{RÛÚæ:Ö'kÂö©3ŽÍ;éj1¿˜×{‡wùQÛ Š‡®og¾D Ú¬nÖL… j$Xçò(Œ×ÜaLÿ~™'éµÜ}63ÒЃՌL fÜÂŒj­=l,I‚fó“Éø¦â&ʰnŸã‡kξ1ÂymHçŽç°$ þ“W> â BÒîp`¼ìÔ¾°ùÛ|s]¨eR¤@}ÐW‘sу•Wè6ߤ’0ÔD-œ’óIEîC³²Û j;\•á£M¯D„FýÙ¨Ï —ˆsñK™ùpõ1ú¸!'}¬Oþ ¥LFùû¥J`¯®%¥•§€ì;sâ›´õ«¯2·:öhCMºPošP§S´}Ö.~¹»¹X¼Î²êßeà~Çðÿ0ÜwÁåÀ@ˆTTgßd¸+0‰ú±­S§é£þkñâf•n\•wc«éÄVbŒú²Ñ¯ˆÀ· ßÉšh xLˆ™~èkr3gþ乄Ì:¸Ë¥ê‹.ûRëÕÜ®W”2ò1«^Ä_Ê$7 0n° Z4þÛLVÅV3ŸhâFÌšÕ\dë—ó¥|íÊž‹²ÕÛ\ÇÍ追Úüw”‘$‹K¿+%ßdÈW5-¹ëHö&¡FU/4¥Û¤ÐOÒ­a>+® äÇ×?Š .æ5ª·Ü‚™„¡öã*/øjÏñÉÊfýÌ3¦”¥R¶À+в#“dAVÃÛfu,’Ï ;ø½÷";+H›ù:'zVÐEaŒ¾ÌQfFa„ªJ\<…) F¹u/Ÿ$gdŠQ¬Þo¯l#¯waLÇ[”‹ªiSÒL¯„¶„}ÊM4º|<˜­,àÙ‚tÇ=ÉGJ‚?¤IÂveñÍóìPùc#ìË8©Ä â8ÚÖ´º*2)›–èçª!¾ÙpÒN®ž$¢ÛKÖ¾Þ:¼¶›ÌRóŸ˜HWãv{¾ ")9ŸjaŸ•ÿF£î»ª÷¾Í}’ÑÓ¬™ Šh„2[™L”È*AªN<'ÉçB~¹äe7¨U• iT•†fœøÈ¥°KiÞµg)Š9•øg ™¥µ—vÁïùq9Ï.LÔý¹§y9׊q/eÛ^÷¤¹Š{Qd®’_Õdd*9m×¼^ëš}íÞkù™Ó r½s2 Ú¿r #C©JÈ–Bo=BÛŒ.s=ˆÀ¨m.Fà͈P… ¢}ÅÙ%×mPü‡}¸óYà2PuCÐ Ý#Û”Êd)ÿ†‰áæqeàL×,‹é.£H¢¼Bö0nÉu#J¼Ýqç3BqD\{Y*«ñAŸöò~þ-ûWGðoå×dÕ&Ûb…Ã"ÙéíÛ×´ËŒTÜ,@ Ô‰€¹V™ŽžæX^¾M®˜ý5ÙïW4¹ŒN83ߘ4—!é¼³•m„9‘> %+CQ=ÌRœjý狤v'¿âÔçØ3€k”ÿ‡91A]kÏñ ºË`0¥Åyý¡ùZøË©ýO¹¶„5„¾3ËÓ–œ¯6×¹mƒ5$,YS1Ç+¥ÌÌËÅ%ÝÒUë*ϺS¼>‰¾IÎ÷÷9½ØßØ×ßq§ÇwtéRfy²Ÿ<¢âjrWYÁž…)bky‹ÕG™È‚7(„T Å€Ë%aÉLA¥vŽRtùe~…êQSsòÈ­àÎAgy»§ñJ, ñXd1¹:#7gÉš]œýA%úIW>q%Îdºà¹@JΓ{@CÀ W`jH{ÂxðƒC.\Éö’*Ü®W×óóùF(´´åƒ ô-y×4î‚-‹°zJ:püÍ” ^密߿$³zoK#|‡ä”`ÂmÎÖÊ…î¡Íêöjxza»—`®xa3í&kÛà.· /-°¢Ò+¾f5^¨¶¢]û%à† ? å»ÛA) ?&‹J¸°ç¢VT׳¢NЕ.öº×~–5ñît“t†ÈYTm€ e Rm`B%ˆ¿Ï³‹x=ËôÐB#D¶ô¡ŒÆATÒÐU–§Ž`Þ¬fÉW0Q€íÇœ9^ ‰±Šma/Ù •Æ™‰÷|/ÉÅ„ü+½¦¾OdÓߨýày™Y×ä·òôéÓ¢qO>zØ—Rψ©:’Ô)©šjùf‚úW(oV[WK›ì½êYÂÛžÕÈ3š8 …\Tf›Ü1£b&!òüm¾XÐô_ÅBg~Ss¦¦>BRçóÙ,Y¢ë ®²­4r#Iç«Õ"‰—¸„w/R×Ù¾]m2§$2µùe läàâš03º•¡¤É²È…ö&¾£¢îÌkª£.)i²ÙP—T²Æ"d ® 1UPa}di;öb ѼÈl†[”ñ^rûÓEÊ»Þ÷+çYîm;)Þîj0KC¡ E9“A¿:|×j«YGÖÎ6^7ò¿ÑÊÊsýÙ,¼»jB•¹œ¤õŌèæ‘BIŠ-ÞBÚ°$*ŠEÉïa#ÓÌÅ¡:N¿ù7ê-*¸›!ÂhsGÛ­¿ÓjÙn„—‹ØîæÙªöƒ°Å{©tÁb×Pé»/¢Ðõ®ï(™ WU…sK„ðð+2׋HÜ Í×#LðçÁ@®Žðeó].¡§ BVEt¶3oã5yÐëˆè ÜQ@í,/Ÿ^ðë»mòí: ;ƒúU¶ b[eü(®k]^‰¹%ÖP3´æ†îk­w²Xæ\`úâW-ò|@û·÷,f$59>´;PÑÙ#w 4çþGÑvix‘@ØÑíÉjŒzéÅɪt-a³óÑpwøÇ:´qù§ÿÜÌ7[úÖŸª€-æ:c€ÆôUa–ª8œ  é#k+ll¾c_Òh~LÕøŒCŒ¢èà—£Qtm•mk~MåGÕ" c)û¶B VQy˱%Eæ€ëUá:‚¨ç­cÛ ÎRüMƒâκUfA2§±ª´¥Þ¸YU¾`Å»Oè¦+jÈW‹Õ9¡¯;¶ÈʳÐÄ™¶ø†JE´£l˜ê$'mŠ—ŸpÁô„«,€’þ‡†£00íÝâîi1Y2å7ER‚B.OÏ6ܯ]•(ƒàçoJ7Ï¡÷áãDÑ|ÖÑž¼5b+îÛɆßý²]Y„*wgÕŒ²W”å|±`!´> 'ûm)ŇýôÐ)Üûœ.W|ä†×+lÇrö©ˆsE¯óVåe¾IzÛZ·w7çd«F@“­Ñ·¦EõÏ–y8»_' ·p·¢ñìü½Ôý–û7¹1dDÆdl?òå–e¢Ï38ý‰ DhnŸdׄ<³S¼œUo—r>®‹e÷îdž`Öžl`$5-úÁ¢¥¼©·ØJ-iÏK¢æÜ ù7SæaW/;²‹s§ät™ æQ¶jq&@⡚s‘óßpJU6‹«Z—t‡)péü\m1eîà ÐҼݖåÌ¢åw¿ÌSMVÎo¿-SùÀ}Kðž2¾Ñr2šô·¹’àUmÅ•À»Êà#éÝóϸÛwež‰–ÓK´é×-нL`Z*Ÿ'öÚ»lEõ#—¼šCð]¹­¾c¹óòªEß<æUjø„ßÕRóYAøWͼ,“?lgÿ|BþÇJ®–O.VËË%çåUð·ŸhA j{ÇÌ!òHé(«XR£#jž’z‘Ÿk|=”<+ò™ »ó p)wàÈÝ­–Ìy´áŒnVë$øõÃëâ3%êìK*&'/Jeú–~øC†‹4¡…ÀIÌ`þö[)Q4…»ÀÆM|Ä··I¼Î‹x1­;Ç€øS°Y £¥—”32&@-Vé¼*¼T”$¢Y6—Ôg€²"ùå¼Ü‘ÖwÖ.³âA Xìpªµn˜²Ñ´(LdZìݬXQ)¤PiçŸZ\µrŒëÖLÑÏÊ›È;ŠßÔˆÅÉ.íê .æýŸß“‘Š"âäǬÊ&øhÎJj2l îýxÿºkü‘&v?q^cu3ÅðJ‰/WÔvK5Eç›d½¤Åóe^ ¸¬ÜÌð¤Ó„ñ(¼M2õbÕ÷«Þþ4=zg9Jé+Óé÷ô/¶¾û·ï²þñƒ€6eªføîi¯÷Ýÿœm’[Ú¾÷]öÏ¢=ûPµÿîéwÁ¿T†üHЖ5©>•͘”I~÷¬9õƒï¦ß±/0×sö°¡Ó)«Ø¾HW…œC¸«×Q±¹zàÿBfþÇ?ÿáȇ¤ÿþ—ù¿\ü#ýûÇ¿ýË?þñü·yúÇ?ôìÚýñÿð/ÿÖRÏüÇci¦ùŒD*obÑmpç5ó]Vc)cºÙ?[ä¸Ù»%ì–¡¢ãµn¼6¥|‹²Ù ÎZ^ÒõDSYH¬úâ:^^%Sî-Œÿ!c¹”œ"œôßW1Ú?mòCvÔ#œÎ®å¿ýáåΘNÿíßÿnúGqÞ‡ÇaU-ÖoÉË:éG—j¡´…[¤QÎûÑÈ®6¼þ2lÚUÌ@ Z™¤>ªEw4õMÞ_ú'V¼Žr˜Ó0‹3y—e¹¡‘³,êﻜ¾{ZNÚSYú¬‚bM YÿkYsQÔ ˆÞ”ªà\-sAãL–ÈmQ˜à'nÒàS´ °ÓŸZ¼\7¨™9s —ô©HÞõ<_D˜ ³ƒÛ¥ëm[GQÙ(þËO-®€¨æ»gÝ’%K‡;È„T®‹â û.Ò»t“ÜìNA¥¸;ä [*ô[çä†Xwù‹ÙéDNðõŸ˜\üúì]pr2yVëlÑþeH«Ñ(~{;_^®rCPþ¡œ˜Áµ±(…!p_{¶~,s3gÏ–©Úa[ÔûHò¨ÝI ¦i6yŒ¦¸”€ÝlYįÚ]q¬‡¿DÂPx§EäΙÿ¨pÜ´¸·dÓ’ÅÚUÇ Øcks4Á2E`­ Z-WLI[liÚ‚"ì$²%•u«GCÑ¢Ô3É;ÿùÍ/ÁY昭7¬wBœþŠÏù¤IgY_gd§^l¶ä9ÎÏ$æ‹]~—ÖéEý~¿×?é•]=ý’æ9Àñªþ?¼zù„Pí“ðÙ³“~öu£§ÍzÊ;t ÔÛ$¯*§p#ëóþí*ËJJó4[æ–—ez‰Ó€:k²´§„:׫/ó›l¢ä—ߚ•¾•l ÇPºn넪¿ŠjîJ¹HÊmƒJæ˜wPzÀ)/²×Ø¢>?zÔ=ûðdËÑ[]¥óÞïélFt¿ù£öûãáðÙÙýÉXü»ßM°ÿ¨?™DÑd2'cÒ~4}¿`ÀÏ6ÝÄë xDý¬×׫K¬é÷}NÿLØ_@¦FO£ïçdã“]½¢¹–i=Ü'qz1Ÿÿ™ìÞxò$øo^ϦAE'ú„O£€ðìA¯?ìõà |6ö§£0Hãåìzµ¸ ^}¹ þ›àÉ“ç¬Ênþ6xÉ;y>肱ý-9Î’õgr¾¦ÁIz±ž³8¤à;ûñ—?²ó¯¼lpê®N®:™ƒBvÆ‚ü>ƒš9á¿\ÝÞ­çW×›àdÐ[­³àoäâÄ ZÂÕz3ßÞP«}çoâ4/®·´K¼^fi™Bïcrq½\-VWw ^m×Dv!lüáÌñúâšë«(gúzI“ˆ€/X½àM¼¡˜ Ÿÿü‡d¾ ~]ÎÙšlîþø4x±X(¸)ëtý9™=eåRi_¿¯Ö¿QëÉlžæÌwPVCªÿ°þcp¶ºÜüN³jÿB°º$ÇÌ ÿsïk.ÕØÐÞæ"d4ÉD/·‹ìxúÛëy÷ëÇàÅÛÿüíŇ/Þ~üÿ“ Vä×äs’uE/ƒóÌðKF[ÇKr±%èyóêÃË¿W^üðú—×ÿ#ÕþôúãÛWggÁOï>[ëû>¾~ùë//>ïýðþÝÙ«§9Ö)œêjVíý’\Å *4D½‹bUŸ¤ùtŸÐïÃhâýñÝËÿñý« <‚ÈP?üòúepü¤×#hêõ~üø#%˳—yõ楊tÚ{õö8€Å”“Qf*r"?:­ ;k™N¿¤ˆ˜Ã½œ·¥4Š´&,d4|,‡l˜_ÈåÅŸÑýXDò•±6ÇËÚYþp°dËÜçR´:àÂuŽ5!õ?ŠNõ"Ø,ôW41¥Š×‘£Ñ°¦’ÌÊyðÅ[a(,y½cf ³ µ}žß±Œ«%½‰Ð_ÓÊJ™ýD6笥(ëŠÙA{"†s­® »ÍPŠš*‹jáø\ÑyM–óE£\Š<¸—%È_,wDž·1 x«bÄòUŠg³ çÁ€îËš°íø˜ÛE/„›i¹)²$½¤Îm^_zæ¿È®J“Á›úÙF¯9×I<£ÇŸ‚¸È6›e¦Í¨"S˜–}Q¦‹D6V£ «»!ìkN#@¹WcÖ´²š„Á¸•ÞƒÄö8§´¢o6ã”N™¿ù´c`âÌWK3w6»ßçé5£Ðl†¤Û8¯ýLߦ=/ËWèü)µdo^Ókg¶bE 6€RC÷HI ð&½z±œQXJ‡ŽH¶ÏÈJã+zSŸõh ú]1AÿØ+z&Xü!«€òø'}Nó‚/Î( #RsŒæ·ŒØ? (Мf“÷Žæ|,B· ½-bž_ËbL±r’agÔí:yÍB ^QKƒäóä’FÜÓ®&³P‰^öïl6ežYë|y¨H£²Óz‘ÄÔ}¨ôZÜ5úѱ˜òÂ[|¹É•"Fd±î©ÞC&Ðì67óÒUº9pœ èaýý!¸]Q±rNk¶©¤ËY½â°êåL¶×”±j®ëdÁ2ê“©òÆ_‰¼ppG;¸ÖÉR ü˧ï× M$ü`0TlùL|"3ked£?,œel M”=$tõ, Lnu›'ái w„Ý=(äqÜ®mÜ=$¼õv„±7.¯Ñtˆ+®,·ÛÍ¿¬¶òWk¨ü‰Zú¾d2‹fˇq~©Áõ8çªÞU6|SÅ­VŸø@NâB¸‹ƒ¸ÀÜ9‡KlûÇp>ÔÂZ»‡p‰¯C?ƒ5(ãŽß^+Ço‰ÄÃ?}hÜÅÁ›kÑs7=Pó”‚h/êR =TÏ·zJ˜‚=ßʻҪ@Õõ=(¼ñº>ÿh»}¸jŽÊÕ˜_ôœ¶©æ2K.Ìe˜y­‘([ãÞ]÷¸ï+¬fñò…Â5ÓSЉo’<]K‘“÷U4Ò ²¼E³¿äS—öÊ‘‘‹[šyè²¥·±ósʆ<ó}º%‚¾œœƒbå"úø±HÇ,/[lÅÅÁ.R©è±NØàã2Éa OÉm¥×efGõ¥ xÙÓ€5 y$äsãGàÀ¨EZnN®‡,ªMÖÔic\ \ý1Â×é÷/–w„ùÞ>a^2 Š5•cUƒ­âÑ´®Cö:«I±¢ÃOXW £ç;™/™ûäí"¾Hžf}œm/®±âf›R¯"¶8b˜RŠvSú–ÒxˆÒøŸÏ:8—‹/b '½Ê}U%:ŽPJÇŒ4 >Ï“Á›ò(×Kdå1K«úøñc-KcͲqãܬBJÁÎhsŸ€—§ùûêA//)ðWm[›DqÞðsø¡:ƒÀwŠ“‚ç¬D•`¦ž°ùÉ…0ÔôüGŽØò¼P§ÉŒä\A:ZÒ@—5Äü…À­$ƒ{B© *FÛÁDy>·ˆHzã0C«/¡&6q[J8JUÁMå PXígNe$8·èöqqîÛ¨Ø'ùdÕ®ÝX\Fżaîbg¬³Í„á(×'áXvê‡&%¶MÏh­ScµÄ–vÈ¡<°@_ýøjžWŒ+ÏÏ*Ä.x÷!«rÆ|/Êo¹RiÔS—fÎ:'|æiÀVýÅ‚æ³2fAT•åDÁ½êï÷ë9ëY>9¾—åuÜ^0o`Ò#Í‚¾ºÉÁÈ‹ðuî¸xv Y?]Æþ•ëh›ô5xênDËmˆo‰|;TÌV®OßœÄá>rpL~ª÷²áD}c^Žʑԣp¼vey7ÙÙδҵ(.o»Ôµ8¼£ñ©®kÐðx™³Wµ¨®nû@Qðuoн‘[¶ÎfÙƒÿ¬NÔ¹1ã²@ ?ogWœá –ùN¸’K”ÏlÕš^¡—˜}V}ç§ìk[ÆðX!ðÇÀ ß=Æ™8áGt@Ų[Ühhë>‰_í@9Z¶Ö;âb;ëÇX­X&©¿èÓ4·ºÄåî|Àë\ú í^ŒßùJãê›ÂMP'‚Zôý’ÀO×=À9ÇeGùû‡Âå¿ b޶½“ÁkßUwÅ´ßç>¸/²ÅEN£íEˆ—Ñý¦òž±—t±N{¼_»>ôö°~?d`™nô O½%ÈHÈDœãµÎŸvÃ\vl*©EêâÕ¿d×÷6¥ìÝ,ØÃ•Uû6%åvd\Õ«]]ÑÂác¬“€7ùAX(¹ ³•Rö|¿r´RBNä{"Ô¶§X~,§ðkßý•\òÛÉUC­Ò•Rq¾Z-’˜ÝôPjxÅ›j_B‡4°ú82Õ3íΑì¤ÔM©+k•'òÄôiðzS¸„¥U‚¤•Ý9ï%³>Ï3×Óåf~1¿eœžKÆÍÓÞVTä^ W'Ø@^¢ŠH«e`±3æ’Õs¸ï<öÝãöðõè>ågÎ'æ­ü‰Òú®øî)¥‡1ôõúaIõ¢A4œtõvñèë?üúñ§''Ç~þÍÑ)”±çw?Ÿ½ÎEÌŸ)±¼¢´¢ÏNµžÏëÜÞ°$õ¥`šç«ôhŸ½Òy™%Å/;ÈË=¥•©Vñ-ë‚Rd¯ÈžOé7?îF-4½Ñ1ÌcÓO½ãç§¹'páJ¸'ÝLEŸlçIéÝÁë=?-„Ý:ˆ‰åy­¸F Wä%¿*`?{_Gc­›f¨¯ƒ7¦A{¹º¹!7&AµfY)S¥Œï«¼ Y¤äãü&Ñ·KzíºInÎV½/ÍÖéë%Û¬>`0Ë_­âpÅ2¹à¸|Õxj ^ú™‹R&m’%¹òe:Ûàs¼Ø2ÓKÖ­çºÂÁ’oîW«Õì§õª¬«Ì& ¢¨˜+ÔïäF»pï"þÏñù"±ïGêˆI1yo¿Ì/YÓ÷´N×z3ORök…séjÌÆ©ð€éä±·r˜ë¼&Ï|÷´'ή˜³¨‹dþìT÷'`íCñ­€v^#+Y²ÞÄ÷îa§=^UÛûãuR”0¡!¶›²²Ï›_Ï>²Z<™JªÀzaù¨ã %›k‘ˆ)/²2EëLã.á+Ã=cÁÊKº^’îX As¬*s3lÒò•ò2Å!tõдúš@9ïP•Ê=ImÈ­—Xj\E‘p¯ãål!Rí_ØWÉ*\”k£á¡rixžAÁÃn:«µÐ/Ùwè&Êù ת„ ZÅrD82NTTW{&ÇTÏCW·pï­°ýo’õ%_š)¹ãHÄ€BÕÎþ™ˆEAÈêøÆ°-èŒM8?U"¹Jq& ÙW˜ÅßTÒ3Ã\š\5À+奩sE©çqlÃ90P>&k²œ UøQÎeš È|[œ¹©‚¥dòØá†¼A®w7·EŸ¥dVçx!ˆõÝwg•(Ï„fj¶©(‹ÈÙyÉ&iÜMšl }¨$ÕÃWŸÐ§RMYµxÆ rywIÞ7ó²ü»tÉÇqµ‘Íså–ÿÕZw;åÜQ\UïÊ‘ ðr†V zLÓù@ø‘cT)£þ´Èt8?%œrxÀX_ ¥©¾çV{Å4s$¬HQ›š©Bçlë3}ÁÎ빈ljo gô겺'Ô«Ñ3jGÈ‘ËÜå n,3+ö’áŠTµÑ ëõ…ŽYU=¾€)WŸõôý‚×g2¡õüŠ®¸ùX³c\P5ôg@[Ž‹˜»èš5à•ú]Í€n_’oëc¢ì¹T!Øžx—4ŒâJP+ªcг’FQ—†#¦d)±dΰ©P ¤‹ {`©„íÜ·«Í#Hf?);ô idØ´Ú×õ^ʸv*$hê‰L2êβ™¶æ¥=›4Y™³í-µ·C«þJÛ´à—ï™Iëõòs¼˜ø¨ü,pDH9¿±Û™‚ûü"^ÏÈÁDWã¯óÕ‚÷ßçfükeXmý{{0ý›í&ÎhQ3û7H£bÕ9& $úÒ½|ÿ¤~³šÍ/ç8ÀÛhÀôæýã€NS3õÊφ#/ì¹Ï—+r;¹Øü•Z{Õ©¾V7±6ì•=˜-M±4?ÅóÅv Ìö½ú»i¶Ø+g+„ˆ¥N¾0͉ £Ä5À\'>ÍoÐ^¯¶¿./®ãå$F|Ôµ4¨Æ—wLAÀÛ”3cb@.Ï«Åçdm¯rþá.{Õƒú/³î%›g­›ÖUº~õåb±¥¸³ìß“ »@-ie½,L´ Æ×ÞB÷1ÏÃìMˆ+Ë? ä…=`ÉËÕ‹ÙlžÙÎJW…ôEá¶¢Îý­Å lXw±ø±ÄŠ3.öëd6§â(Š}~4Ìl¾Ó£ZËcÚ?—+Zr — 2’6’ø‚#Jí¨Ø×ôÏÈ­Ôg¼ÅZÕA†òÞ¾â$Wìý%N J£ÙS[Ù©ö÷ö'›„ÆàÄëùâî×eŒ³Ýx;Ó•Ððf;˜©©(AÓ™<ó­Jtz»ÚÌ/çY`Pp¶Ú® QeoÏ©ÑæÜäsVµ£?š(hí¸Z€“ k¬äB›/_S/8"ĕ˰--¡ùó Žâ/rG™2§ú^ÔVóM—G*û´s‡ßŠY½ ¿sŒn¬®\%ÉÀ ÎÂeqáÊ;»Íüñêû’¨Η¿Õ¼¼ÊüÛùº±‘Ýõ¢V"µº¥„^gì¾^¦›˜4ÿÅεìÏ«™¾æQ¤W²ÊvÔ:Ksr£äßö '2/ú÷ªæýSL ÝÕtr}¹N±ž•yT0G×zÌØÂuÅ ¬--ꥥþ‚M#}mðo†1Ü”1óØ·¢µ é¨Ì¬OÒ9ö`­qØœHGïã5ùLFµpxu%zJ=úùY€ï6炵V@°2¿Rî‚ïX¨âòëÑ‹Y¬ÙÝ«/ót“¢×#µ•Ýõyo§š‰bAóåU™ðËŠ†ZaOÅ¡EÜÕ;"Þ092½žßæc}ØÊ±*žþåÖ²ié¯ï ˜ïÚK¥Šû»Ý±%!Á|p©þãf’T=ØÑµ äñl<´MØy–×Éã˾ËfMö›®3WÙ­×Añý³[Þ§wˆ§þ£êgy±TýçìÀãK@¶:LrYEMy5W…tm·¼«æ™ÓRaÁ BNت#kMÃU33Ü|bdPÓÇ ¸1X,L@+öH–+‚ãö#Éú|ãuO±&Ø¥(ÕíÐv4@]D‹ 0£`FnEÑõ¼Ú´t¶<-k]—†c áá)Kääc~¼LÇSÎ9Ï~OZ9¤Î/,¡Yu_~ÌÉT§v.4”a˜óÄ‚¦ÝFTÁxâc€ù I¤ŽÓŽ@=ó TØÇS¹H€½Œ1-RZØwƒ .D3­H°aÍ8ØBŸFºü'2p´¥ ¼È+x G‰•Ú€ƒfàš!”FDhÁÁ3ô ÏJô!Á´àày…g<… ÇtfÓ§½òøPO1-¤z„D£)dª)ÁŠš B==¢ñ4ÛTã59­ÔG4e´X32·#-³¹cðØFävP™HCŸõU%•p2µ1rVÝDPI'<Á†GH)tF¸‹ŸéØÈìW¯Æ.at<î5$Õ·")·£¼>I é ö‹ øT€79ç~¡ãc ÊëI Ú ªÈí|GIjhER·¸–¤à¼ü*%²×¸•P:Ó=Ì“äA1Vä•fÚ¹ y¥ëŸËW9uäñ”÷`À/`ñ*£Žlv”³è%l¨^¡yx~zU˜Ç4Gì\áÿšÎ %7 ±‰-ìò‹¤(¶TŽÌš†s—®P«“¿—yR~!@pRµaêabÿ®Ìº\ïlØæ€)Q=õºÌLõƒšÚAs²™ñ^UdTZÈaœ1 å ]-™„AÀU©;Œ@í»À$[a3¿8†;Aê¡ÕÄ·ïtQi¹Èî*%ÚH&gÃàc¨àæÏôi!9nÆÂd¾HW>V>ªÇwªA:Ô'\ªIhyªü$Òáæ´'žH4"r`Ñ«ÒÅÆ€6ȳ‰É ?P«Â#ìÔ–äˆ;ú ž@­Iˆuœ€jø µ&=š„ŒžD­I“¸CêñãA΄`ãéõt ‰¾=>LW d<ɬ „ü~Z²oY8 ™Ý‰šZ¶PÃ暆x®55fá¶,=z;š-K‹l(›Gw¥µ2Êz׈¶¢ˆpÕ‰â Q/ºG•) VèÑ2ë¨ŕȑ?ˬw…©_ˬ£®W•6²Ìº+QAhFM-³Ì*<㦖YïæAŶ3³nd™Œõ–Ù_¿žÁÄ`™xy'þ,³GGevð̆4ÎŽWËl„šF#Z90¢=«i™¼Ê…ƒ~6´«³NR¡I9ßøýZf¸eáGMÎy€ êZf^OòÁ°ªr;ßQ’²²# ü’\-³¡b%E.E~Mûadkœõí›×έȷϞ—K‘wϽF&ÚöÛí´™©­H—Î[kýwkv[Éxr-Œ{–—¯… “ÔNš—´íz!^ë}1wHÝŸ7ôüP cHѽµ¦é×¾uN"ûå$b 4… ¾Ëˆ«ò3²S~úÉk§ülõõvÌûɉêçœw#,¬±Ï°·K™x Kñª4 O|‡¥xw‘n–âU-õ+?›H¿©òÓ¯„Y)?™u3åghP~ú½+"“òÓ¯à3ð¨ütôõÖ*?­¾uÞ•Ÿ¢H§ül"ÊÏa]å§oOŽÇ=NR‘ÕeÚݼã9, uÒ†¥ø5èƒ }}XŠ×“· ö8äÞêò|ÔÉÙåùö¦Gìò|;Çuy¾uÑB]žï.Ïw®¦èò|wy¾}Ùnº<ßÞõˆ]žï.Ïw—ç»óUê|•ÚòUò¥C4z ]Ãì»)7Ô«/·k‚¤9«ÐˆkãåNò§óåo@ªmUM(åÚÆI|aÂWË)Ä]iÞ©Ž:ÕQ§:êTGê¨Suª£NuÔ©Ž:ÕQ§:êTGê¨Suª£Nutª#@UѪfæ&¹9OÖù¨.*š6»X- º6zx–^f-ëèìUF¶¨ÓuÚ¢N[Ôi‹:mQ§-ê´E¶¨ÓuÚ¢N[Ôi‹:mQ§-ê´Eû¡-Ê7›Žÿìm«eº‰™.†Ó¥×óÛ\õòak*Qÿz¥R#9‚^;Ä2aZ5³J©Ž.éCr5'BilfÃÞtLëäfõ?™O¦Y˜ú­Hö, ើÖÛI€å;œÙOþ+ïj‘xsqMoÍ0¶äŸ[Ô€|`$ƒƒ¢üÞ¢þÃæ~ߎxd©)&r¶ªØ¿tm¥,ö.S·# y—ªý:ö›y/ù,tìšnì±Ð±oM“çBÇ^-(ÑIÃBǾÓô4Mqç»$iC±{¶žû,=jÒûzñÐï{4ÆC¯"Î0ò§1ºkZñp`CCgÇ«Æ,J«Ñï¢&­NcüKÒâ$u¯UiãÙL£Ò{1›íFGêTwû¬ºËu:,??O°kÞ°5Uޯ˴X˜L õš€°¾$}@Z{PÿAPŽ)‰H.·)Œ6éW?ÞC Nã,øz®¨>®[Qݯ£ÕÏå‰_^¯ ƒ“VŽ/¿NWƒgVg—ómÁOEõ‘eEu¿¾¡’Ã;â¼îñpÒ’‚ÕwJz? VïNrjúÎD¿;W[3•`“2Z=Žr¹WïGìjúóVQÖwÆÊ6K$e&—ÝçØÆ…lÞPKÂw¹;ø«¿oç„à“åE1Xíôš½YBöœN€´xôàŸôâ:¹‰{™ì?ŸrjzJ1ãiŒ~Øï‡ÃG}òLÆâßäŒ&“GýÉ$Š&“q8“ö“0< úžÆ×>[Bë xtNÈm}½ºÄÚ™~?ÐçôÏ„Cdj4KaO ?#û‡°ˆå¹…|üéÉÉñŸÉVá¶E·Î$® ;lÞ\¼>§ï¥…Këe/‰¦OÉ·é*¾eýPêìñ­§”xω Ãà®=~þf*tIG3CA?õJ©î†2“`±Êò®d\êSÑ;ÛTùôY6ÀX qq0ç(äV§ðÓÈ0ÈJqøâ:ÎÞ½xÿCöuqp¿R¯‚¢;:ï)9Íh†nÂùDè‹ëUº™žôOú”µÄÅ"§½fvœÁûK¼œ-’IºZB ø2‡OZ^±€)ÿ%¦u8Ô„áHj£û€n¾üÍirq™‹LrMw©“€¨´¹Ø `ÝD¼¬o€ŽoêÒâËçû&`*òßeÆ·v'ÿ…ò«"ÿ‘¯:ùo»üW}üÞ/¤8L ¤&Ê7ÇöÂ^&”±+[=y‰6øÛÙ¿ä·I2…cNhtÐKTž¢™ûž^eâ^) J•tÄypÈœ7…úÆšPÏx7¦4aÈΨ¶ÏFµ|‘^,È’Íî^}!'dªquPÛ¾w¼£?åÐΟ²‘o à céOéèx‚»œ´²ÁTPÏOþ”C¿þ”CŸþ”CÏþ”Cþ”C¯V¤¡oÊ¡WóѰ¡?åЫÙhØØŸrèÕp4jìO9òêO9²ò§Ý?åØàO9ö+ Mþ”c¯ÂÙØ£?å¸ʱ•?åx?ü)G5ý)G~ý)Guý)G^åÕQ+þ”#ïþ”#;ñµùµ¶›Ó¨®›Óȯ›Ó¨¶›ÓÈëI>jÅÍiä×Íidåæ4º_7§¥›“»'1ì'gëæÔÈ?O½Ñ·äæä݉ÏâÁ«¼:hèæäîÏJÏVnNîa-NnN¹0@}ž =aïkôséøÁü>Í‹|ú%yÃàÿCÙþC>töŸ]<¶öŸŒNšyúpŽŠœLÆ:7œ ¨igBG¿Xäµ°÷Ïo~9có8._H2wŸVë›Æô¿?þû–œ&—ó„ÆQÎ|±º¹]$_ÔtpæÙXºàuy‘ö¡ÓxyÇ›¾ý–|¡xB.›¥ƒŒØåi¼Ù¬ççÛMýoá~ùöÍÇwÿáÕÛJ@”ÓfEã¢Z;Ò´±h¿\-ß&Wäx¢¹>6ɹâ/3*^&ujpÇ_(Նń8cî=н]ú³oŸ¯V‹$^³bI3?Õ†ÅÜŸÕêýÓGÏô‘9›æ^«ZbÉI±41H]ð¨Qúç3؈D#Ë {ÐÿϦÈQèôç³éíÑЇ•ÓWŽVôú Yꇬî³w_ ºÔy%l”Mz×'ˆ¡é}¥î‹D&¯¤!÷þ Iâ!‹=€D〛Òþð¾Â÷³eæ—ó IýP¹_q —/£šÔÜ}Tlµ‹+)åŸ?Üeã¶ÀšË®4±æü(›ªæF ´²"Ë·«³ÓÅ‹Ée²¦ïôÅg"ùS{=:žÕ{–Ô·éhgÛ‹ëüP׌¢´ªßûÙ&ÖŸxc«±òÿ§w2 ÷@c»ó3¡zµx=_ÜýºŒ‹¤kn5Þ‡d6§×[Ý[isogu§yëDÐNóÖiÞ:Í[§yë4oæíAÞ=;Í['ötš·Nóöur?Lóe–åTnùϻеIYä¼ÒªØ÷ƒ&Ї̋ñÔo¸P å‰ëô,‘ï-‘wz–NÏÒéY:=K§géô,‡xÓèô,ØÓéY:=Ë×Éý0=‹¡r§rQ[î$ø&+5sî›z«~4¹vœyŸ8s§géȺӳtz–NÏÒéY:=K§gy7NÏÒ‰=_…4ßéY:îç¦g‘kcZÒn:–Y²˜NÖüÐÞ%9u„‘oíåÁ !cËĵïòÑyÈÇi§Eè´¡Ó"tZ„N‹pˆrt§EèÄžN‹Ði¾Nî‡]Sù¼Ó¯–<>FiØ]Lˆl2‡î.¦ÝÅ´»˜vÓîbÚ]LQ4ë.¦ØÓ]L»‹é×Éýl.¦6wÒî:z@Äúùrwí®£Ýu´»Žv×Ñî:zˆYwíÄžî:Ú]G¿Nîgsý\ÍÉñëÜy±ö»¸¦®“›ÕgÿdÌ:}ÐDûùó›xsqMošÙ(m¬zþÀ(Ãеڨ‹™ï¶Ëþn—N‹Óiq:-N§Å9-N<›y^tÚãƒ^ð}v­–äåÍKš œqU­d¦im5گ˴ÀÄ›äæ$—¤%>5¹É©:5ëW®hèÔ¬oïÔ¬šõëä~•šµ7KsbåiÓçGþÉ´µ=ªfÿù´ä;|J3üéŠê…‹OOOg‹:côÃ~<>ê“g2ÿ&O Çú“IM&ãpBþM¾èý¶&Í?Ût¯ƒàÑùês²¾^]bíL¿èsúç/7‹€L-e|,|Ú?’åÅŠR;‘|?þôääøÏÏN9²Ï·„÷l 6Œ¿U¸ ¥/‰ä$u(–©Ö†@©®—7œòTš÷Áh4ß§º!ó&iÑÀ<0³`.2§õ‡7Áb•™Ot›¦Žíšc€Çê@$ì(ïÍ€ôª:¯DƒOS´?ÞN¤ú=°`¯ä¾NÒ”CO>IúÅâz•n¦'ý“>s\¬}Úc\—ÂMþÊ¿”í}ï’‡ûèùÿeVe¬6Ç=ÿ‡£Q(ñÿp<Ž:þ¿‹Çÿ/ŠÕÜÜþ¨Íù¯è§z*mð·³É/2d Ega÷ùQÀ?Gð©ðô cùö#õž±±[§w/ùÇÆÊ}YOLüÈpi|EÚ,ÓÁhú’oøš6x“ý^ž·¤•ølÌì. :}ÇZ¨£ŽŒš ûü “©þê\ŽowÏ.¡œx…òd _¹uÐq÷òª¯P=+œDÔÂ}`š%lÏ|Â6ìO!8¤1‡}×1õöùª@²ÁúÓ³¢ ¼·œ·VX ‡ìªÐ'ò£vöTä•x=í(¯D;UÕ^<^¬¤fŸ°ŒlöóP˽ÙC“1a?|Kx'MšœêVšL¤Q‘ 5ñzLNÚÙR·!ÀÀ|ægWMÜÎ ¬“þôe¼†]\$ÈpO˜¶7Ö À…SÌ)F‚ ÷)aóÊÆO¢)s¡™[§q¶)ÁóÊÁOSÕ÷Ff—€sN WÞ}2œ®:8 3O Wþ}2š®=< óO Oñ^…gls œ83k;¿"ä(y6šæž0ðòÌïMçÙ¸9<žy½ã<›L1XLƒJÈÜŽµüVŒÀwbCÏœï.nŽ ©œL¦àW`Ò9i"¨¤sr‚ Ò‰×+ßɳ©Öþ&³_½­®„Ññ¸×”ÕõïY;×?Ü^Œ±ŸXÔì„59ç~¡ãc ÊëIþlÐU=s;ßQ’Z‘”ó!^Ï·»º›"—"¿ú…I(‹Ýм ¨“– ¯Ô=ñ¤j˜x•W' • ¯ÒêÄJÝ0ñ£oèšóç§L?-5é ]‚YŠVw Ø÷¹v=P3¸hTñTm®"Vö?ш]{ ƒý/êG‘dÿŒ¢qgÿÛÅsßö?ÙÃÍ$§ÓÜyÀlnôïôÁ[Êí¼<¨×^ƒGÄ(¶‹O ÛO7U«RµŠÚ%©¡åmâíhÕ_çÁ»ï¼’ÕÛÅN:zÜzÄËxìd‰> pnˆUºÄmwôÙEbw‘Ø]$v£Hl€Õ ¹Þ.tØAÔMb45lŒ¹7kz¨F>Â+Uu‹£Ü›Õ|ÈrÂáÝ«EFZ±œZ!{C¥‡Ês ÆûÖHvÏ.Ÿ\ÿÿöl³zÿÛÕÓÛ5%Í9ÿ/÷KcôûÃþd4Âø?}$þ?†‚QÀÈÏWÎÿåõ—ø¥—1ê¯ÿd8t뿋ǰþWiú‰ý–|ºX-©›@fêOT°ñ1È£Ñ@ZÿhB¦“ÿvñ¸ûœ1²`éø¾l^,ž/æéuîM€û؈i÷¾áF®ðôK: ø}ÀÞ"c†½~óË›Ç1uS\Jž>í‘ÿ×܇:w{q>sWcÿdîsdÓ åžrJ¿}’]Ü ¬‹_æxHç‘,J’9ÍRþ¸ú-Y¾ú’é\ oš ÀékÒ&§ê¬iÖUï•.= £9W€øÞßm7ÒØdÝ7óå61Á_v°ç&ðŸö*"¼£—“ÖÑiîZÚØ$œ îËŒoc²Irh¿?&;s ,aÖiJþÞ­A¡8íåcY +º/ÛŽ[!¨æø/!tÛL¡Ã[¡À@'F8þáÉ“€é!Óàɰ7 íqžÞPJ´ý%P¥Ñòb=¿ÍØLF£Œê6kê%g"¦PÏ ùµ—¤ øñø>o%ìWMÜ15“»nË»ñ€!<00t9Sls¦ðÊi—`¯ˆÖ(¢²#< „žßÙ š¯T¨jÌ Ñx•¢©a+Ûmx „ûºáaöÛ•mÙŸÓžØÉÁ¸û+Ûý¯^€áþ7QÝ€pÿ#7Âîþ·“ÇöþÇÈ]½¾ÈåÇò÷c²MÞrÉŸt”ñኇßéVdÀæB穟›L ÷xºÜˆÆ"_œo7ÉO«õ Á(å…ßÿ}KN¶Ëy2“ZçÖöq§åÅéôbus»H¾p¼‘—÷sNHù~ ¨$¼¯Óâ§Ycùš­Ÿ3Fl4[DÒùfìq)e­vÞ:¯ŒQ_h¶ºzz«2*”qSHiì]«Å¡ó+G/¦¯Biµj:uŒÝ²Í¥,t>ö wÚ«Ôü±S|Üw¡°±þŸÜˆMc˜ü?±œÿ{ Fü·‹ÇVþëtÂNØŸNø¾©¾{Ч1ÿ¿2«Löß¾jÿ¥)Á;þ¿ƒ§û¯xEmz^@wn-ÔÃ1Գݱ¼i©ãði@.¡hjHÚ³$窌vt.(q°¢Dsó²b·4œ=™—ëŽ[¢ÌŸyÙLÍË.ã£hxØæåFV+g³%]ý>Âlà›"ˆD;› Ð¿¡±bQQÇæN5ï/J,¶„IJ-aj Ál½<ÚGUU÷´ð8Þÿj•4éÿJý§I?ìì¿;yöÒÿ×¹,`Fª¹&áa ÖŠHó”8ÛO6UëÁ«”Ä× +hÄæTÓ§Z@P/¬t%ïåqäÿµ‚šüú“¡ÌÿÃq¿ãÿ»xö“ÿ;'ÅY–«™ª%†/P5àøjQK¦œîQ„i˜;çÁÜzVQäzf—rÄßbZË$$vÄÔ03‰ã PºD9¥@ïœ.©cuƒÇ„4ttñLJ, ŸA¡ÒeŠùªŸ\þ»J–tµ“Y âÿGQÿ½“GYÿëu|wS7_´PýÇ“a·þ;y´ëÏýû¬ŽÂOz ÷?rÙÈõ†ýAwÿÛÅãÿ]ñRF.yZ%Nîáh©ö­.&#\'ÂMŒüð4ûš5'ŸÐßË„ëù¬P‘¯eí_ýäIEÇä‚·p˜ët¾Ü\º¼G¡'ëa¾?‹ÖãÒT1}1íù)2îq½®uËq=‹Œà'—ªZQoê°ÒÔb&„Qs½‹ÑëüKTö·ä&¹ûSy¾fÉco–&sµ‡" ÇàCŸõ²Ù®KS5%¯éÇ-Yáõ/zÊ+¾ò2¤•ë¼ÁúínT5¼à¯ ­‚D”H¸‰¿¼» ÷62Òvy¾Ú.YˆÌÍ|Y|Û/”Üä"uÃ[óãåL b¨NlötÌ¢!ÁŸæÉbfBOöý/AÃ|±ˆÏ©h³Þ&°côv\bû¶ Є2bµšJ²ºüi_ñãK‘PòëÕï)~u|MsrS: ,ëú’.Ú9Û‡üZÕ§4)r…¶f_ˆN-#)øDæI 䊠gäThæ†ÈÀâG2 Îò[Ží2¦ä]ÃqoѦ\Vtå|ºò.ÌÏ1Ñ„CP6%`–=µÙŸųð]ÕQ>qѦø–s)õÊêmàŒ´¤Of0™ç[mì²*$„ýl 5ä¨LVZÉ ’VަWAƒ’ˆnÇîÌ\‡iE…’‘#@Ñ‚­ Â ûµL‹Õš{e¶NK†i geC2»µÄ†ªYÀW™éÏäk¨:\6!ø×ÜÎîy6ü,ò&x0 ò¾¯¼ÝÃ=Šþççü_/‰Böµ CþÏq8êô»xŒë/}vÑšê¿&Jþ—pÐùìäÙ™þO¢£¯„çk§DÞu×vš=ßš=9×Óî‘‘Y™97 _ñvs-_Õ¯é£ÚB¶®j>y"tºC8_öÛ«9Y'-â.Ýž“»ñ¤ ¾w°5ÇbÕ“GTÖÖwó1é”W뽪i«û*y‰Ç±­ ¼Òˆ ¥¶(÷Hã±E–`—oíghÛSŽÚ\Û)h¶Oîì²ÑzÊй­ö#8å{“Cn‰ÖwëY²þþØr²Ü*ªëj;E€®ø¯œ'VíÇÙÉ[Ù±¶óʧò½7…6|µÅ”Úvûc·Šm|ó5Pn£gŽ2ÍzšcEdö¢äÆO©ƒk¥ìÖ2ŸË!{¹‚à1{`‹Ç æþV•}örqyy?×ÒÖꟖ'‹ãÅnyµm6Hïl…BgÔY¢j?¹þŸfˆm¥ö}êÛÃɤ³ÿìâá×?¾ºZ'WñfåÅêW=õ×4ŽÂnýwñ 럳ÍO³xª¾¯—÷±xLùcÅþ7îê?ïæiÿ]~QˆcBÃ@| 3Æ0“O%*öž!F·Æ’~{¦¬úvª½LP™éÖÑt“¥½.ãÜB60ØÑ2uÓHúZÀiÃ'qÔ[Ã,u&—úQuŽ®¶ðÙö¼ÌK§&Û/VC²àPâG¸OÎqy ·àæƒ"Û£dë‘fñüèñãúÓsœWþü-óy¯ Tÿľèé__m×å[”¦‰—³E"Î~w¾üÍíÍÅü2ÙÌEhysš%žYÓ<ëQp„ F®tõ–Ü.“¨Ô­œCT À:Y'7ä(‡ÀÞÏ&hÛ œ.pd›®ô…899c§UÂLïú4•ÒˆJ’N—!-“”~P0è4_`-ëŽë6k=Ø$î„§Ò¤°â)³4ßãoW›ùež'âŒ2v(úcxK¥érWjéEÍ3 À¬¦FŠZBü˜lÇ=œâ|;xøûá}I|CýÆüŽápÿNúÝý¼þ—óEò)ûøé2¦!kwõR¾ Áÿ7œ åú?ÃhÜåÿÜÉã~ÿÿ‰ÐÈ#‘Ÿ2 ÁÓ¾™oPý9磄›u““­3 žò¿A;Éœû­6°J8eu€Üo ²¦Ê[;ÍôFÆÝTñW Âê¢é[q»,aõ §Iº0£AyÃr]Ðùœ4¼³Êfx͆öAÉÈn´ª¥Õ(ùr¾X”Ìî^}™§›ol5Úoƒäxi²á®í¾4FGù‚ò_Æ„ÇôÅ¿ÎW F-è8h[«‘Þl7q¢y$´­ÝHDô¸´ om5E¸y •U﯉ µ^'™‡š&ѦÚ̪ÿ÷ñz3?ÅóÅv÷5»7n7£"Çê®6Wtþñ·œ­>õc6y gCÛÞ5¬3×¶ÉšL™ýðâ’úxµBØ`_µpsÿ8¿I~]f sq’Ñ¿potC.£³Ö…&y¯ˆN²Ôô‘i)òìÖ8á«­öˆKþ\®Ö-]’ÑѾ"ú?T>Y;S·Yÿ[«Ô;øü¿ÆƒHÖÿFã°Ëÿº“Ç¡þ{}÷!Ì™`~Ãûœ±/d?Õ›£lgì]v(Gá͵6£~pÔÕ­8ØêÖjŒÕ­M×ð|¸ïí†Âg£¶ÏÀ_׋œA‰QÜK¬RD(’Â- Ì&7m³.7àMÕ)@ÀÖv½Ð$Ÿ”ß|_ÔßËH(èép¸*Ybü}¼¹>(”߀çÔòñ‘Ú0.“õA!=ó&{_ õHWT{’Ë0¿Š»'^Þýúáõa¬äËu2£uUâEêw!)¯Uɤ·ÂÌLrï˜L P‘zj-5N9@7«ËË4Ù .…ü¤ÏJš¡.e<ümßÁ7s˜ªNC_|;œtÇ{°@¾ít´sý;M¨5¡·ÇT‡s«°OJ¬Ã¿|S’8#AáÍ-Ä(›nšâ›™Ít «‰ïS=©òW_Þ {íÊ]¬–‰öÅ*+·àe{ß·ªÃyÌ÷w¿¯â1æÊõŸ£Q—ÿq7ÿ¯ýrüÚ'¯Ýzzi]¼8Æ¿K§®Î¹¥3ÓtÎ-sË×áÜ´¿íð¸LAü 9[ç6Ô¹ unCûH-‡jWùs¼Þ´Å 3›äWCŠ]­ _6±”+dëÚqÕÆŽ¢T@]J#;iU±:RªÜ¦s}ÛCº~ÈwªÎõ­s}{¨g}m×·îyT'þÛ)õ{ þƒpÉñßãѨÓÿïâñÿí+‰›Ä¶šçn³è°½süûáôf¯òv-Sï¸ùÃyêÄ×ÕûÁÿ{ØŸÈñ߃pÐñÿ<^ø¿¨»8:ÍïQ§ú}8ªß²ZˆP/¤Ë?Ðåèòtùöç]þ.ÿ€Œœ.ÿ€©›½É?pô [µ|º {”¡$¹ÃMQ ¥¾Ý¤/°«ÓÅäz¹Z}ݦnT%À—ˆ‚úWªRÕÀ²èÝ'’]tóXÝ6˜†xIÐÌBºàƒ]f¾¤-É7 «qJ©¸þ@•@­I9äk  š±di¥ÆPŠ cUù CW¶¾è–ÅU‘/ÀBâzª”S÷-W…ê;‘Eé"t„¨¡3.ºêM!D¬; ¢Eè%7¬C5ŽEè#©¢´_#[NFkXM‰w™SfG8< ¿™$²î‘óŸÖÓLNžcï×÷ë‡_ ױ܇CGG>–äÇ€Î"ÏÏ¡âó§E|P{U‡.¯‚߯‚×uîˆGóJ%uPØñæK€ˆà"tt§A&¹l’÷]%Z~8ël³Ê¾¨Ö‹®fmï>Õ¹¯óí{`KüG]3 Éþ7ì+õÿF]þçÝ<{éÿWÛýà ¯™Õ΃¡Î‡‰³fznkK]s uX†±4`v\IRá2íSѳj'ˆ‡9ZH#7ðÙˆ²\¹LCqHQÀ,Ç Œ™ ÞÜÑTo\Žnœ\Àù„q0…£”u°q¡ÌLŸ0 §hí? ,MÀ²¡OÈFS iđ눵j";+Œòø–ðÞ ›°¹Ò¨ØöòJ'á° ºÑL€9ò³ÇB7ÂBÁO_Æk8›ž´­„mì ÜdŠåo“`ÃÓ¼•°M¼ÝÉôMfœ&/\ Þ‰WðžMÕ4qTP¹šg^OÀþÈ*'æ+»&R€ O8²ÐIð€yêJxüJ‘Í93k»DmÈQ2§y1ø 4A…z† ¢b8äðø|…”¡dKƒ… ©Z ™Û±–ix0ø†6¤1poܲ¶!¤ ¦àW`Ò‰šÈ*éDClx„”"¯Ra4šj3«ÈìWŸ…¥„Ññ¸ÇI*[q·£¼>Ié0šš‹š½ƒU“s ªt|Œª¼žäѳV¨Êí|G¹TߊK9âõò«!”DÈ^l Ó3݃ä3‘EˆÆ«pzÒÎ…È+]?ósò*§†ý©šJMÞ`@®µRjö*¥†¡Í¦ Å/1Wi2’½à”+f lA {¢!ȱ¥Lœ¼¤¬ªUws£4@Haö”ó«hðäVér”ÔS'þÏ5\oÿ‹†£ÉHŽÿžL†ýoÏ~Åw†¿‡høëBó*¯¶.4O|ºÐ¼.4oÏqÞ…æu¡y2rºÐýÜ츳ß3Î~¾ý×ü:ûywgkäìçÝ™­©³ŸW¯hÐØÙÏ«»rdåÑåîšÔÌÙo`pöóë±5šœý¼zh FýÜý²pg?+ϬA;žY†nê?£ÈȘÓL3ˆ.œúϨ¢3æ4ã0ª_÷ÇQM÷Ç&’à©6®ëþØÄÕIåk“VÕ |“E'Vü×ÙÙɳû㳺îM$€C÷ëº?º{ù,1lƒª}ÖQ¾må¿ïî¸îÅýñÄÎý±‰“ŸJ>Ï,Ý}»ñµsGôíÞççŠèUz£†.^e÷p`åé,”ÚÔ×HD} G·ìO$ ¡'‘W…8ßrw*7Ðïÿ¿½ë{n7Â}•ÿ ï¹±%‘tΓfÆI.s¹™4©ëk‡–èDYrI*õ¯/@$A.€ÅÒ– ¼ÜYÀX‹ï[8xWƤ†áÞ™Áb¾Y©$»ÈMá#h|@•ëé|R*¼Ü!P×ý…”ýVB8ê1~ $ƒ„Ëv³»+¡M­ý±­½ßns³Ým–Éòä¸>@ þ3Ñì{ÝšN7!¸„4®ÄÐi ì­O‘åß%Ù¦üÒU“òÄÐÒSÓeú53)gûÍÚh“’“I¦¥;2y'üìå+KÈPõ&Y»RvAU.ŒÅ Y •>H3&sŒ™fM¤¨:ÜBk¾ŠÇò1dݤ?"p)™ôQ?!1ûâQë /µ\ðâPËG-®ÛÉ•?`¤¤ÉmšdßÞ§¥–÷]$4Jˆt5Kñ˜Ðô©Šb5Ó&aŠÄÎäÎzT †hIU»`QYsºÐKԣݮËÑ}²ê{ØL“ú‘ï>¡fà ºQ½†]¿¨dãÀy÷zùîxÃW]ª™ß‰B_ÍàÆOYþЫ›Ó´^ÍÝÑÝ—IÊ‘ðù6ÓC‚§Í?ýÔöÿÜ‘}ç*½Íƒÿ/½ÿoŒŽ?û±hRĘѼÿa6õï?’Ìýé$¡iW«ÿU ‚Mªt®½ ïÁ·<>úÒ•f¼qÜ/¼Á.zQÌß‹}.N_Í- A$0úF{Ç ѯ‡ïè Q-ZÚ¢U‡Š¶ÿ³Ú‘¾ß¦wïJ/Ì?Nþ»‹×«Ûu|ÙLl-í‘V@ e «¨À­3ÊGYÁ?,ôàîaâFbôºwÍѨÊ_Äý¨2¸î€ þ¥[&ߣ&hôßòÿ¢R*Ćá9,½ÓšÛÙ©¥Ô¢l>šˆÇô¢;Šzz¤Aòá–±w7Ù"]݈ÁÄ4Ø1Ë4¿£®p,†Ç ÐOC¶‡˜çáæ¹ §þù¹ôsî4†éÓŸÓGoƒ.^T[žGሷÓÙœ³²· ×ज़¥’[EÍúÕ!ñ60 Þ,j\xæŽx:ÅȇSÇÄÛÐ)X>œÙoC§`ùpnK¼ ]ÃÀ–xº}µ ÷lÓ0ï6©ˆ·Q 'ÞFnƒ7D¡‚x9ÝðD‘;âmdúÆPs‰Úoß9fjDÆéÔX4GZ€ÿwœ^n…ܧQ6ØŸ_@‡€'¡ï”I ©¬°B·AûCèÍ “6tºõ _Áy MÃuˆ÷ ¿¢ ª1·Ï-“63Y&×íË“ÑT—I9ݬD³!fUd¶…bá/†ð‡eÒÎ{¬V3Å-;8C’i§{ð`:ÐÁÏéìfŽÎ}N·äÁÜÎ¥¸}Py&&òLæ5æÑšíq°tYÍ-N›ÌHT„à2¿%A´åmÙu0órïI=mÙ ¤ªuIqÏéªüØx›ëÛIŽ‘ú9Ý~M‰*ì¸SxÒpçà–½¶køURŒáÇ8ýNŽZÎKT7ß®ý×yœïj´§oÔ?¶7¼peWŽë‹éXŒÐº ÉêâFݶ 2™CL¨ª¾ÏIzë´}C´þß%bߨŶà×wŠL¦qOõˆÅ¡ˆž‚$ÇM§gçÓ.þkž{ü×É9þ Çá”ïŽTüLýÒ†à,^Ô‘Cè,Ùˆæ‚t{äŽfCRECÍŽFÁšÕ]sf±@tÔZãÛ<öÌcÏž-ö¬‚ÐhÆØ3ŒtéI{¦¨eÑÌG3ìJ¾˜»Åy5ú ‰£ãHäè MÃ;†jU÷ÉS u Ѯۨ_rýÖh Ùh8zMÐuêHÜ.Bj'éxƒä’¸3›"kû@§È MmI*O•ÆtD=&•ië¬çU²R›Ž(]@^´Ô§ô7©4mµ™ {DŠÓÒ[ßugª4]I§1æ>ÔV®àðú¨ý¡úPæiyAŸo’ûohÌÍÍ×ÌŠþ«òÿÎfgAÇÿ;=÷þßqÒ#áÿž²™¦M.ÄÒ¨­DtÓH=r&•¬®‘þuzòZâ6äéòÚ4Ur¶{UéAªãëO—Ÿß”ù˜ÖÂÏð¢¸`dùžæel‰ ºC¤=j¢PýwÚX e¬¬¬É@ÿ¼\à‡æ—F©¨8H³l—{mÍïè3½äËGÚC} ¯N+‘å±Û­h&³DÌ `LU4Jô«- KjmàIªÚZ@&L-ä¨&a¤ *dè²ìg+/ÐOÙG>e…¬( 1ƒ S“ãÔ$æC¡j– º$ô&ԢEЕ0Ò!^ ¤aäCH'z(£Us~¬­“ô$Ì”âh_Lz¨Ae×ÖcZ_ïèñ}¬µ&’|°j䌯”sYk9IãÁvk啞ý&­ºÅ=СÕâšX+«+ñ`Õö\¶´‡w sî°ë•è'1Õ°3/ßkþBá¡} Ï9añß?³¥qŠ÷¦aïýŸÙÿêýÿÃ'¬ÿ߇IôPUUu U}èÅÿ7•ýopð6u¨ø?³°wÿ;ŸÏ¼ý#aíé__%ë…ÕÔð²êÄÈñÌn„96IsOK§¾ d=8mÊ¢yevމ#¾„¦+ºP+jßíV2ã判Åyž®nvy"øŠó¹å_ü£IÓ@ÔÇ[d.¯Þ¹díê¼€¦ñ͹Í?¸¥úýŸ{E*¨þ=¼¤õ}[ܨÛVrŠÓúe æÑ,i™õvñýzõ¿ú‹´Þvžl>Ž‹û7»[º<[åÔßÔÍ6¿ÏõZGÙëu².WÎðu-ñN¯ªlwóW²(Ö+X½ò¤¨©yÕ÷ÚPDVД¥-&³§FÓ*¯ 7;"µþË.T›éR·è¨ë”©ÓÔ¨0ö›Ê ”»†²ÐNêá‡z™¦ñ^ÑZÌk§ÂšÛ Éœ¶Ÿ63Ùs+U`;d•±.Úª¶o½£Ûþû„¬ÇMþ¶z¬Ü­Ž›àzúÁ¢.86w²P*z-LÛ¥¯¨“Ó­æ RÀhFšëœóAèA÷GëW¾ºK®óøîžYnw«¯ød‘Žß'ˆú©±*K½Ù“-&›qi²ÔÙLmó˜îoˆ”·ÄJæPQÇÌÔ~dõ ¡pJ0¥,{Òv£3}5úPÞVô7à{T2§ºk‚.ÉtU\MÓ÷Üh·K›Ý»ÑúAKÔðmVٷ·V÷*ÉÓ=¸±äÆÄ¢¢¤ÒkµÀRëçüœ”cDÖÒñÕjA©ï–²Ø4êÆ}j*áhÆÉb›-o=Z=qû í†ÀÒÓRå¶½´^<:à(ûä϶…ýÙa=´¯ì)&¹ÿ·b„Yz€U÷³ çÿf÷ÿŽ‘ ÿ§šiŽè?ÎÀŒ{äŒıçô­«›uúúU¥©ê+ß;iRÑ ZR] (F²J)½ˆ¡Bâå’w[ª¨úIXÛfùÅ˳—g4ˆSÌ<;-À"´Åä?Õ2Zªìÿ_›ïÃÕQØÿHüþëyuÞÿÏϼý%e1Ý•eMtµÿÄë5o¹.ãOc¨]®×Ìä~iÓ¸ˆl\Æ´OZš´´2ØúYøZOj§ªA=Ê'¯Y€êÉ÷üâ Ù©“ücÒ<—ÉšÓv@&dS¡‹_ ¹8¼¦rØEˆM)ì\‹Íªêœ.ŸÕ¸Kª¾p¦~ìhêEšÄyÒlí§,/ÑOÛ˜¶Õ`]®ÉÐ-÷¿ý\ey¦bÙ‰˜­4¼&Ôl?==ÛÓ³==[‹ÍtGŽˆNLV%Ê›ª0Uã|ñ­¼n/æn”ä«b(D÷3=Ø0á ªV‚ Õ/ vÒšºy’åŸîgN¦,“uÐSUµÎßì7˵›Ï'ð ÕÆo~Š™]vK²EêåB­‚n/—ËÂ%¯¯’Û„1“ìòùP>†°>T9d ôêµ­íz·øV} $µôréK/H{C,άsDÿ=Î+[°ÈŒ³Ä u]Çéj½ÿs+I–¹eY®è~_væèåy`«?uhõ§‡m¾\EŸR;úuâO=ReËô@cPYÅáPŸe­"qÀàj4—Q¶ôgNœ-? F›ñrédÀ©œ'3¸Où°þv»É)ψÞõ[2©»I’UÛŸ›Œiâcrw“¤ˆ¼ô6–ìéUEPõ’C9œâ®u³:ÝNçÙþ¾fþ÷÷vŸÑùG?vŸ¢÷ݧO»{ßNÃp(Ÿo|þÿïaä4´È ü[ÝVg @¿ \ÿæÅVw~Úú¿—ßoàÐõÝ•Š€ïŒà‹­£”b¶jÁnãxüb ÿÿ ÝŽú·päD-ô) œq+oÚ˜²Úø{›UÁŸª«à_y•¬üýý}ë~ÛítºíOoϯHYÑŽXö"Þ¹}ø©õîôš‡ ¸ ÃJJ<Ê*Ü~DÁ0nõƒQ õÈÁv â·NÜ¿åëá/Õ£&õ·"οCÆNŒ¨ºé-4ŸÆ“1ŒÈŸèB) GÐ_áèÄ‹_lý‘8ž;táÀÚ6i6KI5A©çÌ¢žLW¯&gWÇAâÇáä8À¼>iavìÁ‡k5÷!þ‘ éß ÝŽ\ÿ¢ßOB„Dà#çýênQhÇG½ õõb µ‡h·@[츭ì¿VÁŠßSp¦DÌ{„{´ çŒ SÐÅ,æŽÝxrÒ/˜lç‹´~¡û%FÍRÑ• º‘Ö „)ÙÏ™ h§K‡†ÅO%LËÅè»ùïn×ñR¢c©xׂHˆõ仞çô<ô&“’Ù#0.ïÚ¹|wˆ¤µÈ¹6;ùÏ|6‡c'd€¡?Ñ¿1R¶˜´ˆàé˜ uÞÃ6…i*/’¸q3JœÒÂæÕ6‹òLÓ`6ƒS½pao9[·GfÅÞ H–ˆ3ÜN§˜^Ž5FüÖhÍМj;iŒÝü‚ô\4ej$š+¢AÙ«`0‘5}ÿ…µ\Š c•§IPfòZŽƒr)}únç8ûܪ*³ˆÙL÷s{qå]Ñ’ð É¢™€óç³ÂGõz,ÁÂLùÀœ)@Z‰úQχ47zÜ*ÖÏDð±\’5ŒaHÀ¨gÀ>ýCʽDS hu€ >ELÒè]ÚRö ¬%ûI*¾y{ .†Ã5ø>ñï 8‡þM| \¼†0~Â^Ÿzð.ûrŽ~ÆÉâ'ð&„PxõÖõ“þÕìþ@(õ®}õœþÜ”ø2mLz™6Ç/ûpÚþz€0 èêAÿ¢÷‡m‡j]œ ]?Ps5JÄœ"$qí¦0%rªxF/–Tb®^Ó- á=!  š`¾:Ò)÷õHI«ƒÛP“©…zMCÖ4DÇlK;FŒ Š1394Àu”fé5­ØÒ ã3 íWEóM=£°µhiæ°Í„¡2ш*bkéèÑJG¹¦ß„€Ä)îki¹(i^2Ò´Ufø[KJKAI3‘”ŒéÆØlº–—–…bf'/MËmÔFç¤&l´Z‹MZl¢vѦä&fæ\ NËEKóœ¦"©ÒÃ’µè´´43ÑÉŒrÌšÖÂÓ²ÐÌl…§©8Žæ ÎX|ê¹>¾h£8Žc§‰Õ!¾årÀ*Ç¡ãG¸HõÍü} Dñ;Y2DràN+§¸òFðÏ£>þ³ìöLÛÐR 6›ýìg†¡`0I„*ynŒ€ò„:m±vÛF„Z³‘XŪ­òq ÖxÓ®‡Î*ÇÊÇD«¬Qºä»XEd¶)?.áìì4¡ÀÜåc‚>ÖÄ {ˆ¼ñµÇÆ5é oÄ úN:q¦Ì¸lBHãIèâ{¤p`7ø6(¹8úiäìéœ\æÌt­1¤õW_$3P=j̼ÏW!VÌК^†·Äˆ1elÄà§äl‚)PÁÚ./®V†·|"e%Æ×—ÒÁ:ã±ç¦}´vЖ·ƒÛÞA3Ó³Üо¸ª¨\0Ÿ\U´- 7] ôI<—ÊÏ"ÏÍÝÙ9µ Ð&tF&ô:ƒA£ˆÃ±Rxhù0Îbç8Ñè7sà—zx2¹W’$ú²¹ŸT馥‹ß·f×a›–G¿Û\àô{ÑñrÛ“Å:9þGØ»Jq5 ª<þÓÓ§{})þÓóýÝýuü§y<ŠøO$ÜÓÿìì€W'ç`£~èŽÉ† AJ&8ÌÑvµF.àèò,jÊÊ‹'$îŒB:l#ÏC/c\—‰’Þï°v¤€5Ü¿uüœÜCT±Hìãðm]$!ä’´ "A|‹þƒá(ÂHÐKÚÚÏÀ(  d{f ìì¼T‡»"Cº‚NØ¿UÄAJBÿàò¬}ïŽÑ:^o+tØYÈ(´³âMF…¢*ØU÷çŸV»ªJ«vä+Ü–A˜)›xT„âNDQ&ý8A ž¬&9¾ãyéIHoüFéndá­66&Yx+ ’%Ž-g›ôšvË… tt4>,§Ñ‚iI4ú3ý-Fƒ8AKÅõ"¦J¦„t@ß…¡3Ùj³ÖÚBséO1 …:Ö9dBAEðœÞ´j—Ô8Š\ß² E‘аæ1")/¸±¬õ8¹¼ ‘1ž}q®f»Q,7¾5¨ }9,Ô&o«k¿Ûbø ÑxzGNè½®îòÄ «.‚ÌÞW·p} (ZÓïÓ=±Ûé®_ó­ã'hçÁŒ'´¬zââB½Ò²æÙȹh]Ð"«Y÷-¸É¨fåsÌÙ,랣á^†d§²ªw‘„uª}@›@zïá0 {nt[¯úqàyˆpÝžëT¿¾uÃÁ%Rs'ïà}®Fïÿ«e½WapAEF?:çeú%™Ö|ˆóœPq3ý¦QÁmK‚~ož;&,V8)_ÃNðj’jæ3[å¢L²$³ºêæm ß4"D)ó›F…hhú¦Q!Z̾iTÈ6‹%AFÁ¢²œXl‹i¥³A-cRÅv:Õ¾È4^ó¡ÝÝ'QŒ`XË.’UVjàâ×OVT /9‹Γ¤Åž-h¡\%£‘Ú·ŽƒQêu?›u ´Û, },†÷sF¨oÿ€“û ¼ÇùR×+ÃÅñ5­hIçcÇú@hÄEú4¬;¶«6.7Á¨ª à}?ÔfVë<ß½í'´Gê~ñíѽžÔ™N*öh°ŸNÇÞâ›Åð‡qßÁ‰õyÔšèm­85ȾÊζf`óŸKrQc.I½õ\.Õ\bÛQ©ìÛ»/­gr¦3É ö“9 5×ó¹LóÉ»ÚÏ騾ƒâz^g:¯ØRæÖuì'Õ}q×úRÍ&µÄ¹ñd.*ÝzjŠS“¾Í²YnäÉ,Ó{"oÓ T¹Â“^!×BÈå"zÿCÌ)Chéõa^ùÄ1ÊR… ¼5®‚—ç“©è8Ïr*öBÄ×}©/þªHeG¹a©bº‚:oa㡞Ýh±Å¥bœÅ"’—(g´±êov£ú0îW ªPBì3“Øt6Ãy" bÕL) Is%è§–½ÎptÄ”Q5:E!it‚%Ų׎kUƒ+–‘}±c›‰ã››ÝȘbU18u1±_YG³î{v£äÕŠ‘ꋊý«4˜ZpÌnÔ™0^1dM¹¢#¼ ÙÛw?»‘æ‚jÅPu•78¹·MŽKz—8@E‰”'…Zç/i_Òp 9êŠ^ÜC¦ŠbŸXƒÊ´…Шê:5m*áò±M –Šs:(u•*Õ n†°j„1”Åâð)„/CÈÔ•°Bé ¸Š”)ÂtR‘eŠ UHSÉ@¦ðéä-|Š Uð©¤Sø4›·¼bù*è[´!pÛ®F}µ PK¶WCˆKÄ!´ê*jDC(Ë6v˜š:pêöpC@K÷e¤ºJ jw`%¬B¤ítƒ}Eƒ*÷Xðþòø ¸º8º8ü øåúú’í¼bÈC~ã¥MÊŠ€zk‚vÓ·!9·· àݶßêKBõ–øhóSÉ [R°Ü,J†Ã,H ´r•¢Á<. ƒ§Í”‡`[è8ˆ¦•ÖS·¼S§ד¶¼“¦“”×s¶¼sV¢F¬§m‰§M¯]­§m‰§M«"­gmyg­R_OÞòN^©ab=qË;qå¶šõÌ-ïÌU¯ÖS·,SÇGÃOMy§þ`¸~éy™4Ÿ·É ÁkùÀðÃ]^aÚcÆ?ž^´ááÉð,¸o;ðûy™™/iPøuLøÙEXòûâ{¡á‘ë_ôûIˆð‰VÒÈy`¿º[´Ó³«WïØÑXļ£9(Ód(ʾðktø= r°¼‹Î<ÆVèäÝ#Ú˜ÇxOáèo¾“,…^˜çÔïxºs²³mÑT³ê1—+,׌+Íÿ›¨¸ mPX: µ©F?†cêèds;ê˜fpbárU¯T3EGI8õðìè·>7BÕ¥aJµUƒ¤Z{x³¥Õ Cº€xŽJO^å 1æêYwòY{Áþ|8ú  =å !/¼ƒÀ䯧©%G¼¸q‘D0ñ·xO®Xâl¨àÓÛs@µ|œÔÝq}|»‚(”O@ª>™šEþÄZI¤Äèé«uØáÉÀ”®°äâÝ=y%\¾¸"ƳÁ _bÂwÊ´V]-ê:H_œü—­Zð#CÅ VÞÿJÑéJ£-ª+€×`€“Óºðƒ½ îÜAšœ§¦tC2 ãߤGü©E¯ \¿&÷ÿ6I*Tþ Î=œS,[îÈé»>áOä²Û€ÒXNl¿ãü˦÷Ïè8õw …«èC+}MŠ£Ÿ;ä1­áŽÆÞÔ¸~<œ¾•úw1̱„Ô¸>ù .P¾Ä»! ÑéQÄ1‰Í®p¤[ )ÉùZdÅø˜ÐB®²ñ”ôx;14è’–k²OAÝåŸîxê~ÎUÅÉ`.]þ í«•\Yƒ éyвo¶P ºÎ‹6Òó9•ŸŒÍŠ6BBÇ|‚¡¹LëúbšyW9”v5 ƒÑg}wŠqPR¾Æº­&«¬h‚–NgZ„þdU™ñ¯-9_„¦3ó’餮t„wÒ™ÿiØb[lI>’šbŽË¶Î¦«-ލb˜Œ §©LÓzj7oã”0£z£ÆœeŠóŒIÍ® G*ˆ ¡Þ32/Œ’.x.c£ñʬDÏd Ç]ØÍJ6ºštMyÆ4t-Iz™Ã”®e–W ë®fº•Mµœ¥í°d§4^вàT&T• ¾Ë¿3&/Zà a£›j€¤Ú„ƒï颬›l,âJŽ'e§“V&eK#Âç…µƒl‘ަÖ^…Ø`ùxÚù€”dTª¾¬Ø(©9zªº!’Q‹ +†OªÑè-+6Ð)‡9 MQ[1<ŒÂ.³bƒœ‚X¤-qj‚QkŸ+†O¢ÑØeVl ÓlOLÐnj{RÚóV Ÿ&Û“Úþ±bJ®aºJc‚ÒŽ·b5’lÔvèiíÐwÌU#ÓõDwL×cÞ¼ûd¾‚ê'¨†JË K½/5NDí–xäceÞ›ô7ïa°v0XÌ“ÿC½™®ˆÿ»ÿ´xþ¿»>ÿŸÏ#žÿow[måq?a"Ûhá·¢I»>Œ[8 Z”l^£N^Q.µ]pØf¬™ÕÍBßâ?ØÛ6n‘b{NÂ}oWd UðAövÕA¶P#ö£©õ£Žº N;í›ö"±9§¯ÙÄz#¾ßú0ÆÝ6¿_¤‘Œò½”n™„̪àGç÷Q:Z±‰xee¼ª’ÏÅl¶îÑÆBQ=æTlû&\í¼¡¨ùàGûê~Ëé’›²éN#2 ï‹’MmE'bÔ­qG¶²x[Ò Æq|‚]»¿p–v±i¡B®iW‡BFéjaÇ ·t|rcÇõ¢z€gÕù®Ûùú}¹9—U]A K¶\ WœbòŠïèL¿,Ì)ïø~sJ¡ºfjÃ@]‚”B|ïàwçÎÙ‰ó޵®šöDâÐvI?—€’ µ§•HçIlWMÛì©â%]!MÞád.!]§6˜KW4èk“=QN¡œÌô>Ñd9ˆœtŽ«B[,…ë·óŠõò·¥Y0êð—BˆÌŠÁRìk6($¬hö§|}¾=ß}¸íúwöÞOœOý‹‰×»~óî®wõóŸðÓ»zç Žþúùc÷î|÷óäóÇ_¿þËýùäý©—|ýô¼0ßzïï~g¾H‡ñCc —á]ZW ˜Ìu›s-´¡ð /®³i›WµR%)éú,‘­H$ÏôJRÏ<Ó·9¬iiBƒp#œéãå?q™ýÞÛíÜõ÷¼?oÝ}úçÝç7¿Æ½ÉÏ£þèWïó›³»vÞÿ½7ú|‡è÷í¿vŽÏ÷Þ=íï½÷F¿vÎwß{½?Ý}öÿî}þøÞ=ï¼;}ÿæ×ο>zQÿÏèÅ<—_>Äf>¬omi%m“å©Ï¿•%*)v;a®C<Š‘ g½3›·ÙeÍË|‡,èlßÚ2L•CÛUHUÊG²ÉhÖk°Ø¼Ý4q›%(ªðßÜ $Æë˜šË Ä£Y¯Àbó–+·ÌX­@Á¤ó¸V`ñ.3C¡p›yûïh¹éÎ`òH鲉‰]q.µ2åÈ/ÜfÎ0ƒ‘˜¡ýwð¬óS‡b¡­0‘±ëËÙ‹² Ì‹¾ÈYóÉîÿÞxAÏñ“ˆ5{ ¸*þ÷³îséþï³gëû¿óyÊã'ñÄÿþ~ó°pxëÍùÅ«£ó,r4^eÕ÷­ð÷öV^¬ŠAtjV´#–Í‚|j½Ã™›¦Ç²J#·Q0ä¸.NáÑŽáCüÖ‰û·|=ü¥zÔ¤þ–>̺vD&wÞH¶‡Ô±eÆ3»4h—DòÎ}ã)§ÙâsŸD‚oä÷›Úóa›ù/Uv…Ó¦Öê+¿wnÖ§4‰µ†)¯Çú]Û[·ìPž'ÚVô^½—¥«ùƒPœ¤¹ÂxlØu"!®ÓIÝÝŒÆÆ!¦/^²ie™‘ç3¯%8˜ÉÄ67:)éÁ¢ºrl²,}Aî”2~£"€jÁ „Ä*AÒÐ{Úð~GõÉÈ€âH$÷}zË<òâþQF^Úl²—‰FŠÁFÚ›€~Z4øã0A%Ú$7¡Uqí¶R=zÍž`œA¼Œ`äD¿KO39O6&ޝ>ʱÁ~iŒ~„ì¶+A@”ù[Qcà‡„Œ±P²‘˜Å¡¸iå ”õšÆ—Ò,wƒ>«»Í–¯Õ[ڼ³‚iåmiº6’ êÒ "«4s¸r×Õ½¼)+2†¿9­H^$àÒœá’V®Fó‚3³Ûcé€5úùRÜŽ°¤|ôåÛµ‚´ª“ÑÏ‚¶V"¹øâ(p%ÐSš{] CUâu…LÀç׊@wô­ÞrHfpñÀ×0‰¹š‡k¬•l}&p3­—r…YBºTiÖ³óÿ³_ÐXƒþ×¹çÿî<}ÞÙ-äÿîtÖçÿóxçÿ…ã~ËüÜFgôé:eTGW„þ|9†£qº¤©-E@fÕ÷:îµ½ü¨ûb+ ýƒäÜñoTGìÙ{ay=†l©%Ö@1ÐÔ5.D'}yÙãtËä>¦Ÿ_‹C—¨& çD$BüA¡µ |zBW8,GßœµÖKbB8ÌÛrp[\4ãÝü׋-„¯{‚Ôìßþ¡±k@hòÙ¡8 m=øoâá¢ÏVÍÊcSüA LÕp±SjMÜö~@åÛ4x;1¼ ƒ;×ïW;†×p8o´Í‹ðÆñÝ?…@ï4œ¥Qn´Ñ,Ån“­…Ðá­ 4z8üçÀ¯Ù¨feþâD'ÎÄsonã+Ø Ç÷zAàA§¸*mÚ?†a츾°"n‰”Ü–V´ÂX¥Yæ2s“xHo1wÑÏØ–½ëz¾{™øº Y:ªY»U¥ÎÙ›¡`ü«Üã¬`2öóUN´yWzæŽßÊÑÙ²Íñr÷ J˜äm÷!‚ÀtØCÐ!n‡×ß‘¾±ð®“W[…C]aÍÔuÄL1QåÁ»(d;©ªVÏô()uJ]$N̼.•Kª¦³%·bø=¦°Œæå^©Â¾ã ¯~q»ÔcsWµ]ŸÿÌã)?ÿùpýšœÿ¨Âÿ‚ÙÚ4>Ø,ÓZÃ(iÁAÒþпA²Ï‘$ëæàêâèrçôݯ&g8wÐC{u[®{lsþÃ×5ÏEŸÖùxurn„æ6908êÚTø>Zàx)VTCeZ˜­—››‡,ùàa ?Ú.Äé.Ôz¾YǬ—š³=Åt“Ì‹‡ÿ³³£M艈RwIÜõ•ÜÙÁqâKk‘Hr‡’3bº]1ðÌ3Ýþ%'ˬçe‰©Ž÷³D]ÿöo–¨ò°ÍûI€ÚEˆ²wi&ˆ*”L…™%AP(*‘¬¿üÔŽ´åxíG4„‡Âéš³¥‡ b„›6›õuèø“£ 1œ÷Í?ä*J[9¤Ã6KGÊììˆ!ˆfO:BêRº)ºV:þWEêÝÜ´©k-Ç—è©É#{j‘$7/™ÆHû—ôi_TahTIïuØä5Lü”ðPŸ_¾6dÝg³¨§»#Á«Â»RÀ[Š}•3¥ˆ\ŽW?79›¬¨ó“ª¯ŠN“EÅÚŒ×d9HGjÅAé•BUô:“|ÇÞâæ¯þHœ²½¶G¥ÿ¬{f)X$YÿÒ|gV7%KC†f‰¤o0ôà*Nz©Íà+â:1Bþ1Zê®CbÎ=&ߎÿ÷A·µÛz n û„V„‚ô9•6£.É“N ÆœJoWi%  [ý—.‰4Nk®ÇnJzìüoU­Î“鈅“E軽náþW÷ù:þë\žúúß§·)åüNy`PFdä–$Ú¨a8DExšS]鲫YõÐLãšÚywl|{ÎıosƒéeT13VËjá+eSLpªLn `‹22•¹®’ÑÈÁÇq14(æPB~nH²2þ¿.¬cž,oªÚî ~]âÿrS/nr^_·%<•ÞŸ×o`œô<7BÒûÙ‰y#xä0+‰üê¢òµz¢dÍ(…´òjaZnCV×—IœuN[ÃÒ„Jªº¥í‰ºÛ?DD£qŸ`Z¿tÜÐŒ*§¡+;Šžb‹ƒZÈ æ`Ìjö(¤)À¾¶2h¢#3ñmø‹ÜÚqöÇ$ñçT ܨŸDX6šª?ˆñ¥°ºÕãR9½i&¢Ön!7’$6D^Å¥róÒFf_Ú,BHà˜=‰sÉÆÒa¾¸5ÞÝ’¬<ÎJT£KâÛ'õɼãÕfvñJ¶kIn5ØGž'CÞ¥j[UXúñ!t×plyáùŒ‹ÛbIÀÈ‘ÙÞl)[ÙÝòb'ÝÄ÷58qÓ ‚k{Æ%’€’­ZЮ1tœÍ(›Y˹֖œ'] ¤”=§Vj¶+ÚªÍê¤{3v[q\ëîp¥vºÓpFT5ò¬°ˆ45¦JØnä ­ÀL6n¾&—°Ç‚ÀœK@,0ñÒÑ‹\ܨY›Qkcý…£ãè:*Ö•/_0ZFnÕM DQ.V@;(ÅŠÈÉu@Š¥Ê1 ñoƒ&5£åÓ6¤Ã´3æOÔ*8uƒAIj0þw%®åR¤Šš[Ó¯5¡"¸£2ÓÝsº34¢oÐ㼺ûÍRމ ÖÞÔš ˆ’]l)‘VBU*ÐÒ¥&¤{y³“/làK‰,£‰/ê‡K7«I׉2 neêêÒ!¯z(U‡—nk'ŸžÌÿç½C|}îñŸ÷ööžã?ï¯ãÍåy¬ñ¿0[Ðõûä*Ü'W=˜n@‹K÷,£òò‰Û4ªN„:"A–æÌ$¬“e“äöí‚2ýÊÈ\Xª_ bˆ0zè£"€9Å + ¡àa‰ßÃIÙ˜„¨&w.M.l*Ç<ÃÁZ†Éš.8”4ŽtÉ͕Ɨe•8–b™3HLÖ9ÅÞ"ºŠff¶ÒEj),u:_ëŽw®‹ë¯œÈíëã;ΘÒ/›/x¤_LÂÿÕhûêÞE’aà ŸÀ(žÔ¤q3°gùTA‹a‚HD&xìx;ÏãzlzÉ¿u=ˆ“4^Ô½i<Â*nÔl›¶mõ³;n~ø8ŽOó8?º>jÖóó³FÅì¤ñùÇ6?ÿ¸Õ¦çŸ ¿ùùÇÍ6>ÿÖÊùŸnÇ/SÕNÆÆ¥a_”n,;+Èq…Þø@Û¸j»0o«@;‹a‘¦ªãÏ–`±*!“ªsËè·e–ŒPi™ÂäÖê~:,ˆšžQèY…lb¥ø=M÷¶¡Ëd½š4Y¹|#âK–î.5a2\Í﬉´- –ïjÊÑš¯I•×Pç“«TéD|yS ­P§¼“ºx0ž‹âþT‡Öõº¸­tÏhêÓül`¼Ó¯Aý€¿±Eh5•«Pº7ÂŽñ='ŠR/ Ú" žþ”@rá÷$gfâÛB0poÜ88j,ö“‰HDÇŸÐ0´“vÜûšh±@*\/ÙV /‘Jµaëñ"YTMãà»9B\Àáöã 5ns œð¢®R)Õ+åæY¬P‹Ò€m m¼¸h4cÔ€ômèx°B›F%B†Ž.†Cìà„Ç~Äšé_yBß>jÜ¿uü+Ì–HÏUñéKߺÁû…Õ[•Ö`…°q&„r‰ù1.㺨‘åëõbV/Ió$å2scKº4-ÇŠ­i³LRøc]ÕµSÙ×ëZ¿4 ÉTÈá5sÊ%íܶ«—Â…ë€3Ì0SÎHJ®hüÚJ xsÏØ³•&ÐEèǯ FS _j ô â«4RÂ×0Ž©3•3}‚žªÓµeË~d¿¢Ç^<ì[ÁW,Ï’ÌL%§@K9x»µ&¥¿*Íí³`Ím¹­Df¬Å.ÊUEÑ—îJ ¨4¿š°€s{z~gQ!£óY¶´b:ʯ-æò kˆâ=¿Lg˜ L-hAµÎU6ch‹ùËJYëìáÕç4û~}OTx²ûŸW¿uÑ&NÆqÓW@Ëïvq0éþç³Î³gëûŸóxÌîN{ýsÓþúç¦ùõÏMåõÏKb»*F˜ÈGØ£·þ#Ë» ›5ï‚nÚßÝ,»|Y=:£Hòd)¾è¶gv1”¯ áËLD n?avô‹ÝJEiq!”¬ü#‹>«* õ[æ«­Ÿà3,8±£rPØ.ƒPö¡Ý”?²ëÆxÓPÉhX6|Š€ó×,8¼ª[,b‡Yα"],äï‚ë ƒ˜9_²Î ½@3ЧCËý<ª»#î$5ûã½,ýJÇYœ˜ ÌY¯ e/_ÄüDH稈o!À…Á-*ÏdþâÅëýå&þÛ-|©ä‡_µÉ;r¨€«Ü¸wÐéyAJ…S¡cåÙ4maiŽldäsµx &ÅgvŠŠ6 Ÿ¹uBéÆÏjÌ¡™‰·d(^Z¦% æóXXÜ%–[¡v»X©®µ›”êi€i± ¨ÅrÍM²HÆÙz¸‚ˆB€Ñ޼› tãÛÑN7_OZyqÔ6·pÜc€³Œ³$_Ÿ„úèã÷!#Œõгß9ŒòAßñð ƒÆŠa‡Á 铲V‘º³ƒ*quDƒû[·‹Q7€x\81[€Áˆq†÷è6H<„PH±B¦Îñ&> á6BøÃÓ¤wîL‚}ºÃtB˜3潌6šÕ_~<úë ÏÅÀh»æzÌʾ¢ý¢VȬGˆøI“iRyˆaÀ™åÅVpâa‚Œ–k+r&j½ÄÞ;>Áª€> Q\ð9zÒá׸ÇÐ@šG’Ì ^Ÿ€ÄG4?pû1¾¥“UAßq‚ýà¿FpÒ“mÊðúQØoù.Z7Á]› NíQ{èŽÓÿuêìtñ_äVüoS!úÅö—žçø_·qëǨêWp CHˆÂ!DFáA'¤Xrzˆç5…†‹©ùïÎsENô˜ÀS0Y{}D·ˆL†ø;P `ìü~Œ Þ0ZÉ×6áHíÑài;ºuºxp·ñÈSÂ+Bª^oˆ>"BVmYfA SΕj ØÃ ¥b*hAàûF?û!$‰;R ¤¸…‘ÑŠ¯À5tFùà[Ù DxCZaóçMhÚý?ߘJ hãJ$¸j¼}‡ÙÍòíŠX΀`9ëkßÀ“Ùÿ"U£1"™ùÚÿ:OwŸudûßÞîÞÚþ7GaÿÃáÞ¾ßÄ)¿I®lÀ唯=Ä“w½í\!‚”b‚°vvÒzÅL‘8=îUN^[ß‹RvÁˆTQžZà&Xº1,l–éQYÕÆf©mÀÒȨlÇ0¤ª®YŒ8rp‚g=5¹DD€ì£Ý±•M.ÂÈ‘Œ+– (%Mà¥X';EÂÝ+R¿å5©ñˆ?’Äupbt¾IòRJÉF’ _»#¸µ±T fŒ¸BPD[Ôk 롃Oº¶xl©šáØ–G˜¾¯ä³³|é¹5páE¼Mõ‘Ô8>ØAb9‡ržVD›•PLuû !þk«l¤r¥±E÷A8°ª„‡@º•Wâ¯ZU HL#æ®ÇÞµù$Òä„ƒÉ f"±‹ppó‚SM9á™17²ñ.ïŠ$éÎ A6ü(çªhA£CK—xãªÊsùíU.ß‹&4†€ 4«x ˜23õãÀõ‹kœQwј˜„šZU~12s‘¼ +€-ט!‰Õ7b&ß{QËߎ·*|7ÖÞ³~2ýïŽ=&!¤„Ó˜X¡ÿ!µOŽÿýüÙÞóµþ7G©ÿ©”¸"}X¤„:úÚ&éKë[éxµ‘¹S(òLb¸ßãó記]òOwÜçÂXå¯LÛÍõņ™À™·;ô'®Ì:É!¹2ß$.+${“S¯ñ#“¸©Kfb Un6dY˜Ck¤åšKËÆá§,ã ŸeÅ< ZØ Ùá9‹´)KU4( Ž¥È…Ân>ö“0$ŽÐy-|öÀNÇ>´®Z€®×¢q_J•"Q”(EA±&™R˜%pƃŸ:)ÆðWqJDÃ! ×ySÖÁ“ÉItø†Í'€©òÿ}Þ)Øÿ;{kûÿ\žGœÿåö(ÏûÔz‡ïI®|õˆ—F†‡3=Q?¦ìå=s&5ZxÆá»?»c“èè3Jä |aId(4Nµ³ÆëI·Í&"®0ÆZ¡`E"5Œ¹WèÖ>¬šFêô_î²b!ÔéÈ8’Ý”]éÃ6=(«ø|¥}IÚõ‡+ÚWU\> «T8N±{ßÀ™*¼aNv&ÄTªµØE´Q-¥êH6Ê•P7rR†.ƒ@hˆ1›¨^ºUÕÞª¢M-#âŒ'i—nÍ>âÍ]ñÕ wŽ¡|t³dæ…/‰iíâ>÷Ø¢¬X‡b.›ˆ®¢›—ÒHJCêeŽ®Qd3$OUcÖh^ýHÂU§½[㌕œÍèˆ7g*äˆ&·F(®Q%`ub4 [1º–¼g>žÁ´öŸÌþ‡¶îÝ8‹ôÏUö¿ýn÷™œÿy¿ÛYÛÿæñÄæ¹U7ü‡² ‹H©Åë܉ÏÞeh6.é0½¡ˆçˆéúü¬NÚ5ë!ÅåŒí†]­Ý0ï_Ìv˜½O= f‹¢k·Ø©ˆ ‹œ‡šÑÒSâÓ0 Â|¶{AàA§8D›–Ry`š–§HmnŸòø¨× á]£íšÚà­?E6=ÍL½ƒ&17Nx`°ïŽœbbO릑8Ø|Ë×ÁŒ@F Ïࣻ›AŒ[žd­½½j6ÅâeeƒÓ¥W”tã¶:h¡mÔ:ba¡yžëÊþXEÞ<íú,=J©7øÂÕR„HÆÇÊɶèK겸ñ[Ϙ @²Ÿ£j÷ ªi B®d3'×ÍSíÜ»8Aà#=+6““ôøÆ{ÆIÆ»Zü Ç5ÀõðøL£c`&âô'yd±—D`8ƒAz¿£SƒÚøḠ^Š}ðNp,€N?F±*`N¹­^\øÕziõÖ=Ò Sbpô-ÏŠÍ¡S‘Ÿ407U‡Mßúäl)¸]Í#-ž™å2€ŠÅÍñ8«HYœõ¡Í‹íìJâu…Ikâܪ8¥G)’ȦFí2ŸUÉÿ›ÇS~þ÷áú5;ÿÃÇJü! î˜¨?ì³ó/‡NùÞ‡ª÷jè ‡„8|YúšHœèçNDv‹<(NCÅÚGV·Lc?êf¥Ãq·x­7&IÏaÙÇüì/¬Ô€whŸG7¯ ø_¢f{”XÚèjßañ„çøõñ™G7ªÓ‡>§P™!ÕjóöÏCÚúƒ5Ù¦ÈoÑ’DÊ÷ׯ ñZHiΖÄ•oÓJQbøŽŒº¦×5=#V|æ¿ÂQµ}*jáè9œ WIσv`ê&@Ó+9>ænÜv4 T!Øn$ö+‹Ü9J·@f¾0ä‘fr±ˆ´–jëºÉÉUrÌÅŠÚRsT“×ÒYIƒRÖ"Ámù´H–â>þ,» À͉¨CebØX.—n[“þ•ÓÖàUkCbñX~ûÚ†&x¥)YñJtíÛÎ FŠ·G^ËÁ´¤¿¥^R@i]9€´ÞœG@:5XS‚RLÞˆš1[.r¤žlA*–§ù KN^–Š5Pê•èúÃÀé÷¡‡Ç„Ä35Ùú– §ò£#j¡,/®uÃ¥|òüOw¯&Q GÄ´A=°Bÿ{žÅÝÝÇ: ŽÿÓÝ[ûÎåi^ÿÃÌÄs’qbXb?ò@`­™k5µuDA ­¨°Ök¢¾Êi¥ÖÕˆÍôZ¥f9B(¢q­ó( ÉÅþó!ã Õ²ÒÇ© þå!ÚB‘&FHÐs°àA§ö€TG5œè%18˜|ö§2˜D€`1ûÍ+k¿ýû·cÛ\7¹Æ›!ªÀÚ-v$Öã°Rå#L;Ñ$ªÒ¦]¿“é(hèçÚJOλ¯°#äy+Š r@Wný”*G ˆ¢žŒÑ£;Ïn@Žæ]œ-$¿¾’§#Áy7pès É9᎘ìø0H"pîúí³§$iJ \`÷ ØÃÙj|ø_ýàÞZ$“EG"9ê€ÚÆ©gÀ‹¿8º$ D ®“&ÁÅÍÅ÷ÁÎÀáœ'8 IBNïSԢƷe†óUÐlüu‹A€ç  ·íÒþÄæò¬,$›Ià%,ÙI`‘JRàÆ8ï¾5ËÒ¨äÚËþŸ‡'`òüùßxí†Q EÔ8êv[ÿ{ÔÝmMÐÿ÷Z¾8î¶pîšÀdøDÅvI±]Rl—ÛŃqC¡Ô)µGJí‘R{ßoâ!gèDpQÌ3$¥>=˜cAÄxþóæë.þßÞpÜýïðú½‹_îâ—»äå~¹‡_îá—{ÿýïß”hæX…n—’‚Áañ,†ãšä¶~ÀMRH^pÞ‡X@ªH’)$â42€±ãzM¹’5;jåj¡”ëCeTÈ-5xéËÜKÃÔ4F&][ÌÔ¤aQØ8ž£2a€tliN– ŒÙ5cÒl4 CJ“†[éI0ØŒtÛÙJRòQ¨o˜ái\e›Qo“sÚ#…á© áìG¹­HZ—ÂÒÕ ÆÖ#‹9œÆ†´Žÿ¼~ÖÏúY?ëgý¬Ÿõ³~ÖÏúY?ëgý¬Ÿõ³~ÖÍóÿ|ÿËÇ€wstools-0.4.3/src/wstools/TimeoutSocket.py0000755000076500000240000001254612133745461021363 0ustar sorinsstaff00000000000000"""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 Lloyd Zusman Phil Mayes Piers Lauder Radovan Garabik """ ident = "$Id$" import string import socket import select import 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 wstools-0.4.3/src/wstools/UserTuple.py0000644000076500000240000001010712133745461020500 0ustar sorinsstaff00000000000000""" 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 , 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__ wstools-0.4.3/src/wstools/Utility.py0000755000076500000240000014377712133745461020242 0ustar sorinsstaff00000000000000#!/usr/bin/env python # 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 import types import httplib import urllib import socket import 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 location in redirects: 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.""" looseNamespaces = False # if can't find a referenced namespace, try the default one # 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 orig_node = 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: if DOM.looseNamespaces: return self.findTargetNS(orig_node) else: 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): NumberTypes = (types.IntType, types.LongType, types.FloatType, types.ComplexType) if isinstance(key, NumberTypes): 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 isinstance(key, 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 targetNamespace in self.data: 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') # ('http://www.w3.org/2000/xmlns/', 'xmlns') # # 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 wstools-0.4.3/src/wstools/version.py0000644000076500000240000000002612135774546020244 0ustar sorinsstaff00000000000000__version__ = "0.4.3" wstools-0.4.3/src/wstools/WSDLTools.py0000755000076500000240000016630212133745461020356 0ustar sorinsstaff00000000000000# 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 name in self.services: 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 name in self.messages: 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 name in self.portTypes: 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 name in self.bindings: 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 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 's, append all children of imported # document to main document. First iteration grab all original # 's from document, second iteration grab all # "imported" from document, etc break out when # no more '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 '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 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 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 , by creating a special temporary "base-location" attribute, and , by resolving the relative "location" and storing it as "location". document -- document we are loading element -- DOM Element representing base_location -- location of document from which this 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 name in self.parts: 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 name in self.faults: 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 wstools-0.4.3/src/wstools/XMLname.py0000644000076500000240000000476712133772235020067 0ustar sorinsstaff00000000000000"""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, Also see . Author: Gregory R. Warnes 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-Fa-f]{4}_', fun, retval) return retval wstools-0.4.3/src/wstools/XMLSchema.py0000755000076500000240000032514212133745461020344 0ustar sorinsstaff00000000000000#!/usr/bin/env python # 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 import weakref import sys import warnings import logging 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 ns in self._imports: 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 schemaLocation in self._includes: 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 attr in self.__attributes.get(ns, {}) return attr in self.__attributes 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 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 namespace in parent.imports: 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 ns in self.attributes: self.attributes[ns] = {} elif value in self.attributes[ns]: raise SchemaError('attribute %s declared multiple times in %s' % (value, ns)) self.attributes[ns][value] = v elif not value in self.attributes: 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 k in self.attributes: 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 k in self.attributes: 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 k in self.attributes 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 a in self.attributes: 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): """ 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): """ 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): """ 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): """ 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 element information items. It represents the abstract notion of a schema rather than a single schema document (or other representation). parent: ROOT attributes: id -- ID version -- token xml:lang -- language targetNamespace -- anyURI attributeFormDefault -- 'qualified' | 'unqualified', 'unqualified' elementFormDefault -- 'qualified' | 'unqualified', 'unqualified' blockDefault -- '#all' | list of ('substitution | 'extension' | 'restriction') finalDefault -- '#all' | list of ('extension' | 'restriction' | 'list' | 'union') contents: ((include | import | redefine | annotation)*, (attribute, attributeGroup, complexType, element, group, notation, simpleType)*, annotation*)* attributes -- schema attributes imports -- import statements includes -- include statements redefines -- types -- global simpleType, complexType definitions elements -- global element declarations attr_decl -- global attribute declarations attr_groups -- attribute Groups model_groups -- model Groups notations -- global notations """ attributes = {'id': None, 'version': None, 'xml: lang': None, 'targetNamespace': None, 'attributeFormDefault': 'unqualified', 'elementFormDefault': 'unqualified', 'blockDefault': None, 'finalDefault': None} contents = {'xsd': ('include', 'import', 'redefine', 'annotation', 'attribute', 'attributeGroup', 'complexType', 'element', 'group', 'notation', 'simpleType', 'annotation')} empty_namespace = '' 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 self.logger = logging.getLogger('wstools') 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 k in self.attributes['xmlns']: 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 sl not in self.getIncludeSchemas(): 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 k not in getattr(self, collection): 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,?) self.logger.debug("IMPORT: %s : %s" % (import_ns, ex)) del slocd[import_ns] continue except SchemaError, ex: #warnings.warn(\ # ', %s'\ # %(import_ns, 'failed to load schema instance') #) self.logger.debug(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 import_ns in self.getImportSchemas(): 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): """ 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 'schemaLocation' in self.attributes: raise NoSchemaLocationWarning('no schemaLocation attribute in import') reader.loadFromURL(self.attributes.get('schemaLocation'), schema) class Include(XMLSchemaComponent): """ 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): """ 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): """ parent: complexType, restriction, extension, attributeGroup attributes: id -- ID name -- NCName, required type -- QName form -- ('qualified' | 'unqualified'), schema.attributeFormDefault use -- ('optional' | 'prohibited' | 'required'), optional default -- string fixed -- string contents: annotation?, simpleType? """ required = ['name'] attributes = {'id': None, 'name': None, 'type': None, 'form': lambda self: GetSchema(self).getAttributeFormDefault(), 'use': 'optional', 'default': None, 'fixed': None} contents = {'xsd': ['annotation', 'simpleType']} def __init__(self, parent): AttributeDeclaration.__init__(self, parent) self.annotation = None self.content = None def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) for i in contents: component = SplitQName(i.getTagName())[1] if component == 'annotation' and not self.annotation: self.annotation = Annotation(self) self.annotation.fromDom(i) elif component == 'simpleType': self.content = AnonymousSimpleType(self) self.content.fromDom(i) else: raise SchemaError('Unknown component (%s)' % (i.getTagName())) class AttributeWildCard(XMLSchemaComponent,\ AttributeMarker,\ DeclarationMarker,\ WildCardMarker): """ parents: complexType, restriction, extension, attributeGroup attributes: id -- ID namespace -- '##any' | '##other' | (anyURI* | '##targetNamespace' | '##local'), ##any processContents -- 'lax' | 'skip' | 'strict', strict contents: annotation? """ attributes = {'id': None, 'namespace': '##any', 'processContents': 'strict'} contents = {'xsd': ['annotation']} 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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 not m: return False if isinstance(m, basestring): 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): """ 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 not m: 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): """, parents: complexContent attributes: id -- ID base -- QName, required contents: annotation?, (group | all | choice | sequence)?, (attribute | attributeGroup)*, anyAttribute? """ required = ['base'] attributes = {'id': None, 'base': None} contents = {'xsd': ['annotation', 'group', 'all', 'choice',\ 'sequence', 'attribute', 'attributeGroup', 'anyAttribute']} def __init__(self, parent): XMLSchemaComponent.__init__(self, parent) self.annotation = None self.content = None self.attr_content = None def getAttributeContent(self): return self.attr_content def fromDom(self, node): self.setAttributes(node) contents = self.getContents(node) indx = 0 num = len(contents) #XXX ugly if not num: return component = SplitQName(contents[indx].getTagName())[1] if component == 'annotation': self.annotation = Annotation(self) self.annotation.fromDom(contents[indx]) indx += 1 component = SplitQName(contents[indx].getTagName())[1] if component == 'all': self.content = All(self) self.content.fromDom(contents[indx]) indx += 1 elif component == 'choice': self.content = Choice(self) self.content.fromDom(contents[indx]) indx += 1 elif component == 'sequence': self.content = Sequence(self) self.content.fromDom(contents[indx]) indx += 1 elif component == 'group': self.content = ModelGroupReference(self) self.content.fromDom(contents[indx]) indx += 1 else: self.content = None self.attr_content = [] while indx < num: component = SplitQName(contents[indx].getTagName())[1] if component == 'attribute': if contents[indx].hasattr('ref'): self.attr_content.append(AttributeReference(self)) else: self.attr_content.append(LocalAttributeDeclaration(self)) elif component == 'attributeGroup': if contents[indx].hasattr('ref'): self.attr_content.append(AttributeGroupReference(self)) else: self.attr_content.append(AttributeGroupDefinition(self)) elif component == 'anyAttribute': self.attr_content.append(AttributeWildCard(self)) else: raise SchemaError('Unknown component (%s)' % (contents[indx].getTagName())) self.attr_content[-1].fromDom(contents[indx]) indx += 1 class Extension(_DerivationBase, ExtensionMarker): """ parents: complexContent attributes: id -- ID base -- QName, required contents: annotation?, (group | all | choice | sequence)?, (attribute | attributeGroup)*, anyAttribute? """ tag = 'extension' class Restriction(_DerivationBase,\ RestrictionMarker): """ parents: complexContent attributes: id -- ID base -- QName, required contents: annotation?, (group | all | choice | sequence)?, (attribute | attributeGroup)*, anyAttribute? """ tag = 'restriction' class SimpleContent(_DerivedType,\ SimpleMarker): """ parents: complexType attributes: id -- ID contents: annotation?, (restriction | extension) """ attributes = {'id': None} contents = {'xsd': ['annotation', 'restriction', 'extension']} tag = 'simpleContent' class Extension(XMLSchemaComponent,\ ExtensionMarker): """ parents: simpleContent attributes: id -- ID base -- QName, required contents: annotation?, (attribute | attributeGroup)*, anyAttribute? """ required = ['base'] attributes = {'id': None, 'base': None} contents = {'xsd': ['annotation', 'attribute', 'attributeGroup', 'anyAttribute']} 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ 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): """ parents: attribute, element, list, restriction, union attributes: id -- ID contents: annotation?, (restriction | list | union) """ required = [] attributes = {'id': None} tag = 'simpleType' class 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 __new__(typ, 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]) return tuple.__new__(typ, args) def getTargetNamespace(self): return self[0] def getName(self): return self[1] wstools-0.4.3/src/wstools.egg-info/0000755000076500000240000000000012136000550017653 5ustar sorinsstaff00000000000000wstools-0.4.3/src/wstools.egg-info/dependency_links.txt0000644000076500000240000000000112136000546023726 0ustar sorinsstaff00000000000000 wstools-0.4.3/src/wstools.egg-info/PKG-INFO0000644000076500000240000000402512136000546020756 0ustar sorinsstaff00000000000000Metadata-Version: 1.0 Name: wstools Version: 0.4.3 Summary: wstools Home-page: https://github.com/pycontribs/wstools.git Author: Gregory Warnes, kiorky, sorin Author-email: Gregory.R.Warnes@Pfizer.com, kiorky@cryptelium.net, sorin.sbarnea+os@gmail.com License: UNKNOWN Description: WSDL parsing services package for Web Services for Python. see https://github.com/pycontribs/wstools.git General ======== - Homepage: https://github.com/pycontribs/wstools - Mailing List: https://groups.google.com/forum/#!forum/pycontribs - Package: http://pypi.python.org/pypi/wstools/ - Docs (TBD): http://packages.python.org/wstools Credits ======== Companies --------- |makinacom|_ * `Planet Makina Corpus `_ * `Contact us `_ .. |makinacom| image:: http://depot.makina-corpus.org/public/logo.gif .. _makinacom: http://www.makina-corpus.com Authors ------------ - Makina Corpus Contributors ----------------- - Sorin Sbarnea CHANGELOG =========== 0.4.1 (2013-04-18) --------------------- - Added tox for testing and disabled the tests due to broken binary assets. 0.4 (2012-06-26) ---------------------- - Replaced print() with logging.debug() or warn() in order to allow users to change verbosity. - Added release.sh script which runs tests, pep8 and allow you to release only when these are passing. 0.3 (2011-02-21) ---------------------- - fix url - proper release 0.2 - (unreleased) ---------------------- - proper release 0.1 - (unreleased) ---------------------- - make wstools as an egg Platform: UNKNOWN wstools-0.4.3/src/wstools.egg-info/requires.txt0000644000076500000240000000001012136000546022247 0ustar sorinsstaff00000000000000docutilswstools-0.4.3/src/wstools.egg-info/SOURCES.txt0000644000076500000240000000147412136000546021552 0ustar sorinsstaff00000000000000CHANGES.txt MANIFEST.in README.txt setup.py docs/license.txt src/__init__.py src/wstools/MIMEAttachment.py src/wstools/Namespaces.py src/wstools/TimeoutSocket.py src/wstools/UserTuple.py src/wstools/Utility.py src/wstools/WSDLTools.py src/wstools/XMLSchema.py src/wstools/XMLname.py src/wstools/__init__.py src/wstools/c14n.py src/wstools/logging.py src/wstools/version.py src/wstools.egg-info/PKG-INFO src/wstools.egg-info/SOURCES.txt src/wstools.egg-info/dependency_links.txt src/wstools.egg-info/requires.txt src/wstools.egg-info/top_level.txt src/wstools/tests/README src/wstools/tests/__init__.py src/wstools/tests/config.txt src/wstools/tests/schema.tar.gz src/wstools/tests/test_t1.py src/wstools/tests/test_wsdl.py src/wstools/tests/test_wstools.py src/wstools/tests/test_wstools_net.py src/wstools/tests/xmethods.tar.gzwstools-0.4.3/src/wstools.egg-info/top_level.txt0000644000076500000240000000001012136000546022401 0ustar sorinsstaff00000000000000wstools