View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements. See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership. The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License. You may obtain a copy of the License at
    9    *
    10   * http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing,
    13   * software distributed under the License is distributed on an
    14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   * KIND, either express or implied. See the License for the
    16   * specific language governing permissions and limitations
    17   * under the License.
    18   */
    19
    20  package org.apache.ws.commons.schema;
    21
    22  import java.io.IOException;
    23  import java.io.Reader;
    24  import java.security.PrivilegedActionException;
    25  import java.security.PrivilegedExceptionAction;
    26  import java.util.ArrayList;
    27  import java.util.Collection;
    28  import java.util.HashMap;
    29  import java.util.Iterator;
    30  import java.util.List;
    31  import java.util.Map;
    32  import java.util.Stack;
    33
    34  import javax.xml.namespace.QName;
    35  import javax.xml.parsers.DocumentBuilder;
    36  import javax.xml.parsers.DocumentBuilderFactory;
    37  import javax.xml.parsers.ParserConfigurationException;
    38  import javax.xml.transform.Source;
    39  import javax.xml.transform.dom.DOMSource;
    40  import javax.xml.transform.sax.SAXSource;
    41  import javax.xml.transform.stream.StreamSource;
    42
    43  import org.apache.ws.commons.schema.constants.Constants;
    44  import org.apache.ws.commons.schema.extensions.ExtensionRegistry;
    45  import org.apache.ws.commons.schema.resolver.CollectionURIResolver;
    46  import org.apache.ws.commons.schema.resolver.DefaultURIResolver;
    47  import org.apache.ws.commons.schema.resolver.URIResolver;
    48  import org.apache.ws.commons.schema.utils.DOMUtil;
    49  import org.apache.ws.commons.schema.utils.NamespacePrefixList;
    50  import org.apache.ws.commons.schema.utils.TargetNamespaceValidator;
    51  import org.w3c.dom.Document;
    52  import org.w3c.dom.Element;
    53  import org.w3c.dom.Node;
    54  import org.xml.sax.InputSource;
    55  import org.xml.sax.SAXException;
    56
    57  /**
    58   * Contains a cache of XML Schema definition language (XSD).
    59   *
    60   */
    61  public final class XmlSchemaCollection {
    62
    63      // the default extension registry
    64      private ExtensionRegistry extReg = new ExtensionRegistry();
    65
    66      public ExtensionRegistry getExtReg() {
    67          return extReg;
    68      }
    69
    70      public void setExtReg(ExtensionRegistry extReg) {
    71          this.extReg = extReg;
    72      }
    73
    74      /**
    75       * This map contains a list of Schema objects keyed in by their namespaces
    76       * When resolving schemas, this map will be checked for the presence of the schema
    77       * first
    78       */
    79      private Map knownNamespaceMap = new HashMap();
    80
    81      /**
    82       * get the namespace map
    83       * @return a map of previously known XMLSchema objects keyed by their namespace (String)
    84       */
    85      public Map getKnownNamespaceMap() {
    86  		return knownNamespaceMap;
    87  	}
    88
    89      /**
    90       * sets the known namespace map
    91       * @param knownNamespaceMap a map of previously known XMLSchema objects keyed by their namespace (String)
    92       */
    93  	public void setKnownNamespaceMap(Map knownNamespaceMap) {
    94  		this.knownNamespaceMap = knownNamespaceMap;
    95  	}
    96
    97
    98  	/**
    99  	 * Key that identifies a schema in a collection, composed of a targetNamespace
    100 	 * and a system ID.
    101 	 */
    102     public static class SchemaKey {
    103         private final String namespace;
    104         private final String systemId;
    105         SchemaKey(String pNamespace, String pSystemId) {
    106             namespace = pNamespace == null ? Constants.NULL_NS_URI : pNamespace;
    107             systemId = pSystemId == null ? "" : pSystemId;
    108         }
    109
    110         String getNamespace() { return namespace; }
    111
    112         String getSystemId() { return systemId; }
    113
    114         public int hashCode() {
    115             final int PRIME = 31;
    116             return (PRIME + namespace.hashCode()) * PRIME + systemId.hashCode();
    117         }
    118
    119         public boolean equals(Object obj) {
    120             if (this == obj)
    121                 return true;
    122             if (obj == null)
    123                 return false;
    124             if (getClass() != obj.getClass())
    125                 return false;
    126             final SchemaKey other = (SchemaKey) obj;
    127             return namespace.equals(other.namespace)  &&  systemId.equals(other.systemId);
    128         }
    129
    130         public String toString() {
    131             return Constants.NULL_NS_URI.equals(namespace) ?
    132                     systemId : ("{" + namespace + "}" + systemId);
    133         }
    134     }
    135
    136     /**
    137      * Map of included schemas.
    138      */
    139     private Map schemas = new HashMap();
    140
    141
    142     /**
    143      * base URI is used as the base for loading the
    144      * imports
    145      */
    146     String baseUri = null;
    147     /**
    148      * In-scope namespaces for XML processing
    149      */
    150     private NamespacePrefixList namespaceContext;
    151
    152     /**
    153      * An org.xml.sax.EntityResolver that is used to
    154      * resolve the imports/includes
    155      */
    156     private URIResolver schemaResolver = new DefaultURIResolver();
    157
    158 	XmlSchema xsd = new XmlSchema(XmlSchema.SCHEMA_NS, this);
    159
    160     /**
    161      * stack to track imports (to prevent recursion)
    162      */
    163     Stack stack = new Stack();
    164
    165     /**
    166      * Set the base URI. This is used when schemas need to be
    167      * loaded from relative locations
    168      * @param baseUri  baseUri for this collection.
    169      */
    170     public void setBaseUri(String baseUri) {
    171         this.baseUri = baseUri;
    172         if(schemaResolver instanceof CollectionURIResolver) {
    173         	CollectionURIResolver resolverWithBase =
    174         		(CollectionURIResolver) schemaResolver;
    175         	resolverWithBase.setCollectionBaseURI(baseUri);
    176         }
    177     }
    178
    179     /**
    180      * Register a custom URI resolver
    181      * @param schemaResolver   resolver
    182      */
    183     public void setSchemaResolver(URIResolver schemaResolver) {
    184         this.schemaResolver = schemaResolver;
    185     }
    186
    187     /**
    188      * Retrieve the custom URI resolver, if any.
    189      * @return the current resolver.
    190      */
    191     public URIResolver getSchemaResolver() {
    192 		return schemaResolver;
    193 	}
    194
    195     /**
    196      * This section should comply to the XMLSchema specification; see
    197      * <a href="http://www.w3.org/TR/2004/PER-xmlschema-2-20040318/datatypes.html#built-in-datatypes">
    198      *  http://www.w3.org/TR/2004/PER-xmlschema-2-20040318/datatypes.html#built-in-datatypes</a>.
    199      * This needs to be inspected by another pair of eyes
    200      */
    201     public void init() {
    202
    203     	/*
    204     	 * Defined in section 4.
    205     	 */
    206     	addSimpleType(xsd, Constants.XSD_ANYSIMPLETYPE.getLocalPart());
    207     	addSimpleType(xsd, Constants.XSD_ANYTYPE.getLocalPart());
    208
    209         /*
    210         Primitive types
    211
    212         3.2.1 string
    213         3.2.2 boolean
    214         3.2.3 decimal
    215         3.2.4 float
    216         3.2.5 double
    217         3.2.6 duration
    218         3.2.7 dateTime
    219         3.2.8 time
    220         3.2.9 date
    221         3.2.10 gYearMonth
    222         3.2.11 gYear
    223         3.2.12 gMonthDay
    224         3.2.13 gDay
    225         3.2.14 gMonth
    226         3.2.15 hexBinary
    227         3.2.16 base64Binary
    228         3.2.17 anyURI
    229         3.2.18 QName
    230         3.2.19 NOTATION
    231         */
    232         addSimpleType(xsd, Constants.XSD_STRING.getLocalPart());
    233         addSimpleType(xsd, Constants.XSD_BOOLEAN.getLocalPart());
    234         addSimpleType(xsd, Constants.XSD_FLOAT.getLocalPart());
    235         addSimpleType(xsd, Constants.XSD_DOUBLE.getLocalPart());
    236         addSimpleType(xsd, Constants.XSD_QNAME.getLocalPart());
    237         addSimpleType(xsd, Constants.XSD_DECIMAL.getLocalPart());
    238         addSimpleType(xsd, Constants.XSD_DURATION.getLocalPart());
    239         addSimpleType(xsd, Constants.XSD_DATE.getLocalPart());
    240         addSimpleType(xsd, Constants.XSD_TIME.getLocalPart());
    241         addSimpleType(xsd, Constants.XSD_DATETIME.getLocalPart());
    242         addSimpleType(xsd, Constants.XSD_DAY.getLocalPart());
    243         addSimpleType(xsd, Constants.XSD_MONTH.getLocalPart());
    244         addSimpleType(xsd, Constants.XSD_MONTHDAY.getLocalPart());
    245         addSimpleType(xsd, Constants.XSD_YEAR.getLocalPart());
    246         addSimpleType(xsd, Constants.XSD_YEARMONTH.getLocalPart());
    247         addSimpleType(xsd, Constants.XSD_NOTATION.getLocalPart());
    248         addSimpleType(xsd, Constants.XSD_HEXBIN.getLocalPart());
    249         addSimpleType(xsd, Constants.XSD_BASE64.getLocalPart());
    250         addSimpleType(xsd, Constants.XSD_ANYURI.getLocalPart());
    251
    252
    253         /*
    254          3.3.1 normalizedString
    255         3.3.2 token
    256         3.3.3 language
    257         3.3.4 NMTOKEN
    258         3.3.5 NMTOKENS
    259         3.3.6 Name
    260         3.3.7 NCName
    261         3.3.8 ID
    262         3.3.9 IDREF
    263         3.3.10 IDREFS
    264         3.3.11 ENTITY
    265         3.3.12 ENTITIES
    266         3.3.13 integer
    267         3.3.14 nonPositiveInteger
    268         3.3.15 negativeInteger
    269         3.3.16 long
    270         3.3.17 int
    271         3.3.18 short
    272         3.3.19 byte
    273         3.3.20 nonNegativeInteger
    274         3.3.21 unsignedLong
    275         3.3.22 unsignedInt
    276         3.3.23 unsignedShort
    277         3.3.24 unsignedByte
    278         3.3.25 positiveInteger
    279         */
    280
    281          //derived types from decimal
    282         addSimpleType(xsd, Constants.XSD_LONG.getLocalPart());
    283         addSimpleType(xsd, Constants.XSD_SHORT.getLocalPart());
    284         addSimpleType(xsd, Constants.XSD_BYTE.getLocalPart());
    285         addSimpleType(xsd, Constants.XSD_INTEGER.getLocalPart());
    286         addSimpleType(xsd, Constants.XSD_INT.getLocalPart());
    287         addSimpleType(xsd, Constants.XSD_POSITIVEINTEGER.getLocalPart());
    288         addSimpleType(xsd, Constants.XSD_NEGATIVEINTEGER.getLocalPart());
    289         addSimpleType(xsd, Constants.XSD_NONPOSITIVEINTEGER.getLocalPart());
    290         addSimpleType(xsd, Constants.XSD_NONNEGATIVEINTEGER.getLocalPart());
    291         addSimpleType(xsd, Constants.XSD_UNSIGNEDBYTE.getLocalPart());
    292         addSimpleType(xsd, Constants.XSD_UNSIGNEDINT.getLocalPart());
    293         addSimpleType(xsd, Constants.XSD_UNSIGNEDLONG.getLocalPart());
    294         addSimpleType(xsd, Constants.XSD_UNSIGNEDSHORT.getLocalPart());
    295
    296         //derived types from string
    297         addSimpleType(xsd, Constants.XSD_NAME.getLocalPart());
    298         addSimpleType(xsd, Constants.XSD_NORMALIZEDSTRING.getLocalPart());
    299         addSimpleType(xsd, Constants.XSD_NCNAME.getLocalPart());
    300         addSimpleType(xsd, Constants.XSD_NMTOKEN.getLocalPart());
    301         addSimpleType(xsd, Constants.XSD_NMTOKENS.getLocalPart());
    302         addSimpleType(xsd, Constants.XSD_ENTITY.getLocalPart());
    303         addSimpleType(xsd, Constants.XSD_ENTITIES.getLocalPart());
    304         addSimpleType(xsd, Constants.XSD_ID.getLocalPart());
    305         addSimpleType(xsd, Constants.XSD_IDREF.getLocalPart());
    306         addSimpleType(xsd, Constants.XSD_IDREFS.getLocalPart());
    307         addSimpleType(xsd, Constants.XSD_LANGUAGE.getLocalPart());
    308         addSimpleType(xsd, Constants.XSD_TOKEN.getLocalPart());
    309
    310         //SchemaKey key = new SchemaKey(XmlSchema.SCHEMA_NS, null);
    311         //addSchema(key, xsd);
    312
    313         // look for a system property to see whether we have a registered
    314         // extension registry class. if so we'll instantiate a new one
    315         // and set it as the extension registry
    316         //if there is an error, we'll just print out a message and move on.
    317
    318         if (System.getProperty(Constants.SystemConstants.EXTENSION_REGISTRY_KEY)!= null){
    319             try {
    320                 Class clazz = Class.forName(System.getProperty(Constants.SystemConstants.EXTENSION_REGISTRY_KEY));
    321                 this.extReg = (ExtensionRegistry)clazz.newInstance();
    322             } catch (ClassNotFoundException e) {
    323                 System.err.println("The specified extension registry class cannot be found!");
    324             } catch (InstantiationException e) {
    325                 System.err.println("The specified extension registry class cannot be instantiated!");
    326             } catch (IllegalAccessException e) {
    327                 System.err.println("The specified extension registry class cannot be accessed!");
    328             }
    329         }
    330     }
    331
    332     boolean containsSchema(SchemaKey pKey) {
    333         return schemas.containsKey(pKey);
    334     }
    335
    336
    337     /**
    338      * gets a schema from the external namespace map
    339      * @param namespace
    340      * @return
    341      */
    342     XmlSchema getKnownSchema(String namespace) {
    343         return (XmlSchema) knownNamespaceMap.get(namespace);
    344     }
    345
    346     /**
    347      * Get a schema given a SchemaKey
    348      * @param pKey
    349      * @return
    350      */
    351     XmlSchema getSchema(SchemaKey pKey) {
    352         return (XmlSchema) schemas.get(pKey);
    353     }
    354
    355     void addSchema(SchemaKey pKey, XmlSchema pSchema) {
    356         if (schemas.containsKey(pKey)) {
    357             throw new IllegalStateException("A schema with target namespace "
    358                     + pKey.getNamespace() + " and system ID " + pKey.getSystemId()
    359                     + " is already present.");
    360         }
    361         schemas.put(pKey, pSchema);
    362     }
    363
    364     private void addSimpleType(XmlSchema schema,String typeName){
    365         XmlSchemaSimpleType type;
    366         type = new XmlSchemaSimpleType(schema);
    367         type.setName(typeName);
    368         schema.addType(type);
    369     }
    370     public XmlSchema read(Reader r, ValidationEventHandler veh) {
    371         return read(new InputSource(r), veh);
    372     }
    373
    374     XmlSchema read(final InputSource inputSource, ValidationEventHandler veh,
    375             TargetNamespaceValidator namespaceValidator) {
    376         try {
    377             DocumentBuilderFactory docFac = DocumentBuilderFactory.newInstance();
    378             docFac.setNamespaceAware(true);
    379             final DocumentBuilder builder = docFac.newDocumentBuilder();
    380             Document doc = null;
    381             doc = parse_doPriv(inputSource, builder, doc);
    382             return read(doc, inputSource.getSystemId(), veh, namespaceValidator);
    383         } catch (ParserConfigurationException e) {
    384             throw new XmlSchemaException(e.getMessage());
    385         } catch (IOException e) {
    386             throw new XmlSchemaException(e.getMessage());
    387         } catch (SAXException e) {
    388             throw new XmlSchemaException(e.getMessage());
    389         }
    390     }
    391
    392     private Document parse_doPriv(final InputSource inputSource, final DocumentBuilder builder, Document doc) throws IOException, SAXException {
    393         try {
    394             doc = (Document) java.security.AccessController.doPrivileged(
    395                     new PrivilegedExceptionAction() {
    396                         public Object run() throws IOException, SAXException {
    397                             return builder.parse(inputSource);
    398                         }
    399                     }
    400             );
    401         } catch (PrivilegedActionException e) {
    402             Exception exception = e.getException();
    403             if(exception instanceof IOException) {
    404                 throw (IOException) exception;
    405             }
    406             if(exception instanceof SAXException) {
    407                 throw (SAXException) exception;
    408             }
    409         }
    410         return doc;
    411     }
    412
    413     /**
    414      * Read an XML schema into the collection from a SAX InputSource.
    415      * Schemas in a collection must be unique in the concatenation of system ID and
    416      * targetNamespace. In this API, the systemID is taken from the source.
    417      * @param inputSource the XSD document.
    418      * @param veh handler that is called back for validation.
    419      * @return the XML schema object.
    420      */
    421     public XmlSchema read(InputSource inputSource, ValidationEventHandler veh) {
    422         return read(inputSource, veh, null);
    423     }
    424
    425     /**
    426      * Read an XML schema into the collection from a TRaX source.
    427      * Schemas in a collection must be unique in the concatenation of system ID and
    428      * targetNamespace. In this API, the systemID is taken from the Source.
    429      * @param source the XSD document.
    430      * @param veh handler that is called back for validation.
    431      * @return the XML schema object.
    432      */
    433     public XmlSchema read(Source source, ValidationEventHandler veh) {
    434         if (source instanceof SAXSource) {
    435             return read(((SAXSource) source).getInputSource(), veh);
    436         } else if (source instanceof DOMSource) {
    437             Node node = ((DOMSource) source).getNode();
    438             if (node instanceof Document) {
    439                 node = ((Document) node).getDocumentElement();
    440             }
    441             return read((Document) node, veh);
    442         } else if (source instanceof StreamSource) {
    443             StreamSource ss = (StreamSource) source;
    444             InputSource isource = new InputSource(ss.getSystemId());
    445             isource.setByteStream(ss.getInputStream());
    446             isource.setCharacterStream(ss.getReader());
    447             isource.setPublicId(ss.getPublicId());
    448             return read(isource, veh);
    449         } else {
    450             InputSource isource = new InputSource(source.getSystemId());
    451             return read(isource, veh);
    452         }
    453     }
    454
    455     /**
    456      * Read an XML schema into the collection from a DOM document.
    457      * Schemas in a collection must be unique in the concatenation of system ID and
    458      * targetNamespace. In this API, the systemID is taken from the document.
    459      * @param doc the XSD document.
    460      * @param veh handler that is called back for validation.
    461      * @return the XML schema object.
    462      */
    463     public XmlSchema read(Document doc, ValidationEventHandler veh) {
    464         SchemaBuilder builder = new SchemaBuilder(this, null);
    465         return builder.build(doc, null, veh);
    466     }
    467
    468
    469     /**
    470      * Read an XML Schema into the collection from a DOM element. Schemas in a collection
    471      * must be unique in the concatentation of System ID and targetNamespace. The system ID will
    472      * be empty for this API.
    473      * @param elem the DOM element for the schema.
    474      * @return the XmlSchema
    475      */
    476     public XmlSchema read(Element elem) {
    477         SchemaBuilder builder = new SchemaBuilder(this, null);
    478         XmlSchema xmlSchema = builder.handleXmlSchemaElement(elem, null);
    479         xmlSchema.setInputEncoding(DOMUtil.getXmlEncoding(elem.getOwnerDocument()));
    480         return xmlSchema;
    481     }
    482
    483     /**
    484      * Read an XML Schema from a complete XSD XML DOM Document into this collection.
    485      * Schemas in a collection must be unique in
    486      * the concatenation of SystemId and targetNamespace.
    487      * @param doc The schema document.
    488      * @param systemId System ID for this schema.
    489      * @param veh handler to be called to check validity of the schema.
    490      * @return the schema object.
    491      */
    492     public XmlSchema read(Document doc, String systemId, ValidationEventHandler veh) {
    493         return read(doc, systemId, veh, null);
    494     }
    495
    496     /**
    497      * Read an XML Schema from a complete XSD XML DOM Document into this collection.
    498      *  Schemas in a collection must be unique in
    499      * the concatenation of SystemId and targetNamespace.
    500      * @param doc Source document.
    501      * @param systemId System id.
    502      * @param veh Stub for future capability to handle validation errors.
    503      * @param validator object that is called back to check validity of the target namespace.
    504      * @return the schema object.
    505      */
    506     public XmlSchema read(Document doc, String systemId, ValidationEventHandler veh,
    507             TargetNamespaceValidator validator) {
    508         SchemaBuilder builder = new SchemaBuilder(this, validator);
    509         XmlSchema schema = builder.build(doc, systemId, veh);
    510         schema.setInputEncoding(DOMUtil.getInputEncoding(doc));
    511 		return schema;
    512     }
    513
    514     /**
    515      * Read a schema from a DOM tree into the collection. The schemas in a collection must be unique
    516      * in the concatenation of the target namespace and the system ID.
    517      * @param elem xs:schema DOM element.
    518      * @param systemId System id.
    519      * @return the schema object.
    520      */
    521     public XmlSchema read(Element elem, String systemId) {
    522         SchemaBuilder builder = new SchemaBuilder(this, null);
    523         XmlSchema xmlSchema = builder.handleXmlSchemaElement(elem, systemId);
    524         xmlSchema.setInputEncoding(DOMUtil.getInputEncoding(elem.getOwnerDocument()));
    525         return xmlSchema;
    526     }
    527
    528     /**
    529      * Creates new XmlSchemaCollection
    530      */
    531     public XmlSchemaCollection() {
    532         init();
    533     }
    534
    535     /**
    536      * Retrieve a set containing the XmlSchema instances with the given system ID.
    537      * In general, this will return a single instance, or none. However,
    538      * if the schema has no targetNamespace attribute and was included
    539      * from schemata with different target namespaces, then it may
    540      * occur, that multiple schema instances with different logical
    541      * target namespaces may be returned.
    542      * @param systemId  the system id for this  schema
    543      * @return array of XmlSchema objects
    544      */
    545     public XmlSchema[] getXmlSchema(String systemId) {
    546         if (systemId == null) {
    547             systemId = "";
    548         }
    549         final List result = new ArrayList();
    550         for (Iterator iter = schemas.entrySet().iterator();  iter.hasNext();  ) {
    551             Map.Entry entry = (Map.Entry) iter.next();
    552             if (((SchemaKey) entry.getKey()).getSystemId().equals(systemId)) {
    553                 result.add(entry.getValue());
    554             }
    555         }
    556         return (XmlSchema[]) result.toArray(new XmlSchema[result.size()]);
    557     }
    558
    559     /**
    560      * Returns an array of all the XmlSchemas in this collection.
    561      * @return the list of XmlSchema objects
    562      */
    563     public XmlSchema[] getXmlSchemas() {
    564         Collection c = schemas.values();
    565         return (XmlSchema[]) c.toArray(new XmlSchema[c.size()]);
    566     }
    567
    568
    569     /**
    570      * Retrieve a global element from the schema collection.
    571      * @param qname the element QName.
    572      * @return the element object, or null.
    573      */
    574     public XmlSchemaElement getElementByQName(QName qname) {
    575 		String uri = qname.getNamespaceURI();
    576 		for (Iterator iter = schemas.entrySet().iterator(); iter.hasNext();) {
    577 			Map.Entry entry = (Map.Entry) iter.next();
    578 			if (((SchemaKey) entry.getKey()).getNamespace().equals(uri)) {
    579 				XmlSchemaElement element = ((XmlSchema) entry.getValue())
    580 						.getElementByName(qname);
    581 				if (element != null) {
    582 					return element;
    583 				}
    584 			}
    585 		}
    586 		return null;
    587 	}
    588
    589     /**
    590      * Retrieve a global type from the schema collection.
    591      * @param schemaTypeName the QName of the type.
    592      * @return the type object, or null.
    593      */
    594     public XmlSchemaType getTypeByQName(QName schemaTypeName) {
    595 		String uri = schemaTypeName.getNamespaceURI();
    596 		for (Iterator iter = schemas.entrySet().iterator(); iter.hasNext();) {
    597 			Map.Entry entry = (Map.Entry) iter.next();
    598 			if (((SchemaKey) entry.getKey()).getNamespace().equals(uri)) {
    599 				XmlSchemaType type = ((XmlSchema) entry.getValue())
    600 						.getTypeByName(schemaTypeName);
    601 				if (type != null) {
    602 					return type;
    603 				}
    604 			}
    605 		}
    606 		return null;
    607 	}
    608
    609     /**
    610      * Find a global attribute by QName in this collection of schemas.
    611      * @param schemaAttributeName the name of the attribute.
    612      * @return the attribute or null.
    613      */
    614     public XmlSchemaAttribute getAttributeByQName(QName schemaAttributeName) {
    615         String uri = schemaAttributeName.getNamespaceURI();
    616         for (Iterator iter = schemas.entrySet().iterator();  iter.hasNext();  ) {
    617             Map.Entry entry = (Map.Entry) iter.next();
    618             if (((SchemaKey) entry.getKey()).getNamespace().equals(uri)) {
    619                 XmlSchemaAttribute attribute = ((XmlSchema) entry.getValue()).getAttributeByName(schemaAttributeName);
    620                 if (attribute != null) {
    621                     return attribute;
    622                 }
    623         }
    624         }
    625         return null;
    626     }
    627
    628     /**
    629      * Return the schema from this collection for a particular targetNamespace.
    630      * @param uri target namespace URI.
    631      * @return the schema.
    632      */
    633     public XmlSchema schemaForNamespace(String uri) {
    634         for (Iterator iter = schemas.entrySet().iterator();  iter.hasNext();  ) {
    635             Map.Entry entry = (Map.Entry) iter.next();
    636             if (((SchemaKey) entry.getKey()).getNamespace().equals(uri)) {
    637                 return (XmlSchema) entry.getValue();
    638             }
    639         }
    640         return null;
    641     }
    642
    643     Map unresolvedTypes = new HashMap();
    644
    645     void addUnresolvedType(QName type, TypeReceiver receiver) {
    646         ArrayList receivers = (ArrayList)unresolvedTypes.get(type);
    647         if (receivers == null) {
    648             receivers = new ArrayList();
    649             unresolvedTypes.put(type, receivers);
    650         }
    651         receivers.add(receiver);
    652     }
    653
    654     void resolveType(QName typeName, XmlSchemaType type) {
    655         ArrayList receivers = (ArrayList)unresolvedTypes.get(typeName);
    656         if (receivers == null)
    657             return;
    658         for (Iterator i = receivers.iterator(); i.hasNext();) {
    659             TypeReceiver receiver = (TypeReceiver) i.next();
    660             receiver.setType(type);
    661         }
    662         unresolvedTypes.remove(typeName);
    663     }
    664
    665     /**
    666      * Retrieve the namespace context.
    667      * @return the namespace context.
    668      */
    669     public NamespacePrefixList getNamespaceContext() {
    670         return namespaceContext;
    671     }
    672
    673     /**
    674      * Set the namespace context for this collection, which controls the assignment of
    675      * namespace prefixes to namespaces.
    676      * @param namespaceContext the context.
    677      */
    678     public void setNamespaceContext(NamespacePrefixList namespaceContext) {
    679         this.namespaceContext = namespaceContext;
    680     }
    681
    682     /**
    683      * Push a schema onto the stack of schemas.
    684      *  This function, while public, is probably not useful outside of
    685      * the implementation.
    686      * @param pKey the schema key.
    687      */
    688     public void push(SchemaKey pKey) {
    689         stack.push(pKey);
    690     }
    691
    692     /**
    693      * Pop the stack of schemas. This function, while public, is probably not useful outside of
    694      * the implementation.
    695      */
    696     public void pop() {
    697         stack.pop();
    698     }
    699
    700     /**
    701      * Return an indication of whether a particular schema is in the working stack of
    702      * schemas. This function, while public, is probably not useful outside of
    703      * the implementation.
    704      * @param pKey schema key
    705      * @return true if the schema is in the stack.
    706      */
    707     public boolean check(SchemaKey pKey) {
    708         return (stack.indexOf(pKey)==-1);
    709     }
    710
    711 	public String toString() {
    712     	return super.toString() + "[" + schemas.toString() + "]";
    713     }
    714 }