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