KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > dom4j > datatype > SchemaParser


1 /*
2  * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
3  *
4  * This software is open source.
5  * See the bottom of this file for the licence.
6  */

7
8 package org.dom4j.datatype;
9
10 import com.sun.msv.datatype.xsd.DatatypeFactory;
11 import com.sun.msv.datatype.xsd.TypeIncubator;
12 import com.sun.msv.datatype.xsd.XSDatatype;
13
14 import java.util.HashMap JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.Map JavaDoc;
17
18 import org.dom4j.Attribute;
19 import org.dom4j.Document;
20 import org.dom4j.DocumentFactory;
21 import org.dom4j.Element;
22 import org.dom4j.Namespace;
23 import org.dom4j.QName;
24 import org.dom4j.io.SAXReader;
25 import org.dom4j.util.AttributeHelper;
26
27 import org.relaxng.datatype.DatatypeException;
28 import org.relaxng.datatype.ValidationContext;
29
30 import org.xml.sax.EntityResolver JavaDoc;
31 import org.xml.sax.InputSource JavaDoc;
32
33 /**
34  * <p>
35  * <code>SchemaParser</code> reads an XML Schema Document.
36  * </p>
37  *
38  * @author <a HREF="mailto:jstrachan@apache.org">James Strachan </a>
39  * @author Yuxin Ruan
40  * @version $Revision: 1.19 $
41  */

