View Javadoc

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