Clover coverage report - dom4j - 1.5
Coverage timestamp: vr sep 3 2004 20:47:03 GMT+01:00
file stats: LOC: 494   Methods: 18
NCLOC: 322   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
SchemaParser.java 67,6% 71,6% 83,3% 71,3%
coverage coverage
 1    /*
 2    * Copyright 2001-2004 (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    * $Id: SchemaParser.java,v 1.17 2004/06/25 08:03:34 maartenc Exp $
 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    // Use QNames for the elements
 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  0 public SchemaParser() {
 67  0 this(DatatypeDocumentFactory.singleton);
 68    }
 69   
 70  54 public SchemaParser(DatatypeDocumentFactory documentFactory) {
 71  54 this.documentFactory = documentFactory;
 72  54 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  44 public void build( Document schemaDocument ) {
 80  44 this.targetNamespace = null;
 81  44 internalBuild( schemaDocument );
 82    }
 83   
 84  6 public void build( Document schemaDocument, Namespace targetNamespace) {
 85  6 this.targetNamespace = targetNamespace;
 86  6 internalBuild( schemaDocument );
 87    }
 88   
 89  50 private synchronized void internalBuild( Document schemaDocument ) {
 90  50 Element root = schemaDocument.getRootElement();
 91  50 if ( root != null ) {
 92    //handle schema includes
 93  50 Iterator includeIter = root.elementIterator( XSD_INCLUDE );
 94  50 while (includeIter.hasNext()) {
 95  0 Element includeElement = (Element) includeIter.next();
 96  0 String inclSchemaInstanceURI = includeElement.attributeValue("schemaLocation");
 97  0 EntityResolver resolver = schemaDocument.getEntityResolver();
 98  0 try {
 99  0 if ( resolver == null ) {
 100  0 throw new InvalidSchemaException( "No EntityResolver available so could not resolve the schema URI: " +
 101    inclSchemaInstanceURI );
 102    }
 103  0 InputSource inputSource = resolver.resolveEntity( null, inclSchemaInstanceURI );
 104  0 if ( inputSource == null ) {
 105  0 throw new InvalidSchemaException( "Could not resolve the schema URI: " + inclSchemaInstanceURI );
 106    }
 107  0 SAXReader reader = new SAXReader();
 108  0 Document inclSchemaDocument = reader.read( inputSource );
 109  0 build( inclSchemaDocument );
 110    }
 111    catch (Exception e) {
 112  0 System.out.println( "Failed to load schema: " + inclSchemaInstanceURI );
 113  0 System.out.println( "Caught: " + e );
 114  0 e.printStackTrace();
 115  0 throw new InvalidSchemaException( "Failed to load schema: " + inclSchemaInstanceURI );
 116    }
 117    }
 118   
 119    //handle elements
 120  50 Iterator iter = root.elementIterator( XSD_ELEMENT );
 121  50 while ( iter.hasNext() ) {
 122  360 onDatatypeElement( (Element) iter.next() , documentFactory);
 123    }
 124   
 125    //handle named simple types
 126  50 iter = root.elementIterator( XSD_SIMPLETYPE );
 127  50 while ( iter.hasNext() ) {
 128  2 onNamedSchemaSimpleType((Element) iter.next());
 129    }
 130   
 131    //hanlde named complex types
 132  50 iter = root.elementIterator( XSD_COMPLEXTYPE );
 133  50 while ( iter.hasNext() ) {
 134  4 onNamedSchemaComplexType((Element) iter.next());
 135    }
 136   
 137  50 namedTypeResolver.resolveNamedTypes();
 138   
 139    }
 140    }
 141   
 142   
 143    // Implementation methods
 144    //-------------------------------------------------------------------------
 145   
 146    /** processes an XML Schema &lt;element&gt; tag
 147    */
 148  812 private void onDatatypeElement( Element xsdElement , DocumentFactory parentFactory ) {
 149  812 String name = xsdElement.attributeValue( "name" );
 150  812 String type = xsdElement.attributeValue( "type" );
 151  812 QName qname = getQName( name );
 152   
 153  812 DatatypeElementFactory elementFactory = getDatatypeElementFactory( qname );
 154   
 155  812 if ( type != null ) {
 156    // register type with this element name
 157  292 XSDatatype dataType = getTypeByName(type);
 158  292 if (dataType!=null) {
 159  286 elementFactory.setChildElementXSDatatype( qname, dataType );
 160    }
 161    else {
 162  6 QName typeQName=getQName(type);
 163  6 namedTypeResolver.registerTypedElement(xsdElement,typeQName,parentFactory);
 164    }
 165  292 return;
 166    }
 167   
 168    // handle element types derrived from simpleTypes
 169  520 Element xsdSimpleType = xsdElement.element( XSD_SIMPLETYPE );
 170  520 if ( xsdSimpleType != null ) {
 171  0 System.out.println("Agfa-sg: handle element types derrived from simpleTypes for element: " + name);
 172  0 XSDatatype dataType = loadXSDatatypeFromSimpleType( xsdSimpleType );
 173  0 if (dataType != null) {
 174  0 System.out.println("dataType (from loadXSDatatypeFromSimpleType) = " + dataType);
 175  0 elementFactory.setChildElementXSDatatype( qname, dataType );
 176    }
 177    }
 178   
 179  520 Element schemaComplexType = xsdElement.element( XSD_COMPLEXTYPE );
 180  520 if ( schemaComplexType != null ) {
 181  214 onSchemaComplexType( schemaComplexType, elementFactory );
 182    }
 183   
 184  520 Iterator iter = xsdElement.elementIterator( XSD_ATTRIBUTE );
 185  520 if ( iter.hasNext() ) {
 186  0 do {
 187  0 onDatatypeAttribute(
 188    xsdElement,
 189    elementFactory,
 190    (Element) iter.next()
 191    );
 192    }
 193  0 while ( iter.hasNext() );
 194    }
 195    }
 196   
 197    /** processes an named XML Schema &lt;complexTypegt; tag
 198    */
 199  4 private void onNamedSchemaComplexType(Element schemaComplexType) {
 200  4 Attribute nameAttr=schemaComplexType.attribute("name");
 201  0 if (nameAttr==null) return;
 202  4 String name=nameAttr.getText();
 203  4 QName qname=getQName(name);
 204   
 205  4 DatatypeElementFactory elementFactory = getDatatypeElementFactory( qname );
 206    //DatatypeElementFactory elementFactory=new DatatypeElementFactory(qname);
 207   
 208  4 onSchemaComplexType(schemaComplexType,elementFactory);
 209  4 namedTypeResolver.registerComplexType(qname,elementFactory);
 210    }
 211   
 212    /** processes an XML Schema &lt;complexTypegt; tag
 213    */
 214  218 private void onSchemaComplexType( Element schemaComplexType, DatatypeElementFactory elementFactory ) {
 215  218 Iterator iter = schemaComplexType.elementIterator( XSD_ATTRIBUTE );
 216  218 while ( iter.hasNext() ) {
 217  530 Element xsdAttribute = (Element) iter.next();
 218  530 String name = xsdAttribute.attributeValue( "name" );
 219  530 QName qname = getQName( name );
 220   
 221  530 XSDatatype dataType = dataTypeForXsdAttribute( xsdAttribute );
 222  530 if ( dataType != null ) {
 223    // register the XSDatatype for the given Attribute
 224    // #### should both these be done?
 225    //elementFactory.setChildElementXSDatatype( qname, dataType );
 226  530 elementFactory.setAttributeXSDatatype( qname, dataType );
 227    }
 228    else {
 229  0 String type = xsdAttribute.attributeValue( "type" );
 230  0 System.out.println( "Warning: Couldn't find XSDatatype for type: " + type + " attribute: " + name );
 231    }
 232    }
 233   
 234    //handle sequence definition
 235  218 Element schemaSequence = schemaComplexType.element( XSD_SEQUENCE );
 236  218 if (schemaSequence!=null) {
 237  98 onChildElements(schemaSequence,elementFactory);
 238    }
 239   
 240    //handle choice definition
 241  218 Element schemaChoice = schemaComplexType.element( XSD_CHOICE );
 242  218 if (schemaChoice!=null) {
 243  0 onChildElements(schemaChoice,elementFactory);
 244    }
 245   
 246    //handle all definition
 247  218 Element schemaAll = schemaComplexType.element( XSD_ALL );
 248  218 if (schemaAll!=null) {
 249  38 onChildElements(schemaAll,elementFactory);
 250    }
 251    }
 252   
 253  136 private void onChildElements(Element element,DatatypeElementFactory factory) {
 254  136 Iterator iter = element.elementIterator( XSD_ELEMENT );
 255  136 while ( iter.hasNext() ) {
 256  452 Element xsdElement = (Element) iter.next();
 257  452 onDatatypeElement(xsdElement,factory);
 258    }
 259    }
 260   
 261    /** processes an XML Schema &lt;attribute&gt; tag
 262    */
 263  0 private void onDatatypeAttribute(
 264    Element xsdElement,
 265    DatatypeElementFactory elementFactory,
 266    Element xsdAttribute
 267    ) {
 268  0 String name = xsdAttribute.attributeValue( "name" );
 269  0 QName qname = getQName( name );
 270  0 XSDatatype dataType = dataTypeForXsdAttribute( xsdAttribute );
 271  0 if ( dataType != null ) {
 272    // register the XSDatatype for the given Attribute
 273  0 elementFactory.setAttributeXSDatatype( qname, dataType );
 274    }
 275    else {
 276  0 String type = xsdAttribute.attributeValue( "type" );
 277  0 System.out.println( "Warning: Couldn't find XSDatatype for type: " + type + " attribute: " + name );
 278    }
 279    }
 280   
 281    /** processes an XML Schema &lt;attribute&gt; tag
 282    */
 283  530 private XSDatatype dataTypeForXsdAttribute( Element xsdAttribute ) {
 284  530 String type = xsdAttribute.attributeValue( "type" );
 285  530 XSDatatype dataType = null;
 286  530 if ( type != null ) {
 287  418 dataType = getTypeByName( type );
 288    }
 289    else {
 290    // must parse the <simpleType> element
 291  112 Element xsdSimpleType = xsdAttribute.element( XSD_SIMPLETYPE );
 292  112 if ( xsdSimpleType == null ) {
 293  0 String name = xsdAttribute.attributeValue( "name" );
 294  0 throw new InvalidSchemaException(
 295    "The attribute: " + name + " has no type attribute and does not contain a <simpleType/> element"
 296    );
 297    }
 298  112 dataType = loadXSDatatypeFromSimpleType( xsdSimpleType );
 299    }
 300  530 return dataType;
 301    }
 302   
 303    /** processes an named XML Schema &lt;simpleTypegt; tag
 304    */
 305  2 private void onNamedSchemaSimpleType(Element schemaSimpleType) {
 306  2 Attribute nameAttr=schemaSimpleType.attribute("name");
 307  0 if (nameAttr==null) return;
 308  2 String name=nameAttr.getText();
 309  2 QName qname=getQName(name);
 310  2 XSDatatype datatype=loadXSDatatypeFromSimpleType(schemaSimpleType);
 311  2 namedTypeResolver.registerSimpleType(qname,datatype);
 312    }
 313   
 314    /** Loads a XSDatatype object from a <simpleType> attribute schema element */
 315  114 private XSDatatype loadXSDatatypeFromSimpleType( Element xsdSimpleType ) {
 316  114 Element xsdRestriction = xsdSimpleType.element( XSD_RESTRICTION );
 317  114 if ( xsdRestriction != null ) {
 318  114 String base = xsdRestriction.attributeValue( "base" );
 319  114 if ( base != null ) {
 320  114 XSDatatype baseType = getTypeByName( base );
 321  114 if ( baseType == null ) {
 322  0 onSchemaError(
 323    "Invalid base type: " + base
 324    + " when trying to build restriction: " + xsdRestriction
 325    );
 326    }
 327    else {
 328  114 return deriveSimpleType( baseType, xsdRestriction );
 329    }
 330    }
 331    else {
 332    // simpleType and base are mutually exclusive and you
 333    // must have one within a <restriction> tag
 334  0 Element xsdSubType = xsdSimpleType.element( XSD_SIMPLETYPE );
 335  0 if ( xsdSubType == null ) {
 336  0 onSchemaError(
 337    "The simpleType element: "+ xsdSimpleType
 338    + " must contain a base attribute or simpleType element"
 339    );
 340    }
 341    else {
 342  0 return loadXSDatatypeFromSimpleType( xsdSubType );
 343    }
 344    }
 345    }
 346    else {
 347  0 onSchemaError(
 348    "No <restriction>. Could not create XSDatatype for simpleType: "
 349    + xsdSimpleType
 350    );
 351    }
 352  0 return null;
 353    }
 354   
 355    /** Derives a new type from a base type and a set of restrictions */
 356  114 private XSDatatype deriveSimpleType( XSDatatype baseType, Element xsdRestriction ) {
 357  114 TypeIncubator incubator = new TypeIncubator(baseType);
 358  114 ValidationContext context = null;
 359   
 360  114 try {
 361  114 for ( Iterator iter = xsdRestriction.elementIterator(); iter.hasNext(); ) {
 362  206 Element element = (Element) iter.next();
 363  206 String name = element.getName();
 364  206 String value = element.attributeValue( "value" );
 365  206 boolean fixed = AttributeHelper.booleanValue( element, "fixed" );
 366   
 367    // add facet
 368  206 incubator.addFacet( name, value, fixed, context );
 369    }
 370    // derive a new type by those facets
 371  114 String newTypeName = null;
 372  114 return incubator.derive( newTypeName );
 373    }
 374    catch (DatatypeException e) {
 375  0 onSchemaError(
 376    "Invalid restriction: " + e.getMessage()
 377    + " when trying to build restriction: " + xsdRestriction
 378    );
 379  0 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  816 private DatatypeElementFactory getDatatypeElementFactory( QName elementQName ) {
 387  816 DatatypeElementFactory factory = documentFactory.getElementFactory( elementQName );
 388  816 if ( factory == null ) {
 389  806 factory = new DatatypeElementFactory( elementQName );
 390  806 elementQName.setDocumentFactory(factory);
 391    }
 392  816 return factory;
 393    }
 394   
 395  824 private XSDatatype getTypeByName( String type ) {
 396  824 XSDatatype dataType = (XSDatatype) dataTypeCache.get( type );
 397  824 if ( dataType == null ) {
 398    // first check to see if it is a built-in type
 399    // maybe a prefix is being used
 400  380 int idx = type.indexOf(':');
 401  380 if (idx >= 0 ) {
 402  100 String localName = type.substring(idx + 1);
 403  100 try {
 404  100 dataType = DatatypeFactory.getTypeByName( localName );
 405    } catch (DatatypeException e) {}
 406    }
 407  380 if ( dataType == null ) {
 408  280 try {
 409  280 dataType = DatatypeFactory.getTypeByName( type );
 410    } catch (DatatypeException e) {}
 411    }
 412   
 413  380 if ( dataType == null ) {
 414    // it's no built-in type, maybe it's a type we defined ourself
 415  6 QName typeQName = getQName(type);
 416  6 dataType = (XSDatatype) namedTypeResolver.simpleTypeMap.get( typeQName );
 417    }
 418   
 419  380 if ( dataType != null ) {
 420    // store in cache for later
 421  374 dataTypeCache.put( type, dataType );
 422    }
 423    }
 424   
 425  824 return dataType;
 426    }
 427   
 428  1360 private QName getQName( String name ) {
 429  1360 if (targetNamespace == null) {
 430  1192 return documentFactory.createQName(name);
 431    } else {
 432  168 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  0 private void onSchemaError( String message ) {
 440    // Some users may wish to disable exception throwing
 441    // and instead use some kind of listener for errors and continue
 442    //System.out.println( "WARNING: " + message );
 443   
 444  0 throw new InvalidSchemaException( message );
 445    }
 446    }
 447   
 448   
 449   
 450   
 451    /*
 452    * Redistribution and use of this software and associated documentation
 453    * ("Software"), with or without modification, are permitted provided
 454    * that the following conditions are met:
 455    *
 456    * 1. Redistributions of source code must retain copyright
 457    * statements and notices. Redistributions must also contain a
 458    * copy of this document.
 459    *
 460    * 2. Redistributions in binary form must reproduce the
 461    * above copyright notice, this list of conditions and the
 462    * following disclaimer in the documentation and/or other
 463    * materials provided with the distribution.
 464    *
 465    * 3. The name "DOM4J" must not be used to endorse or promote
 466    * products derived from this Software without prior written
 467    * permission of MetaStuff, Ltd. For written permission,
 468    * please contact dom4j-info@metastuff.com.
 469    *
 470    * 4. Products derived from this Software may not be called "DOM4J"
 471    * nor may "DOM4J" appear in their names without prior written
 472    * permission of MetaStuff, Ltd. DOM4J is a registered
 473    * trademark of MetaStuff, Ltd.
 474    *
 475    * 5. Due credit should be given to the DOM4J Project -
 476    * http://www.dom4j.org
 477    *
 478    * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
 479    * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
 480    * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 481    * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 482    * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 483    * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 484    * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 485    * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 486    * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 487    * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 488    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 489    * OF THE POSSIBILITY OF SUCH DAMAGE.
 490    *
 491    * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
 492    *
 493    * $Id: SchemaParser.java,v 1.17 2004/06/25 08:03:34 maartenc Exp $
 494    */