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