Clover coverage report - dom4j - 1.6.1
Coverage timestamp: ma mei 16 2005 14:23:01 GMT+01:00
file stats: LOC: 607   Methods: 18
NCLOC: 324   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
SchemaParser.java 67,6% 71,6% 83,3% 71,3%
coverage coverage
 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;
 15    import java.util.Iterator;
 16    import java.util.Map;
 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;
 31    import org.xml.sax.InputSource;
 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 dataTypeCache = new HashMap();
 80   
 81    /** NamedTypeResolver */
 82    private NamedTypeResolver namedTypeResolver;
 83   
 84    /** target namespace */
 85    private Namespace targetNamespace;
 86   
 87  0 public SchemaParser() {
 88  0 this(DatatypeDocumentFactory.singleton);
 89    }
 90   
 91  27 public SchemaParser(DatatypeDocumentFactory documentFactory) {
 92  27 this.documentFactory = documentFactory;
 93  27 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  22 public void build(Document schemaDocument) {
 103  22 this.targetNamespace = null;
 104  22 internalBuild(schemaDocument);
 105    }
 106   
 107  3 public void build(Document schemaDocument, Namespace namespace) {
 108  3 this.targetNamespace = namespace;
 109  3 internalBuild(schemaDocument);
 110    }
 111   
 112  25 private synchronized void internalBuild(Document schemaDocument) {
 113  25 Element root = schemaDocument.getRootElement();
 114   
 115  25 if (root != null) {
 116    // handle schema includes
 117  25 Iterator includeIter = root.elementIterator(XSD_INCLUDE);
 118   
 119  25 while (includeIter.hasNext()) {
 120  0 Element includeElement = (Element) includeIter.next();
 121  0 String inclSchemaInstanceURI = includeElement
 122    .attributeValue("schemaLocation");
 123  0 EntityResolver resolver = schemaDocument.getEntityResolver();
 124   
 125  0 try {
 126  0 if (resolver == null) {
 127  0 String msg = "No EntityResolver available";
 128  0 throw new InvalidSchemaException(msg);
 129    }
 130   
 131  0 InputSource inputSource = resolver.resolveEntity(null,
 132    inclSchemaInstanceURI);
 133   
 134  0 if (inputSource == null) {
 135  0 String msg = "Could not resolve the schema URI: "
 136    + inclSchemaInstanceURI;
 137  0 throw new InvalidSchemaException(msg);
 138    }
 139   
 140  0 SAXReader reader = new SAXReader();
 141  0 Document inclSchemaDocument = reader.read(inputSource);
 142  0 build(inclSchemaDocument);
 143    } catch (Exception e) {
 144  0 System.out.println("Failed to load schema: "
 145    + inclSchemaInstanceURI);
 146  0 System.out.println("Caught: " + e);
 147  0 e.printStackTrace();
 148  0 throw new InvalidSchemaException("Failed to load schema: "
 149    + inclSchemaInstanceURI);
 150    }
 151    }
 152   
 153    // handle elements
 154  25 Iterator iter = root.elementIterator(XSD_ELEMENT);
 155   
 156  25 while (iter.hasNext()) {
 157  180 onDatatypeElement((Element) iter.next(), documentFactory);
 158    }
 159   
 160    // handle named simple types
 161  25 iter = root.elementIterator(XSD_SIMPLETYPE);
 162   
 163  25 while (iter.hasNext()) {
 164  1 onNamedSchemaSimpleType((Element) iter.next());
 165    }
 166   
 167    // hanlde named complex types
 168  25 iter = root.elementIterator(XSD_COMPLEXTYPE);
 169   
 170  25 while (iter.hasNext()) {
 171  2 onNamedSchemaComplexType((Element) iter.next());
 172    }
 173   
 174  25 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  406 private void onDatatypeElement(Element xsdElement,
 190    DocumentFactory parentFactory) {
 191  406 String name = xsdElement.attributeValue("name");
 192  406 String type = xsdElement.attributeValue("type");
 193  406 QName qname = getQName(name);
 194   
 195  406 DatatypeElementFactory factory = getDatatypeElementFactory(qname);
 196   
 197  406 if (type != null) {
 198    // register type with this element name
 199  146 XSDatatype dataType = getTypeByName(type);
 200   
 201  146 if (dataType != null) {
 202  143 factory.setChildElementXSDatatype(qname, dataType);
 203    } else {
 204  3 QName typeQName = getQName(type);
 205  3 namedTypeResolver.registerTypedElement(xsdElement, typeQName,
 206    parentFactory);
 207    }
 208   
 209  146 return;
 210    }
 211   
 212    // handle element types derrived from simpleTypes
 213  260 Element xsdSimpleType = xsdElement.element(XSD_SIMPLETYPE);
 214   
 215  260 if (xsdSimpleType != null) {
 216  0 XSDatatype dataType = loadXSDatatypeFromSimpleType(xsdSimpleType);
 217   
 218  0 if (dataType != null) {
 219  0 factory.setChildElementXSDatatype(qname, dataType);
 220    }
 221    }
 222   
 223  260 Element schemaComplexType = xsdElement.element(XSD_COMPLEXTYPE);
 224   
 225  260 if (schemaComplexType != null) {
 226  107 onSchemaComplexType(schemaComplexType, factory);
 227    }
 228   
 229  260 Iterator iter = xsdElement.elementIterator(XSD_ATTRIBUTE);
 230   
 231  260 if (iter.hasNext()) {
 232  0 do {
 233  0 onDatatypeAttribute(xsdElement, factory, (Element) iter
 234    .next());
 235  0 } 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  2 private void onNamedSchemaComplexType(Element schemaComplexType) {
 246  2 Attribute nameAttr = schemaComplexType.attribute("name");
 247   
 248  2 if (nameAttr == null) {
 249  0 return;
 250    }
 251   
 252  2 String name = nameAttr.getText();
 253  2 QName qname = getQName(name);
 254   
 255  2 DatatypeElementFactory factory = getDatatypeElementFactory(qname);
 256   
 257  2 onSchemaComplexType(schemaComplexType, factory);
 258  2 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  109 private void onSchemaComplexType(Element schemaComplexType,
 270    DatatypeElementFactory elementFactory) {
 271  109 Iterator iter = schemaComplexType.elementIterator(XSD_ATTRIBUTE);
 272   
 273  109 while (iter.hasNext()) {
 274  265 Element xsdAttribute = (Element) iter.next();
 275  265 String name = xsdAttribute.attributeValue("name");
 276  265 QName qname = getQName(name);
 277   
 278  265 XSDatatype dataType = dataTypeForXsdAttribute(xsdAttribute);
 279   
 280  265 if (dataType != null) {
 281    // register the XSDatatype for the given Attribute
 282    // #### should both these be done?
 283    // elementFactory.setChildElementXSDatatype( qname, dataType );
 284  265 elementFactory.setAttributeXSDatatype(qname, dataType);
 285    }
 286    }
 287   
 288    // handle sequence definition
 289  109 Element schemaSequence = schemaComplexType.element(XSD_SEQUENCE);
 290   
 291  109 if (schemaSequence != null) {
 292  49 onChildElements(schemaSequence, elementFactory);
 293    }
 294   
 295    // handle choice definition
 296  109 Element schemaChoice = schemaComplexType.element(XSD_CHOICE);
 297   
 298  109 if (schemaChoice != null) {
 299  0 onChildElements(schemaChoice, elementFactory);
 300    }
 301   
 302    // handle all definition
 303  109 Element schemaAll = schemaComplexType.element(XSD_ALL);
 304   
 305  109 if (schemaAll != null) {
 306  19 onChildElements(schemaAll, elementFactory);
 307    }
 308    }
 309   
 310  68 private void onChildElements(Element element, DatatypeElementFactory fact) {
 311  68 Iterator iter = element.elementIterator(XSD_ELEMENT);
 312   
 313  68 while (iter.hasNext()) {
 314  226 Element xsdElement = (Element) iter.next();
 315  226 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  0 private void onDatatypeAttribute(Element xsdElement,
 330    DatatypeElementFactory elementFactory, Element xsdAttribute) {
 331  0 String name = xsdAttribute.attributeValue("name");
 332  0 QName qname = getQName(name);
 333  0 XSDatatype dataType = dataTypeForXsdAttribute(xsdAttribute);
 334   
 335  0 if (dataType != null) {
 336    // register the XSDatatype for the given Attribute
 337  0 elementFactory.setAttributeXSDatatype(qname, dataType);
 338    } else {
 339  0 String type = xsdAttribute.attributeValue("type");
 340  0 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  265 private XSDatatype dataTypeForXsdAttribute(Element xsdAttribute) {
 357  265 String type = xsdAttribute.attributeValue("type");
 358  265 XSDatatype dataType = null;
 359   
 360  265 if (type != null) {
 361  209 dataType = getTypeByName(type);
 362    } else {
 363    // must parse the <simpleType> element
 364  56 Element xsdSimpleType = xsdAttribute.element(XSD_SIMPLETYPE);
 365   
 366  56 if (xsdSimpleType == null) {
 367  0 String name = xsdAttribute.attributeValue("name");
 368  0 String msg = "The attribute: " + name
 369    + " has no type attribute and does not contain a "
 370    + "<simpleType/> element";
 371  0 throw new InvalidSchemaException(msg);
 372    }
 373   
 374  56 dataType = loadXSDatatypeFromSimpleType(xsdSimpleType);
 375    }
 376   
 377  265 return dataType;
 378    }
 379   
 380    /**
 381    * processes an named XML Schema &lt;simpleTypegt; tag
 382    *
 383    * @param schemaSimpleType
 384    * DOCUMENT ME!
 385    */
 386  1 private void onNamedSchemaSimpleType(Element schemaSimpleType) {
 387  1 Attribute nameAttr = schemaSimpleType.attribute("name");
 388   
 389  1 if (nameAttr == null) {
 390  0 return;
 391    }
 392   
 393  1 String name = nameAttr.getText();
 394  1 QName qname = getQName(name);
 395  1 XSDatatype datatype = loadXSDatatypeFromSimpleType(schemaSimpleType);
 396  1 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  57 private XSDatatype loadXSDatatypeFromSimpleType(Element xsdSimpleType) {
 409  57 Element xsdRestriction = xsdSimpleType.element(XSD_RESTRICTION);
 410   
 411  57 if (xsdRestriction != null) {
 412  57 String base = xsdRestriction.attributeValue("base");
 413   
 414  57 if (base != null) {
 415  57 XSDatatype baseType = getTypeByName(base);
 416   
 417  57 if (baseType == null) {
 418  0 onSchemaError("Invalid base type: " + base
 419    + " when trying to build restriction: "
 420    + xsdRestriction);
 421    } else {
 422  57 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  0 Element xsdSubType = xsdSimpleType.element(XSD_SIMPLETYPE);
 428   
 429  0 if (xsdSubType == null) {
 430  0 String msg = "The simpleType element: " + xsdSimpleType
 431    + " must contain a base attribute or simpleType"
 432    + " element";
 433  0 onSchemaError(msg);
 434    } else {
 435  0 return loadXSDatatypeFromSimpleType(xsdSubType);
 436    }
 437    }
 438    } else {
 439  0 onSchemaError("No <restriction>. Could not create XSDatatype for"
 440    + " simpleType: " + xsdSimpleType);
 441    }
 442   
 443  0 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  57 private XSDatatype deriveSimpleType(XSDatatype baseType,
 457    Element xsdRestriction) {
 458  57 TypeIncubator incubator = new TypeIncubator(baseType);
 459  57 ValidationContext context = null;
 460   
 461  57 try {
 462  57 for (Iterator iter = xsdRestriction.elementIterator(); iter
 463    .hasNext();) {
 464  103 Element element = (Element) iter.next();
 465  103 String name = element.getName();
 466  103 String value = element.attributeValue("value");
 467  103 boolean fixed = AttributeHelper.booleanValue(element, "fixed");
 468   
 469    // add facet
 470  103 incubator.addFacet(name, value, fixed, context);
 471    }
 472   
 473    // derive a new type by those facets
 474  57 String newTypeName = null;
 475   
 476  57 return incubator.derive("", newTypeName);
 477    } catch (DatatypeException e) {
 478  0 onSchemaError("Invalid restriction: " + e.getMessage()
 479    + " when trying to build restriction: " + xsdRestriction);
 480   
 481  0 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  408 private DatatypeElementFactory getDatatypeElementFactory(QName name) {
 495  408 DatatypeElementFactory factory = documentFactory
 496    .getElementFactory(name);
 497   
 498  408 if (factory == null) {
 499  403 factory = new DatatypeElementFactory(name);
 500  403 name.setDocumentFactory(factory);
 501    }
 502   
 503  408 return factory;
 504    }
 505   
 506  412 private XSDatatype getTypeByName(String type) {
 507  412 XSDatatype dataType = (XSDatatype) dataTypeCache.get(type);
 508   
 509  412 if (dataType == null) {
 510    // first check to see if it is a built-in type
 511    // maybe a prefix is being used
 512  190 int idx = type.indexOf(':');
 513   
 514  190 if (idx >= 0) {
 515  50 String localName = type.substring(idx + 1);
 516   
 517  50 try {
 518  50 dataType = DatatypeFactory.getTypeByName(localName);
 519    } catch (DatatypeException e) {
 520    }
 521    }
 522   
 523  190 if (dataType == null) {
 524  140 try {
 525  140 dataType = DatatypeFactory.getTypeByName(type);
 526    } catch (DatatypeException e) {
 527    }
 528    }
 529   
 530  190 if (dataType == null) {
 531    // it's no built-in type, maybe it's a type we defined ourself
 532  3 QName typeQName = getQName(type);
 533  3 dataType = (XSDatatype) namedTypeResolver.simpleTypeMap
 534    .get(typeQName);
 535    }
 536   
 537  190 if (dataType != null) {
 538    // store in cache for later
 539  187 dataTypeCache.put(type, dataType);
 540    }
 541    }
 542   
 543  412 return dataType;
 544    }
 545   
 546  680 private QName getQName(String name) {
 547  680 if (targetNamespace == null) {
 548  596 return documentFactory.createQName(name);
 549    } else {
 550  84 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  0 private void onSchemaError(String 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  0 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    */