1
2
3
4
5
6
7
8
9
10 package org.dom4j.datatype;
11
12 import com.sun.msv.datatype.xsd.DatatypeFactory;
13 import com.sun.msv.datatype.xsd.TypeIncubator;
14 import com.sun.msv.datatype.xsd.XSDatatype;
15
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.Map;
19
20 import org.dom4j.Attribute;
21 import org.dom4j.Document;
22 import org.dom4j.DocumentFactory;
23 import org.dom4j.Element;
24 import org.dom4j.Namespace;
25 import org.dom4j.QName;
26 import org.dom4j.io.SAXReader;
27 import org.dom4j.util.AttributeHelper;
28 import org.relaxng.datatype.DatatypeException;
29 import org.relaxng.datatype.ValidationContext;
30 import org.xml.sax.EntityResolver;
31 import org.xml.sax.InputSource;
32
33 /*** <p><code>SchemaParser</code> reads an XML Schema Document.</p>
34 *
35 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
36 * @author Yuxin Ruan
37 * @version $Revision: 1.17 $
38 */
39 public class SchemaParser {
40
41 private static final Namespace XSD_NAMESPACE = Namespace.get( "xsd", "http://www.w3.org/2001/XMLSchema" );
42
43
44 private static final QName XSD_ELEMENT = QName.get( "element", XSD_NAMESPACE );
45 private static final QName XSD_ATTRIBUTE = QName.get( "attribute", XSD_NAMESPACE );
46 private static final QName XSD_SIMPLETYPE = QName.get( "simpleType", XSD_NAMESPACE );
47 private static final QName XSD_COMPLEXTYPE = QName.get( "complexType", XSD_NAMESPACE );
48 private static final QName XSD_RESTRICTION = QName.get( "restriction", XSD_NAMESPACE );
49 private static final QName XSD_SEQUENCE = QName.get( "sequence", XSD_NAMESPACE );
50 private static final QName XSD_CHOICE = QName.get( "choice", XSD_NAMESPACE );
51 private static final QName XSD_ALL = QName.get( "all", XSD_NAMESPACE );
52 private static final QName XSD_INCLUDE = QName.get("include", XSD_NAMESPACE);
53
54 /*** Document factory used to register Element specific factories*/
55 private DatatypeDocumentFactory documentFactory;
56
57 /*** Cache of <code>XSDatatype</code> instances loaded or created during this build */
58 private Map dataTypeCache = new HashMap();
59
60 /*** NamedTypeResolver */
61 private NamedTypeResolver namedTypeResolver;
62
63 /*** target namespace */
64 private Namespace targetNamespace;
65
66 public SchemaParser() {
67 this(DatatypeDocumentFactory.singleton);
68 }
69
70 public SchemaParser(DatatypeDocumentFactory documentFactory) {
71 this.documentFactory = documentFactory;
72 this.namedTypeResolver = new NamedTypeResolver(documentFactory);
73 }
74
75 /*** Parses the given schema document
76 *
77 * @param schemaDocument is the document of the XML Schema
78 */
79 public void build( Document schemaDocument ) {
80 this.targetNamespace = null;
81 internalBuild( schemaDocument );
82 }
83
84 public void build( Document schemaDocument, Namespace targetNamespace) {
85 this.targetNamespace = targetNamespace;
86 internalBuild( schemaDocument );
87 }
88
89 private synchronized void internalBuild( Document schemaDocument ) {
90 Element root = schemaDocument.getRootElement();
91 if ( root != null ) {
92
93 Iterator includeIter = root.elementIterator( XSD_INCLUDE );
94 while (includeIter.hasNext()) {
95 Element includeElement = (Element) includeIter.next();
96 String inclSchemaInstanceURI = includeElement.attributeValue("schemaLocation");
97 EntityResolver resolver = schemaDocument.getEntityResolver();
98 try {
99 if ( resolver == null ) {
100 throw new InvalidSchemaException( "No EntityResolver available so could not resolve the schema URI: " +
101 inclSchemaInstanceURI );
102 }
103 InputSource inputSource = resolver.resolveEntity( null, inclSchemaInstanceURI );
104 if ( inputSource == null ) {
105 throw new InvalidSchemaException( "Could not resolve the schema URI: " + inclSchemaInstanceURI );
106 }
107 SAXReader reader = new SAXReader();
108 Document inclSchemaDocument = reader.read( inputSource );
109 build( inclSchemaDocument );
110 }
111 catch (Exception e) {
112 System.out.println( "Failed to load schema: " + inclSchemaInstanceURI );
113 System.out.println( "Caught: " + e );
114 e.printStackTrace();
115 throw new InvalidSchemaException( "Failed to load schema: " + inclSchemaInstanceURI );
116 }
117 }
118
119
120 Iterator iter = root.elementIterator( XSD_ELEMENT );
121 while ( iter.hasNext() ) {
122 onDatatypeElement( (Element) iter.next() , documentFactory);
123 }
124
125
126 iter = root.elementIterator( XSD_SIMPLETYPE );
127 while ( iter.hasNext() ) {
128 onNamedSchemaSimpleType((Element) iter.next());
129 }
130
131
132 iter = root.elementIterator( XSD_COMPLEXTYPE );
133 while ( iter.hasNext() ) {
134 onNamedSchemaComplexType((Element) iter.next());
135 }
136
137 namedTypeResolver.resolveNamedTypes();
138
139 }
140 }
141
142
143
144
145
146 /*** processes an XML Schema <element> tag
147 */
148 private void onDatatypeElement( Element xsdElement , DocumentFactory parentFactory ) {
149 String name = xsdElement.attributeValue( "name" );
150 String type = xsdElement.attributeValue( "type" );
151 QName qname = getQName( name );
152
153 DatatypeElementFactory elementFactory = getDatatypeElementFactory( qname );
154
155 if ( type != null ) {
156
157 XSDatatype dataType = getTypeByName(type);
158 if (dataType!=null) {
159 elementFactory.setChildElementXSDatatype( qname, dataType );
160 }
161 else {
162 QName typeQName=getQName(type);
163 namedTypeResolver.registerTypedElement(xsdElement,typeQName,parentFactory);
164 }
165 return;
166 }
167
168
169 Element xsdSimpleType = xsdElement.element( XSD_SIMPLETYPE );
170 if ( xsdSimpleType != null ) {
171 System.out.println("Agfa-sg: handle element types derrived from simpleTypes for element: " + name);
172 XSDatatype dataType = loadXSDatatypeFromSimpleType( xsdSimpleType );
173 if (dataType != null) {
174 System.out.println("dataType (from loadXSDatatypeFromSimpleType) = " + dataType);
175 elementFactory.setChildElementXSDatatype( qname, dataType );
176 }
177 }
178
179 Element schemaComplexType = xsdElement.element( XSD_COMPLEXTYPE );
180 if ( schemaComplexType != null ) {
181 onSchemaComplexType( schemaComplexType, elementFactory );
182 }
183
184 Iterator iter = xsdElement.elementIterator( XSD_ATTRIBUTE );
185 if ( iter.hasNext() ) {
186 do {
187 onDatatypeAttribute(
188 xsdElement,
189 elementFactory,
190 (Element) iter.next()
191 );
192 }
193 while ( iter.hasNext() );
194 }
195 }
196
197 /*** processes an named XML Schema <complexTypegt; tag
198 */
199 private void onNamedSchemaComplexType(Element schemaComplexType) {
200 Attribute nameAttr=schemaComplexType.attribute("name");
201 if (nameAttr==null) return;
202 String name=nameAttr.getText();
203 QName qname=getQName(name);
204
205 DatatypeElementFactory elementFactory = getDatatypeElementFactory( qname );
206
207
208 onSchemaComplexType(schemaComplexType,elementFactory);
209 namedTypeResolver.registerComplexType(qname,elementFactory);
210 }
211
212 /*** processes an XML Schema <complexTypegt; tag
213 */
214 private void onSchemaComplexType( Element schemaComplexType, DatatypeElementFactory elementFactory ) {
215 Iterator iter = schemaComplexType.elementIterator( XSD_ATTRIBUTE );
216 while ( iter.hasNext() ) {
217 Element xsdAttribute = (Element) iter.next();
218 String name = xsdAttribute.attributeValue( "name" );
219 QName qname = getQName( name );
220
221 XSDatatype dataType = dataTypeForXsdAttribute( xsdAttribute );
222 if ( dataType != null ) {
223
224
225
226 elementFactory.setAttributeXSDatatype( qname, dataType );
227 }
228 else {
229 String type = xsdAttribute.attributeValue( "type" );
230 System.out.println( "Warning: Couldn't find XSDatatype for type: " + type + " attribute: " + name );
231 }
232 }
233
234
235 Element schemaSequence = schemaComplexType.element( XSD_SEQUENCE );
236 if (schemaSequence!=null) {
237 onChildElements(schemaSequence,elementFactory);
238 }
239
240
241 Element schemaChoice = schemaComplexType.element( XSD_CHOICE );
242 if (schemaChoice!=null) {
243 onChildElements(schemaChoice,elementFactory);
244 }
245
246
247 Element schemaAll = schemaComplexType.element( XSD_ALL );
248 if (schemaAll!=null) {
249 onChildElements(schemaAll,elementFactory);
250 }
251 }
252
253 private void onChildElements(Element element,DatatypeElementFactory factory) {
254 Iterator iter = element.elementIterator( XSD_ELEMENT );
255 while ( iter.hasNext() ) {
256 Element xsdElement = (Element) iter.next();
257 onDatatypeElement(xsdElement,factory);
258 }
259 }
260
261 /*** processes an XML Schema <attribute> tag
262 */
263 private void onDatatypeAttribute(
264 Element xsdElement,
265 DatatypeElementFactory elementFactory,
266 Element xsdAttribute
267 ) {
268 String name = xsdAttribute.attributeValue( "name" );
269 QName qname = getQName( name );
270 XSDatatype dataType = dataTypeForXsdAttribute( xsdAttribute );
271 if ( dataType != null ) {
272
273 elementFactory.setAttributeXSDatatype( qname, dataType );
274 }
275 else {
276 String type = xsdAttribute.attributeValue( "type" );
277 System.out.println( "Warning: Couldn't find XSDatatype for type: " + type + " attribute: " + name );
278 }
279 }
280
281 /*** processes an XML Schema <attribute> tag
282 */
283 private XSDatatype dataTypeForXsdAttribute( Element xsdAttribute ) {
284 String type = xsdAttribute.attributeValue( "type" );
285 XSDatatype dataType = null;
286 if ( type != null ) {
287 dataType = getTypeByName( type );
288 }
289 else {
290
291 Element xsdSimpleType = xsdAttribute.element( XSD_SIMPLETYPE );
292 if ( xsdSimpleType == null ) {
293 String name = xsdAttribute.attributeValue( "name" );
294 throw new InvalidSchemaException(
295 "The attribute: " + name + " has no type attribute and does not contain a <simpleType/> element"
296 );
297 }
298 dataType = loadXSDatatypeFromSimpleType( xsdSimpleType );
299 }
300 return dataType;
301 }
302
303 /*** processes an named XML Schema <simpleTypegt; tag
304 */
305 private void onNamedSchemaSimpleType(Element schemaSimpleType) {
306 Attribute nameAttr=schemaSimpleType.attribute("name");
307 if (nameAttr==null) return;
308 String name=nameAttr.getText();
309 QName qname=getQName(name);
310 XSDatatype datatype=loadXSDatatypeFromSimpleType(schemaSimpleType);
311 namedTypeResolver.registerSimpleType(qname,datatype);
312 }
313
314 /*** Loads a XSDatatype object from a <simpleType> attribute schema element */
315 private XSDatatype loadXSDatatypeFromSimpleType( Element xsdSimpleType ) {
316 Element xsdRestriction = xsdSimpleType.element( XSD_RESTRICTION );
317 if ( xsdRestriction != null ) {
318 String base = xsdRestriction.attributeValue( "base" );
319 if ( base != null ) {
320 XSDatatype baseType = getTypeByName( base );
321 if ( baseType == null ) {
322 onSchemaError(
323 "Invalid base type: " + base
324 + " when trying to build restriction: " + xsdRestriction
325 );
326 }
327 else {
328 return deriveSimpleType( baseType, xsdRestriction );
329 }
330 }
331 else {
332
333
334 Element xsdSubType = xsdSimpleType.element( XSD_SIMPLETYPE );
335 if ( xsdSubType == null ) {
336 onSchemaError(
337 "The simpleType element: "+ xsdSimpleType
338 + " must contain a base attribute or simpleType element"
339 );
340 }
341 else {
342 return loadXSDatatypeFromSimpleType( xsdSubType );
343 }
344 }
345 }
346 else {
347 onSchemaError(
348 "No <restriction>. Could not create XSDatatype for simpleType: "
349 + xsdSimpleType
350 );
351 }
352 return null;
353 }
354
355 /*** Derives a new type from a base type and a set of restrictions */
356 private XSDatatype deriveSimpleType( XSDatatype baseType, Element xsdRestriction ) {
357 TypeIncubator incubator = new TypeIncubator(baseType);
358 ValidationContext context = null;
359
360 try {
361 for ( Iterator iter = xsdRestriction.elementIterator(); iter.hasNext(); ) {
362 Element element = (Element) iter.next();
363 String name = element.getName();
364 String value = element.attributeValue( "value" );
365 boolean fixed = AttributeHelper.booleanValue( element, "fixed" );
366
367
368 incubator.addFacet( name, value, fixed, context );
369 }
370
371 String newTypeName = null;
372 return incubator.derive( newTypeName );
373 }
374 catch (DatatypeException e) {
375 onSchemaError(
376 "Invalid restriction: " + e.getMessage()
377 + " when trying to build restriction: " + xsdRestriction
378 );
379 return null;
380 }
381 }
382
383 /*** @return the <code>DatatypeElementFactory</code> for the given
384 * element QName, creating one if it does not already exist
385 */
386 private DatatypeElementFactory getDatatypeElementFactory( QName elementQName ) {
387 DatatypeElementFactory factory = documentFactory.getElementFactory( elementQName );
388 if ( factory == null ) {
389 factory = new DatatypeElementFactory( elementQName );
390 elementQName.setDocumentFactory(factory);
391 }
392 return factory;
393 }
394
395 private XSDatatype getTypeByName( String type ) {
396 XSDatatype dataType = (XSDatatype) dataTypeCache.get( type );
397 if ( dataType == null ) {
398
399
400 int idx = type.indexOf(':');
401 if (idx >= 0 ) {
402 String localName = type.substring(idx + 1);
403 try {
404 dataType = DatatypeFactory.getTypeByName( localName );
405 } catch (DatatypeException e) {}
406 }
407 if ( dataType == null ) {
408 try {
409 dataType = DatatypeFactory.getTypeByName( type );
410 } catch (DatatypeException e) {}
411 }
412
413 if ( dataType == null ) {
414
415 QName typeQName = getQName(type);
416 dataType = (XSDatatype) namedTypeResolver.simpleTypeMap.get( typeQName );
417 }
418
419 if ( dataType != null ) {
420
421 dataTypeCache.put( type, dataType );
422 }
423 }
424
425 return dataType;
426 }
427
428 private QName getQName( String name ) {
429 if (targetNamespace == null) {
430 return documentFactory.createQName(name);
431 } else {
432 return documentFactory.createQName(name, targetNamespace);
433 }
434 }
435
436 /*** Called when there is a problem with the schema and the builder cannot
437 * handle the XML Schema Data Types correctly
438 */
439 private void onSchemaError( String message ) {
440
441
442
443
444 throw new InvalidSchemaException( message );
445 }
446 }
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494