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