All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dom4j.datatype.SchemaParser Maven / Gradle / Ivy

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

package org.dom4j.datatype;

import com.sun.msv.datatype.xsd.DatatypeFactory;
import com.sun.msv.datatype.xsd.TypeIncubator;
import com.sun.msv.datatype.xsd.XSDatatype;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.SAXReader;
import org.dom4j.util.AttributeHelper;

import org.relaxng.datatype.DatatypeException;
import org.relaxng.datatype.ValidationContext;

import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

/**
 * 

* SchemaParser reads an XML Schema Document. *

* * @author James Strachan * @author Yuxin Ruan * @version $Revision: 1.19 $ */ public class SchemaParser { private static final Namespace XSD_NAMESPACE = Namespace.get("xsd", "http://www.w3.org/2001/XMLSchema"); // Use QNames for the elements private static final QName XSD_ELEMENT = QName .get("element", XSD_NAMESPACE); private static final QName XSD_ATTRIBUTE = QName.get("attribute", XSD_NAMESPACE); private static final QName XSD_SIMPLETYPE = QName.get("simpleType", XSD_NAMESPACE); private static final QName XSD_COMPLEXTYPE = QName.get("complexType", XSD_NAMESPACE); private static final QName XSD_RESTRICTION = QName.get("restriction", XSD_NAMESPACE); private static final QName XSD_SEQUENCE = QName.get("sequence", XSD_NAMESPACE); private static final QName XSD_CHOICE = QName.get("choice", XSD_NAMESPACE); private static final QName XSD_ALL = QName.get("all", XSD_NAMESPACE); private static final QName XSD_INCLUDE = QName .get("include", XSD_NAMESPACE); /** Document factory used to register Element specific factories */ private DatatypeDocumentFactory documentFactory; /** * Cache of XSDatatype instances loaded or created during * this build */ private Map dataTypeCache = new HashMap(); /** NamedTypeResolver */ private NamedTypeResolver namedTypeResolver; /** target namespace */ private Namespace targetNamespace; public SchemaParser() { this(DatatypeDocumentFactory.singleton); } public SchemaParser(DatatypeDocumentFactory documentFactory) { this.documentFactory = documentFactory; this.namedTypeResolver = new NamedTypeResolver(documentFactory); } /** * Parses the given schema document * * @param schemaDocument * is the document of the XML Schema */ public void build(Document schemaDocument) { this.targetNamespace = null; internalBuild(schemaDocument); } public void build(Document schemaDocument, Namespace namespace) { this.targetNamespace = namespace; internalBuild(schemaDocument); } private synchronized void internalBuild(Document schemaDocument) { Element root = schemaDocument.getRootElement(); if (root != null) { // handle schema includes Iterator includeIter = root.elementIterator(XSD_INCLUDE); while (includeIter.hasNext()) { Element includeElement = (Element) includeIter.next(); String inclSchemaInstanceURI = includeElement .attributeValue("schemaLocation"); EntityResolver resolver = schemaDocument.getEntityResolver(); try { if (resolver == null) { String msg = "No EntityResolver available"; throw new InvalidSchemaException(msg); } InputSource inputSource = resolver.resolveEntity(null, inclSchemaInstanceURI); if (inputSource == null) { String msg = "Could not resolve the schema URI: " + inclSchemaInstanceURI; throw new InvalidSchemaException(msg); } SAXReader reader = new SAXReader(); Document inclSchemaDocument = reader.read(inputSource); build(inclSchemaDocument); } catch (Exception e) { System.out.println("Failed to load schema: " + inclSchemaInstanceURI); System.out.println("Caught: " + e); e.printStackTrace(); throw new InvalidSchemaException("Failed to load schema: " + inclSchemaInstanceURI); } } // handle elements Iterator iter = root.elementIterator(XSD_ELEMENT); while (iter.hasNext()) { onDatatypeElement((Element) iter.next(), documentFactory); } // handle named simple types iter = root.elementIterator(XSD_SIMPLETYPE); while (iter.hasNext()) { onNamedSchemaSimpleType((Element) iter.next()); } // hanlde named complex types iter = root.elementIterator(XSD_COMPLEXTYPE); while (iter.hasNext()) { onNamedSchemaComplexType((Element) iter.next()); } namedTypeResolver.resolveNamedTypes(); } } // Implementation methods // ------------------------------------------------------------------------- /** * processes an XML Schema <element> tag * * @param xsdElement * DOCUMENT ME! * @param parentFactory * DOCUMENT ME! */ private void onDatatypeElement(Element xsdElement, DocumentFactory parentFactory) { String name = xsdElement.attributeValue("name"); String type = xsdElement.attributeValue("type"); QName qname = getQName(name); DatatypeElementFactory factory = getDatatypeElementFactory(qname); if (type != null) { // register type with this element name XSDatatype dataType = getTypeByName(type); if (dataType != null) { factory.setChildElementXSDatatype(qname, dataType); } else { QName typeQName = getQName(type); namedTypeResolver.registerTypedElement(xsdElement, typeQName, parentFactory); } return; } // handle element types derrived from simpleTypes Element xsdSimpleType = xsdElement.element(XSD_SIMPLETYPE); if (xsdSimpleType != null) { XSDatatype dataType = loadXSDatatypeFromSimpleType(xsdSimpleType); if (dataType != null) { factory.setChildElementXSDatatype(qname, dataType); } } Element schemaComplexType = xsdElement.element(XSD_COMPLEXTYPE); if (schemaComplexType != null) { onSchemaComplexType(schemaComplexType, factory); } Iterator iter = xsdElement.elementIterator(XSD_ATTRIBUTE); if (iter.hasNext()) { do { onDatatypeAttribute(xsdElement, factory, (Element) iter .next()); } while (iter.hasNext()); } } /** * processes an named XML Schema <complexTypegt; tag * * @param schemaComplexType * DOCUMENT ME! */ private void onNamedSchemaComplexType(Element schemaComplexType) { Attribute nameAttr = schemaComplexType.attribute("name"); if (nameAttr == null) { return; } String name = nameAttr.getText(); QName qname = getQName(name); DatatypeElementFactory factory = getDatatypeElementFactory(qname); onSchemaComplexType(schemaComplexType, factory); namedTypeResolver.registerComplexType(qname, factory); } /** * processes an XML Schema <complexTypegt; tag * * @param schemaComplexType * DOCUMENT ME! * @param elementFactory * DOCUMENT ME! */ private void onSchemaComplexType(Element schemaComplexType, DatatypeElementFactory elementFactory) { Iterator iter = schemaComplexType.elementIterator(XSD_ATTRIBUTE); while (iter.hasNext()) { Element xsdAttribute = (Element) iter.next(); String name = xsdAttribute.attributeValue("name"); QName qname = getQName(name); XSDatatype dataType = dataTypeForXsdAttribute(xsdAttribute); if (dataType != null) { // register the XSDatatype for the given Attribute // #### should both these be done? // elementFactory.setChildElementXSDatatype( qname, dataType ); elementFactory.setAttributeXSDatatype(qname, dataType); } } // handle sequence definition Element schemaSequence = schemaComplexType.element(XSD_SEQUENCE); if (schemaSequence != null) { onChildElements(schemaSequence, elementFactory); } // handle choice definition Element schemaChoice = schemaComplexType.element(XSD_CHOICE); if (schemaChoice != null) { onChildElements(schemaChoice, elementFactory); } // handle all definition Element schemaAll = schemaComplexType.element(XSD_ALL); if (schemaAll != null) { onChildElements(schemaAll, elementFactory); } } private void onChildElements(Element element, DatatypeElementFactory fact) { Iterator iter = element.elementIterator(XSD_ELEMENT); while (iter.hasNext()) { Element xsdElement = (Element) iter.next(); onDatatypeElement(xsdElement, fact); } } /** * processes an XML Schema <attribute> tag * * @param xsdElement * DOCUMENT ME! * @param elementFactory * DOCUMENT ME! * @param xsdAttribute * DOCUMENT ME! */ private void onDatatypeAttribute(Element xsdElement, DatatypeElementFactory elementFactory, Element xsdAttribute) { String name = xsdAttribute.attributeValue("name"); QName qname = getQName(name); XSDatatype dataType = dataTypeForXsdAttribute(xsdAttribute); if (dataType != null) { // register the XSDatatype for the given Attribute elementFactory.setAttributeXSDatatype(qname, dataType); } else { String type = xsdAttribute.attributeValue("type"); System.out.println("Warning: Couldn't find XSDatatype for type: " + type + " attribute: " + name); } } /** * processes an XML Schema <attribute> tag * * @param xsdAttribute * DOCUMENT ME! * * @return DOCUMENT ME! * * @throws InvalidSchemaException * DOCUMENT ME! */ private XSDatatype dataTypeForXsdAttribute(Element xsdAttribute) { String type = xsdAttribute.attributeValue("type"); XSDatatype dataType = null; if (type != null) { dataType = getTypeByName(type); } else { // must parse the element Element xsdSimpleType = xsdAttribute.element(XSD_SIMPLETYPE); if (xsdSimpleType == null) { String name = xsdAttribute.attributeValue("name"); String msg = "The attribute: " + name + " has no type attribute and does not contain a " + " element"; throw new InvalidSchemaException(msg); } dataType = loadXSDatatypeFromSimpleType(xsdSimpleType); } return dataType; } /** * processes an named XML Schema <simpleTypegt; tag * * @param schemaSimpleType * DOCUMENT ME! */ private void onNamedSchemaSimpleType(Element schemaSimpleType) { Attribute nameAttr = schemaSimpleType.attribute("name"); if (nameAttr == null) { return; } String name = nameAttr.getText(); QName qname = getQName(name); XSDatatype datatype = loadXSDatatypeFromSimpleType(schemaSimpleType); namedTypeResolver.registerSimpleType(qname, datatype); } /** * Loads a XSDatatype object from a <simpleType> attribute schema * element * * @param xsdSimpleType * DOCUMENT ME! * * @return DOCUMENT ME! */ private XSDatatype loadXSDatatypeFromSimpleType(Element xsdSimpleType) { Element xsdRestriction = xsdSimpleType.element(XSD_RESTRICTION); if (xsdRestriction != null) { String base = xsdRestriction.attributeValue("base"); if (base != null) { XSDatatype baseType = getTypeByName(base); if (baseType == null) { onSchemaError("Invalid base type: " + base + " when trying to build restriction: " + xsdRestriction); } else { return deriveSimpleType(baseType, xsdRestriction); } } else { // simpleType and base are mutually exclusive and you // must have one within a tag Element xsdSubType = xsdSimpleType.element(XSD_SIMPLETYPE); if (xsdSubType == null) { String msg = "The simpleType element: " + xsdSimpleType + " must contain a base attribute or simpleType" + " element"; onSchemaError(msg); } else { return loadXSDatatypeFromSimpleType(xsdSubType); } } } else { onSchemaError("No . Could not create XSDatatype for" + " simpleType: " + xsdSimpleType); } return null; } /** * Derives a new type from a base type and a set of restrictions * * @param baseType * DOCUMENT ME! * @param xsdRestriction * DOCUMENT ME! * * @return DOCUMENT ME! */ private XSDatatype deriveSimpleType(XSDatatype baseType, Element xsdRestriction) { TypeIncubator incubator = new TypeIncubator(baseType); ValidationContext context = null; try { for (Iterator iter = xsdRestriction.elementIterator(); iter .hasNext();) { Element element = (Element) iter.next(); String name = element.getName(); String value = element.attributeValue("value"); boolean fixed = AttributeHelper.booleanValue(element, "fixed"); // add facet incubator.addFacet(name, value, fixed, context); } // derive a new type by those facets String newTypeName = null; return incubator.derive("", newTypeName); } catch (DatatypeException e) { onSchemaError("Invalid restriction: " + e.getMessage() + " when trying to build restriction: " + xsdRestriction); return null; } } /** * DOCUMENT ME! * * @param name * The name of the element * * @return the DatatypeElementFactory for the given element * QName, creating one if it does not already exist */ private DatatypeElementFactory getDatatypeElementFactory(QName name) { DatatypeElementFactory factory = documentFactory .getElementFactory(name); if (factory == null) { factory = new DatatypeElementFactory(name); name.setDocumentFactory(factory); } return factory; } private XSDatatype getTypeByName(String type) { XSDatatype dataType = (XSDatatype) dataTypeCache.get(type); if (dataType == null) { // first check to see if it is a built-in type // maybe a prefix is being used int idx = type.indexOf(':'); if (idx >= 0) { String localName = type.substring(idx + 1); try { dataType = DatatypeFactory.getTypeByName(localName); } catch (DatatypeException e) { } } if (dataType == null) { try { dataType = DatatypeFactory.getTypeByName(type); } catch (DatatypeException e) { } } if (dataType == null) { // it's no built-in type, maybe it's a type we defined ourself QName typeQName = getQName(type); dataType = (XSDatatype) namedTypeResolver.simpleTypeMap .get(typeQName); } if (dataType != null) { // store in cache for later dataTypeCache.put(type, dataType); } } return dataType; } private QName getQName(String name) { if (targetNamespace == null) { return documentFactory.createQName(name); } else { return documentFactory.createQName(name, targetNamespace); } } /** * Called when there is a problem with the schema and the builder cannot * handle the XML Schema Data Types correctly * * @param message * DOCUMENT ME! * * @throws InvalidSchemaException * DOCUMENT ME! */ private void onSchemaError(String message) { // Some users may wish to disable exception throwing // and instead use some kind of listener for errors and continue // System.out.println( "WARNING: " + message ); throw new InvalidSchemaException(message); } } /* * Redistribution and use of this software and associated documentation * ("Software"), with or without modification, are permitted provided that the * following conditions are met: * * 1. Redistributions of source code must retain copyright statements and * notices. Redistributions must also contain a copy of this document. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The name "DOM4J" must not be used to endorse or promote products derived * from this Software without prior written permission of MetaStuff, Ltd. For * written permission, please contact [email protected]. * * 4. Products derived from this Software may not be called "DOM4J" nor may * "DOM4J" appear in their names without prior written permission of MetaStuff, * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd. * * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org * * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved. */




© 2015 - 2024 Weber Informatics LLC | Privacy Policy