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