42 public class SchemaParser {
43     private static final Namespace XSD_NAMESPACE = Namespace.get("xsd",
44             "http://www.w3.org/2001/XMLSchema");
45
46     // Use QNames for the elements
47
private static final QName XSD_ELEMENT = QName
48             .get("element", XSD_NAMESPACE);
49
50     private static final QName XSD_ATTRIBUTE = QName.get("attribute",
51             XSD_NAMESPACE);
52
53     private static final QName XSD_SIMPLETYPE = QName.get("simpleType",
54             XSD_NAMESPACE);
55
56     private static final QName XSD_COMPLEXTYPE = QName.get("complexType",
57             XSD_NAMESPACE);
58
59     private static final QName XSD_RESTRICTION = QName.get("restriction",
60             XSD_NAMESPACE);
61
62     private static final QName XSD_SEQUENCE = QName.get("sequence",
63             XSD_NAMESPACE);
64
65     private static final QName XSD_CHOICE = QName.get("choice", XSD_NAMESPACE);
66
67     private static final QName XSD_ALL = QName.get("all", XSD_NAMESPACE);
68
69     private static final QName XSD_INCLUDE = QName
70             .get("include", XSD_NAMESPACE);
71
72     /** Document factory used to register Element specific factories */
73     private DatatypeDocumentFactory documentFactory;
74
75     /**
76      * Cache of <code>XSDatatype</code> instances loaded or created during
77      * this build
78      */

79     private Map JavaDoc dataTypeCache = new HashMap JavaDoc();
80
81     /** NamedTypeResolver */
82     private NamedTypeResolver namedTypeResolver;
83
84     /** target namespace */
85     private Namespace targetNamespace;
86
87     public SchemaParser() {
88         this(DatatypeDocumentFactory.singleton);
89     }
90
91     public SchemaParser(DatatypeDocumentFactory documentFactory) {
92         this.documentFactory = documentFactory;
93         this.namedTypeResolver = new NamedTypeResolver(documentFactory);
94     }
95
96     /**
97      * Parses the given schema document
98      *
99      * @param schemaDocument
100      * is the document of the XML Schema
101      */

102     public void build(Document schemaDocument) {
103         this.targetNamespace = null;
104         internalBuild(schemaDocument);
105     }
106
107     public void build(Document schemaDocument, Namespace namespace) {
108         this.targetNamespace = namespace;
109         internalBuild(schemaDocument);
110     }
111
112     private synchronized void internalBuild(Document schemaDocument) {
113         Element root = schemaDocument.getRootElement();
114
115         if (root != null) {
116             // handle schema includes
117
Iterator JavaDoc includeIter = root.elementIterator(XSD_INCLUDE);
118
119             while (includeIter.hasNext()) {
120                 Element includeElement = (Element) includeIter.next();
121                 String JavaDoc inclSchemaInstanceURI = includeElement
122                         .attributeValue("schemaLocation");
123                 EntityResolver JavaDoc resolver = schemaDocument.getEntityResolver();
124
125                 try {
126                     if (resolver == null) {
127                         String JavaDoc msg = "No EntityResolver available";
128                         throw new InvalidSchemaException(msg);
129                     }
130
131                     InputSource JavaDoc inputSource = resolver.resolveEntity(null,
132                             inclSchemaInstanceURI);
133
134                     if (inputSource == null) {
135                         String JavaDoc msg = "Could not resolve the schema URI: "
136                                 + inclSchemaInstanceURI;
137                         throw new InvalidSchemaException(msg);
138                     }
139
140                     SAXReader reader = new SAXReader();
141                     Document inclSchemaDocument = reader.read(inputSource);
142                     build(inclSchemaDocument);
143                 } catch (Exception JavaDoc e) {
144                     System.out.println("Failed to load schema: "
145                             + inclSchemaInstanceURI);
146                     System.out.println("Caught: " + e);
147                     e.printStackTrace();
148                     throw new InvalidSchemaException("Failed to load schema: "
149                             + inclSchemaInstanceURI);
150                 }
151             }
152
153             // handle elements
154
Iterator JavaDoc iter = root.elementIterator(XSD_ELEMENT);
155
156             while (iter.hasNext()) {
157                 onDatatypeElement((Element) iter.next(), documentFactory);
158             }
159
160             // handle named simple types
161
iter = root.elementIterator(XSD_SIMPLETYPE);
162
163             while (iter.hasNext()) {
164                 onNamedSchemaSimpleType((Element) iter.next());
165             }
166
167             // hanlde named complex types
168
iter = root.elementIterator(XSD_COMPLEXTYPE);
169
170             while (iter.hasNext()) {
171                 onNamedSchemaComplexType((Element) iter.next());
172             }
173
174             namedTypeResolver.resolveNamedTypes();
175         }
176     }
177
178     // Implementation methods
179
// -------------------------------------------------------------------------
180

181     /**
182      * processes an XML Schema &lt;element&gt; tag
183      *
184      * @param xsdElement
185      * DOCUMENT ME!
186      * @param parentFactory
187      * DOCUMENT ME!
188      */

189     private void onDatatypeElement(Element xsdElement,
190             DocumentFactory parentFactory) {
191         String JavaDoc name = xsdElement.attributeValue("name");
192         String JavaDoc type = xsdElement.attributeValue("type");
193         QName qname = getQName(name);
194
195         DatatypeElementFactory factory = getDatatypeElementFactory(qname);
196
197         if (type != null) {
198             // register type with this element name
199
XSDatatype dataType = getTypeByName(type);
200
201             if (dataType != null) {
202                 factory.setChildElementXSDatatype(qname, dataType);
203             } else {
204                 QName typeQName = getQName(type);
205                 namedTypeResolver.registerTypedElement(xsdElement, typeQName,
206                         parentFactory);
207             }
208
209             return;
210         }
211
212         // handle element types derrived from simpleTypes
213
Element xsdSimpleType = xsdElement.element(XSD_SIMPLETYPE);
214
215         if (xsdSimpleType != null) {
216             XSDatatype dataType = loadXSDatatypeFromSimpleType(xsdSimpleType);
217
218             if (dataType != null) {
219                 factory.setChildElementXSDatatype(qname, dataType);
220             }
221         }
222
223         Element schemaComplexType = xsdElement.element(XSD_COMPLEXTYPE);
224
225         if (schemaComplexType != null) {
226             onSchemaComplexType(schemaComplexType, factory);
227         }
228
229         Iterator JavaDoc iter = xsdElement.elementIterator(XSD_ATTRIBUTE);
230
231         if (iter.hasNext()) {
232             do {
233                 onDatatypeAttribute(xsdElement, factory, (Element) iter
234                         .next());
235             } while (iter.hasNext());
236         }
237     }
238
239     /**
240      * processes an named XML Schema &lt;complexTypegt; tag
241      *
242      * @param schemaComplexType
243      * DOCUMENT ME!
244      */

245     private void onNamedSchemaComplexType(Element schemaComplexType) {
246         Attribute nameAttr = schemaComplexType.attribute("name");
247
248         if (nameAttr == null) {
249             return;
250         }
251
252         String JavaDoc name = nameAttr.getText();
253         QName qname = getQName(name);
254
255         DatatypeElementFactory factory = getDatatypeElementFactory(qname);
256
257         onSchemaComplexType(schemaComplexType, factory);
258         namedTypeResolver.registerComplexType(qname, factory);
259     }
260
261     /**
262      * processes an XML Schema &lt;complexTypegt; tag
263      *
264      * @param schemaComplexType
265      * DOCUMENT ME!
266      * @param elementFactory
267      * DOCUMENT ME!
268      */

269     private void onSchemaComplexType(Element schemaComplexType,
270             DatatypeElementFactory elementFactory) {
271         Iterator JavaDoc iter = schemaComplexType.elementIterator(XSD_ATTRIBUTE);
272
273         while (iter.hasNext()) {
274             Element xsdAttribute = (Element) iter.next();
275             String JavaDoc name = xsdAttribute.attributeValue("name");
276             QName qname = getQName(name);
277
278             XSDatatype dataType = dataTypeForXsdAttribute(xsdAttribute);
279
280             if (dataType != null) {
281                 // register the XSDatatype for the given Attribute
282
// #### should both these be done?
283
// elementFactory.setChildElementXSDatatype( qname, dataType );
284
elementFactory.setAttributeXSDatatype(qname, dataType);
285             }
286         }
287
288         // handle sequence definition
289
Element schemaSequence = schemaComplexType.element(XSD_SEQUENCE);
290
291         if (schemaSequence != null) {
292             onChildElements(schemaSequence, elementFactory);
293         }
294
295         // handle choice definition
296
Element schemaChoice = schemaComplexType.element(XSD_CHOICE);
297
298         if (schemaChoice != null) {
299             onChildElements(schemaChoice, elementFactory);
300         }
301
302         // handle all definition
303
Element schemaAll = schemaComplexType.element(XSD_ALL);
304
305         if (schemaAll != null) {
306             onChildElements(schemaAll, elementFactory);
307         }
308     }
309
310     private void onChildElements(Element element, DatatypeElementFactory fact) {
311         Iterator JavaDoc iter = element.elementIterator(XSD_ELEMENT);
312
313         while (iter.hasNext()) {
314             Element xsdElement = (Element) iter.next();
315             onDatatypeElement(xsdElement, fact);
316         }
317     }
318
319     /**
320      * processes an XML Schema &lt;attribute&gt; tag
321      *
322      * @param xsdElement
323      * DOCUMENT ME!
324      * @param elementFactory
325      * DOCUMENT ME!
326      * @param xsdAttribute
327      * DOCUMENT ME!
328      */

329     private void onDatatypeAttribute(Element xsdElement,
330             DatatypeElementFactory elementFactory, Element xsdAttribute) {
331         String JavaDoc name = xsdAttribute.attributeValue("name");
332         QName qname = getQName(name);
333         XSDatatype dataType = dataTypeForXsdAttribute(xsdAttribute);
334
335         if (dataType != null) {
336             // register the XSDatatype for the given Attribute
337
elementFactory.setAttributeXSDatatype(qname, dataType);
338         } else {
339             String JavaDoc type = xsdAttribute.attributeValue("type");
340             System.out.println("Warning: Couldn't find XSDatatype for type: "
341                     + type + " attribute: " + name);
342         }
343     }
344
345     /**
346      * processes an XML Schema &lt;attribute&gt; tag
347      *
348      * @param xsdAttribute
349      * DOCUMENT ME!
350      *
351      * @return DOCUMENT ME!
352      *
353      * @throws InvalidSchemaException
354      * DOCUMENT ME!
355      */

356     private XSDatatype dataTypeForXsdAttribute(Element xsdAttribute) {
357         String JavaDoc type = xsdAttribute.attributeValue("type");
358         XSDatatype dataType = null;
359
360         if (type != null) {
361             dataType = getTypeByName(type);
362         } else {
363             // must parse the <simpleType> element
364
Element xsdSimpleType = xsdAttribute.element(XSD_SIMPLETYPE);
365
366             if (xsdSimpleType == null) {
367                 String JavaDoc name = xsdAttribute.attributeValue("name");
368                 String JavaDoc msg = "The attribute: " + name
369                         + " has no type attribute and does not contain a "
370                         + "<simpleType/> element";
371                 throw new InvalidSchemaException(msg);
372             }
373
374             dataType = loadXSDatatypeFromSimpleType(xsdSimpleType);
375         }
376
377         return dataType;
378     }
379
380     /**
381      * processes an named XML Schema &lt;simpleTypegt; tag
382      *
383      * @param schemaSimpleType
384      * DOCUMENT ME!
385      */

386     private void onNamedSchemaSimpleType(Element schemaSimpleType) {
387         Attribute nameAttr = schemaSimpleType.attribute("name");
388
389         if (nameAttr == null) {
390             return;
391         }
392
393         String JavaDoc name = nameAttr.getText();
394         QName qname = getQName(name);
395         XSDatatype datatype = loadXSDatatypeFromSimpleType(schemaSimpleType);
396         namedTypeResolver.registerSimpleType(qname, datatype);
397     }
398
399     /**
400      * Loads a XSDatatype object from a &lt;simpleType&gt; attribute schema
401      * element
402      *
403      * @param xsdSimpleType
404      * DOCUMENT ME!
405      *
406      * @return DOCUMENT ME!
407      */

408     private XSDatatype loadXSDatatypeFromSimpleType(Element xsdSimpleType) {
409         Element xsdRestriction = xsdSimpleType.element(XSD_RESTRICTION);
410
411         if (xsdRestriction != null) {
412             String JavaDoc base = xsdRestriction.attributeValue("base");
413
414             if (base != null) {
415                 XSDatatype baseType = getTypeByName(base);
416
417                 if (baseType == null) {
418                     onSchemaError("Invalid base type: " + base
419                             + " when trying to build restriction: "
420                             + xsdRestriction);
421                 } else {
422                     return deriveSimpleType(baseType, xsdRestriction);
423                 }
424             } else {
425                 // simpleType and base are mutually exclusive and you
426
// must have one within a <restriction> tag
427
Element xsdSubType = xsdSimpleType.element(XSD_SIMPLETYPE);
428
429                 if (xsdSubType == null) {
430                     String JavaDoc msg = "The simpleType element: " + xsdSimpleType
431                             + " must contain a base attribute or simpleType"
432                             + " element";
433                     onSchemaError(msg);
434                 } else {
435                     return loadXSDatatypeFromSimpleType(xsdSubType);
436                 }
437             }
438         } else {
439             onSchemaError("No <restriction>. Could not create XSDatatype for"
440                     + " simpleType: " + xsdSimpleType);
441         }
442
443         return null;
444     }
445
446     /**
447      * Derives a new type from a base type and a set of restrictions
448      *
449      * @param baseType
450      * DOCUMENT ME!
451      * @param xsdRestriction
452      * DOCUMENT ME!
453      *
454      * @return DOCUMENT ME!
455      */

456     private XSDatatype deriveSimpleType(XSDatatype baseType,
457             Element xsdRestriction) {
458         TypeIncubator incubator = new TypeIncubator(baseType);
459         ValidationContext context = null;
460
461         try {
462             for (Iterator JavaDoc iter = xsdRestriction.elementIterator(); iter
463                     .hasNext();) {
464                 Element element = (Element) iter.next();
465                 String JavaDoc name = element.getName();
466                 String JavaDoc value = element.attributeValue("value");
467                 boolean fixed = AttributeHelper.booleanValue(element, "fixed");
468
469                 // add facet
470
incubator.addFacet(name, value, fixed, context);
471             }
472
473             // derive a new type by those facets
474
String JavaDoc newTypeName = null;
475
476             return incubator.derive("", newTypeName);
477         } catch (DatatypeException e) {
478             onSchemaError("Invalid restriction: " + e.getMessage()
479                     + " when trying to build restriction: " + xsdRestriction);
480
481             return null;
482         }
483     }
484
485     /**
486      * DOCUMENT ME!
487      *
488      * @param name
489      * The name of the element
490      *
491      * @return the <code>DatatypeElementFactory</code> for the given element
492      * QName, creating one if it does not already exist
493      */

494     private DatatypeElementFactory getDatatypeElementFactory(QName name) {
495         DatatypeElementFactory factory = documentFactory
496                 .getElementFactory(name);
497
498         if (factory == null) {
499             factory = new DatatypeElementFactory(name);
500             name.setDocumentFactory(factory);
501         }
502
503         return factory;
504     }
505
506     private XSDatatype getTypeByName(String JavaDoc type) {
507         XSDatatype dataType = (XSDatatype) dataTypeCache.get(type);
508
509         if (dataType == null) {
510             // first check to see if it is a built-in type
511
// maybe a prefix is being used
512
int idx = type.indexOf(':');
513
514             if (idx >= 0) {
515                 String JavaDoc localName = type.substring(idx + 1);
516
517                 try {
518                     dataType = DatatypeFactory.getTypeByName(localName);
519                 } catch (DatatypeException e) {
520                 }
521             }
522
523             if (dataType == null) {
524                 try {
525                     dataType = DatatypeFactory.getTypeByName(type);
526                 } catch (DatatypeException e) {
527                 }
528             }
529
530             if (dataType == null) {
531                 // it's no built-in type, maybe it's a type we defined ourself
532
QName typeQName = getQName(type);
533                 dataType = (XSDatatype) namedTypeResolver.simpleTypeMap
534                         .get(typeQName);
535             }
536
537             if (dataType != null) {
538                 // store in cache for later
539
dataTypeCache.put(type, dataType);
540             }
541         }
542
543         return dataType;
544     }
545
546     private QName getQName(String JavaDoc name) {
547         if (targetNamespace == null) {
548             return documentFactory.createQName(name);
549         } else {
550             return documentFactory.createQName(name, targetNamespace);
551         }
552     }
553
554     /**
555      * Called when there is a problem with the schema and the builder cannot
556      * handle the XML Schema Data Types correctly
557      *
558      * @param message
559      * DOCUMENT ME!
560      *
561      * @throws InvalidSchemaException
562      * DOCUMENT ME!
563      */

564     private void onSchemaError(String JavaDoc message) {
565         // Some users may wish to disable exception throwing
566
// and instead use some kind of listener for errors and continue
567
// System.out.println( "WARNING: " + message );
568
throw new InvalidSchemaException(message);
569     }
570 }
571
572 /*
573  * Redistribution and use of this software and associated documentation
574  * ("Software"), with or without modification, are permitted provided that the
575  * following conditions are met:
576  *
577  * 1. Redistributions of source code must retain copyright statements and
578  * notices. Redistributions must also contain a copy of this document.
579  *
580  * 2. Redistributions in binary form must reproduce the above copyright notice,
581  * this list of conditions and the following disclaimer in the documentation
582  * and/or other materials provided with the distribution.
583  *
584  * 3. The name "DOM4J" must not be used to endorse or promote products derived
585  * from this Software without prior written permission of MetaStuff, Ltd. For
586  * written permission, please contact dom4j-info@metastuff.com.
587  *
588  * 4. Products derived from this Software may not be called "DOM4J" nor may
589  * "DOM4J" appear in their names without prior written permission of MetaStuff,
590  * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
591  *
592  * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
593  *
594  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
595  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
596  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
597  * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
598  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
599  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
600  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
601  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
602  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
603  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
604  * POSSIBILITY OF SUCH DAMAGE.
605  *
606  * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
607  */

608
Popular Tags