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

Explore the source code of the class XmlSchema.java

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.ws.commons.schema;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;

import org.apache.ws.commons.schema.XmlSchemaSerializer.XmlSchemaSerializerException;
import org.apache.ws.commons.schema.utils.CollectionFactory;
import org.apache.ws.commons.schema.utils.NamespaceContextOwner;
import org.apache.ws.commons.schema.utils.NamespacePrefixList;

/**
 * Contains the definition of a schema. All XML Schema definition language (XSD) elements are children of the
 * schema element.
 */
public class XmlSchema extends XmlSchemaAnnotated implements NamespaceContextOwner {
    static final String SCHEMA_NS = XMLConstants.W3C_XML_SCHEMA_NS_URI;

    private static final String UTF_8_ENCODING = "UTF-8";

    // This has be ordered so that things come out in the order we parse them.
    private List items;

    private XmlSchemaCollection parent;
    private XmlSchemaDerivationMethod blockDefault;
    private XmlSchemaDerivationMethod finalDefault;
    private XmlSchemaForm elementFormDefault;
    private XmlSchemaForm attributeFormDefault;
    private List externals;
    private Map attributeGroups;
    private Map attributes;
    private Map elements;
    private Map groups;
    private Map notations;
    private Map schemaTypes;
    private String syntacticalTargetNamespace;
    private String schemaNamespacePrefix;
    private String logicalTargetNamespace;
    private String version;

    private NamespacePrefixList namespaceContext;
    // keep the encoding of the input
    private String inputEncoding;

    /**
     * Create a schema that is not a member of a collection and has no target namespace or system ID.
     */
    public XmlSchema() {
        this(null, null, null);
    }

    /**
     * Create a new schema with a target namespace and system ID, and record it as a member of a schema
     * collection.
     *
     * @param namespace the target namespace.
     * @param systemId the system ID for the schema. This is used to resolve references to other schemas.
     * @param parent the parent collection.
     */
    public XmlSchema(String namespace, String systemId, XmlSchemaCollection parent) {
        this.parent = parent;
        attributeFormDefault = XmlSchemaForm.UNQUALIFIED;
        elementFormDefault = XmlSchemaForm.UNQUALIFIED;
        blockDefault = XmlSchemaDerivationMethod.NONE;
        finalDefault = XmlSchemaDerivationMethod.NONE;
        items = new ArrayList();
        externals = new ArrayList();
        elements = new HashMap();
        attributeGroups = new HashMap();
        attributes = new HashMap();
        groups = new HashMap();
        notations = new HashMap();
        schemaTypes = new HashMap();

        logicalTargetNamespace = namespace;
        syntacticalTargetNamespace = namespace;
        if (logicalTargetNamespace == null) {
            logicalTargetNamespace = "";
        }
        if (parent != null) {
            XmlSchemaCollection.SchemaKey schemaKey =
                new XmlSchemaCollection.SchemaKey(
                                                  this.logicalTargetNamespace,
                                                  systemId);
            if (parent.containsSchema(schemaKey)) {
                throw new XmlSchemaException("Schema name conflict in collection");
            } else {
                parent.addSchema(schemaKey, this);
            }
        }
    }

    /**
     * Create a new schema in a collection with a target namespace.
     *
     * @param namespace the target namespace.
     * @param parent the containing collection.
     */
    public XmlSchema(String namespace, XmlSchemaCollection parent) {
        this(namespace, namespace, parent);

    }

    /**
     * Return an array of DOM documents consisting of this schema and any schemas that it references.
     * Referenced schemas are only returned if the [email protected] XmlSchemaExternal} objects corresponding to them
     * have their 'schema' fields filled in.
     *
     * @return DOM documents.
     */
    public Document[] getAllSchemas() {
        try {

            XmlSchemaSerializer xser = new XmlSchemaSerializer();
            xser.setExtReg(this.parent.getExtReg());
            return xser.serializeSchema(this, true);

        } catch (XmlSchemaSerializer.XmlSchemaSerializerException e) {
            throw new XmlSchemaException("Error serializing schema", e);
        }
    }

    /**
     * Retrieve a global attribute by its QName.
     *
     * @param name
     * @return the attribute.
     */
    public XmlSchemaAttribute getAttributeByName(QName name) {
        return this.getAttributeByName(name, true, null);
    }

    /**
     * Look for an attribute by its local name.
     *
     * @param name
     * @return the attribute
     */
    public XmlSchemaAttribute getAttributeByName(String name) {
        QName nameToSearchFor = new QName(this.getTargetNamespace(), name);
        return this.getAttributeByName(nameToSearchFor, false, null);
    }

    /**
     * @return the default attribute form for this schema.
     */
    public XmlSchemaForm getAttributeFormDefault() {
        return attributeFormDefault;
    }

    /**
     * Retrieve an attribute group by QName.
     *
     * @param name
     * @return
     */
    public XmlSchemaAttributeGroup getAttributeGroupByName(QName name) {
        return getAttributeGroupByName(name, true, null);
    }

    /**
     * Return a map containing all the defined attribute groups of this schema. The keys are QNames, where the
     * namespace will always be the target namespace of this schema. This makes it easier to look up items for
     * cross-schema references.
     * 
* If org.apache.ws.commons.schema.protectReadOnlyCollections * is 'true', this will return a map that checks at runtime. * * @return the map of attribute groups. */ public Map getAttributeGroups() { return CollectionFactory.getProtectedMap(attributeGroups); } /** * Return a map containing all the defined attributes of this schema. The keys are QNames, where the * namespace will always be the target namespace of this schema. This makes it easier to look up items for * cross-schema references. *
* If org.apache.ws.commons.schema.protectReadOnlyCollections * is 'true', this will return a map that checks at runtime. * * @return the map of attributes. */ public Map getAttributes() { return CollectionFactory.getProtectedMap(attributes); } /** * Return the default block value for this schema. * * @return the default block value. */ public XmlSchemaDerivationMethod getBlockDefault() { return blockDefault; } /** * Look for a element by its QName. * * @param name * @return the element. */ public XmlSchemaElement getElementByName(QName name) { return this.getElementByName(name, true, null); } /** * get an element by its local name. * * @param name * @return the element. */ public XmlSchemaElement getElementByName(String name) { QName nameToSearchFor = new QName(this.getTargetNamespace(), name); return this.getElementByName(nameToSearchFor, false, null); } /** * @return the default element form for this schema. */ public XmlSchemaForm getElementFormDefault() { return elementFormDefault; } /** * Return a map containing all the defined elements of this schema. The keys are QNames, where the * namespace will always be the target namespace of this schema. This makes it easier to look up items for * cross-schema references. *
* If org.apache.ws.commons.schema.protectReadOnlyCollections * is 'true', this will return a map that checks at runtime * * @return the map of elements. */ public Map getElements() { return CollectionFactory.getProtectedMap(elements); } /** * Return all of the includes, imports, and redefines for this schema. *
* If org.apache.ws.commons.schema.protectReadOnlyCollections * is 'true', this will return a list that checks at runtime * * @return a list of the objects representing includes, imports, and redefines. */ public List getExternals() { return CollectionFactory.getProtectedList(externals); } /** * @return the default 'final' value for this schema. */ public XmlSchemaDerivationMethod getFinalDefault() { return finalDefault; } /** * Retrieve a group by QName. * * @param name * @return */ public XmlSchemaGroup getGroupByName(QName name) { return getGroupByName(name, true, null); } /** * Return a map containing all the defined groups of this schema. The keys are QNames, where the namespace * will always be the target namespace of this schema. This makes it easier to look up items for * cross-schema references.
* If org.apache.ws.commons.schema.protectReadOnlyCollections * is 'true', this will return a map that checks at runtime * * @return the map of groups. */ public Map getGroups() { return CollectionFactory.getProtectedMap(groups); } /** * Return the character encoding for this schema. This will only be present if either the schema was read * from an XML document or there was a call to [email protected] #setInputEncoding(String)}. * * @return */ public String getInputEncoding() { return inputEncoding; } /** * Return all of the global items in this schema.
* If org.apache.ws.commons.schema.protectReadOnlyCollections * is 'true', this will return a map that checks at runtime. * @return all of the global items from this schema. * */ public List getItems() { return CollectionFactory.getProtectedList(items); } /** * Return the logical target namespace. If a schema document has no target namespace, but it is referenced * via an xs:include or xs:redefine, its logical target namespace is the target namespace of the including * schema. * * @return the logical target namespace. */ public String getLogicalTargetNamespace() { return logicalTargetNamespace; } /** * [email protected]} */ public NamespacePrefixList getNamespaceContext() { return namespaceContext; } /** * Retrieve a notation by QName. * * @param name * @return the notation */ public XmlSchemaNotation getNotationByName(QName name) { return getNotationByName(name, true, null); } /** * Return a map containing all the defined notations of this schema. The keys are QNames, where the * namespace will always be the target namespace of this schema. This makes it easier to look up items for * cross-schema references. *
* If org.apache.ws.commons.schema.protectReadOnlyCollections * is 'true', this will return a map that checks at runtime. * * @return the map of notations. */ public Map getNotations() { return CollectionFactory.getProtectedMap(notations); } /** * Return the parent XmlSchemaCollection. If this schema was not initialized in a collection the return * value will be null. * * @return the parent collection. */ public XmlSchemaCollection getParent() { return parent; } /** * Retrieve a DOM tree for this one schema, independent of any included or related schemas. * * @return The DOM document. * @throws XmlSchemaSerializerException */ public Document getSchemaDocument() throws XmlSchemaSerializerException { XmlSchemaSerializer xser = new XmlSchemaSerializer(); xser.setExtReg(this.parent.getExtReg()); return xser.serializeSchema(this, false)[0]; } /** * @return the namespace prefix for the target namespace. */ public String getSchemaNamespacePrefix() { return schemaNamespacePrefix; } /** * Return a map containing all the defined types of this schema. The keys are QNames, where the namespace * will always be the target namespace of this schema. This makes it easier to look up items for * cross-schema references. * * @return the map of types. */ public Map getSchemaTypes() { return schemaTypes; } /** * Return the declared target namespace of this schema. * * @see #getLogicalTargetNamespace() * @return the namespace URI. */ public String getTargetNamespace() { return syntacticalTargetNamespace; } /** * Search this schema, and its peers in its parent collection, for a schema type specified by QName. * * @param name the type name. * @return the type. */ public XmlSchemaType getTypeByName(QName name) { return getTypeByName(name, true, null); } /** * Retrieve a named type from this schema. * * @param name * @return the type. */ public XmlSchemaType getTypeByName(String name) { QName nameToSearchFor = new QName(this.getTargetNamespace(), name); return getTypeByName(nameToSearchFor, false, null); } /** * Return the declared XML Schema version of this schema. XmlSchema supports only version 1.0. * * @return */ public String getVersion() { return version; } /** * Set the default attribute form for this schema. * * @param value the form. This may not be null. */ public void setAttributeFormDefault(XmlSchemaForm value) { attributeFormDefault = value; } /** * Set the default block value for this schema. * * @param blockDefault the new block value. */ public void setBlockDefault(XmlSchemaDerivationMethod blockDefault) { this.blockDefault = blockDefault; } /** * Set the default element form for this schema. * * @param elementFormDefault the element form. This may not be null. */ public void setElementFormDefault(XmlSchemaForm elementFormDefault) { this.elementFormDefault = elementFormDefault; } /** * Set the default 'final' value for this schema. The value may not be null. * * @param finalDefault the new final value. */ public void setFinalDefault(XmlSchemaDerivationMethod finalDefault) { this.finalDefault = finalDefault; } /** * Set the character encoding name for the schema. This is typically set when reading a schema from an XML * file, so that it can be written back out in the same encoding. * * @param encoding Character encoding name. */ public void setInputEncoding(String encoding) { this.inputEncoding = encoding; } /** * Sets the schema elements namespace context. This may be used for schema serialization, until a better * mechanism was found. */ public void setNamespaceContext(NamespacePrefixList namespaceContext) { this.namespaceContext = namespaceContext; } /** * Set the namespace prefix corresponding to the target namespace. * * @param schemaNamespacePrefix */ public void setSchemaNamespacePrefix(String schemaNamespacePrefix) { this.schemaNamespacePrefix = schemaNamespacePrefix; } /** * Set the target namespace for this schema. * * @param targetNamespace the new target namespace URI. A value of "" is ignored. */ public void setTargetNamespace(String targetNamespace) { if (!"".equals(targetNamespace)) { logicalTargetNamespace = targetNamespace; syntacticalTargetNamespace = targetNamespace; } } @Override public String toString() { return super.toString() + "[" + logicalTargetNamespace + "]"; } /** * Serialize the schema as XML to the specified stream using the encoding established with * [email protected] #setInputEncoding(String)}. * * @param out - the output stream to write to * @throws UnsupportedEncodingException for an invalid encoding. */ public void write(OutputStream out) throws UnsupportedEncodingException { if (this.inputEncoding != null && !"".equals(this.inputEncoding)) { write(new OutputStreamWriter(out, this.inputEncoding)); } else { // As per the XML spec the default is taken to be UTF 8 write(new OutputStreamWriter(out, UTF_8_ENCODING)); } } /** * Serialize the schema as XML to the specified stream using the encoding established with * [email protected] #setInputEncoding(String)}. * * @param out - the output stream to write to * @param options - a map of options * @throws UnsupportedEncodingException */ public void write(OutputStream out, Map options) throws UnsupportedEncodingException { if (this.inputEncoding != null && !"".equals(this.inputEncoding)) { write(new OutputStreamWriter(out, this.inputEncoding), options); } else { write(new OutputStreamWriter(out, UTF_8_ENCODING), options); } } /** * Serialize the schema to a [email protected] java.io.Writer}. * * @param writer - the writer to write this */ public void write(Writer writer) { serializeInternal(writer, null); } /** * Serialize the schema to a [email protected] java.io.Writer}. * * @param writer - the writer to write this */ public void write(Writer writer, Map options) { serializeInternal(writer, options); } protected XmlSchemaAttribute getAttributeByName(QName name, boolean deep, Stack schemaStack) { if (schemaStack != null && schemaStack.contains(this)) { // recursive schema - just return null return null; } XmlSchemaAttribute attribute = (XmlSchemaAttribute)attributes.get(name); if (deep) { if (attribute == null) { // search the imports for (XmlSchemaExternal item : externals) { XmlSchema schema = getSchema(item); if (schema != null) { // create an empty stack - push the current parent in // and // use the protected method to process the schema if (schemaStack == null) { schemaStack = new Stack(); } schemaStack.push(this); attribute = schema.getAttributeByName(name, deep, schemaStack); if (attribute != null) { return attribute; } } } } else { return attribute; } } return attribute; } protected XmlSchemaAttributeGroup getAttributeGroupByName(QName name, boolean deep, Stack schemaStack) { if (schemaStack != null && schemaStack.contains(this)) { // recursive schema - just return null return null; } XmlSchemaAttributeGroup group = attributeGroups.get(name); if (deep) { if (group == null) { // search the imports for (XmlSchemaExternal item : externals) { XmlSchema schema = getSchema(item); if (schema != null) { // create an empty stack - push the current parent in // and // use the protected method to process the schema if (schemaStack == null) { schemaStack = new Stack(); } schemaStack.push(this); group = schema.getAttributeGroupByName(name, deep, schemaStack); if (group != null) { return group; } } } } else { return group; } } return group; } protected XmlSchemaElement getElementByName(QName name, boolean deep, Stack schemaStack) { if (schemaStack != null && schemaStack.contains(this)) { // recursive schema - just return null return null; } XmlSchemaElement element = elements.get(name); if (deep) { if (element == null) { // search the imports for (XmlSchemaExternal item : externals) { XmlSchema schema = getSchema(item); if (schema != null) { // create an empty stack - push the current parent in // and // use the protected method to process the schema if (schemaStack == null) { schemaStack = new Stack(); } schemaStack.push(this); element = schema.getElementByName(name, deep, schemaStack); if (element != null) { return element; } } } } else { return element; } } return element; } protected XmlSchemaGroup getGroupByName(QName name, boolean deep, Stack schemaStack) { if (schemaStack != null && schemaStack.contains(this)) { // recursive schema - just return null return null; } XmlSchemaGroup group = groups.get(name); if (deep) { if (group == null) { // search the imports for (XmlSchemaExternal item : externals) { XmlSchema schema = getSchema(item); if (schema != null) { // create an empty stack - push the current parent in // and // use the protected method to process the schema if (schemaStack == null) { schemaStack = new Stack(); } schemaStack.push(this); group = schema.getGroupByName(name, deep, schemaStack); if (group != null) { return group; } } } } else { return group; } } return group; } protected XmlSchemaNotation getNotationByName(QName name, boolean deep, Stack schemaStack) { if (schemaStack != null && schemaStack.contains(this)) { // recursive schema - just return null return null; } XmlSchemaNotation notation = notations.get(name); if (deep) { if (notation == null) { // search the imports for (XmlSchemaExternal item : externals) { XmlSchema schema = getSchema(item); if (schema != null) { // create an empty stack - push the current parent in // and // use the protected method to process the schema if (schemaStack == null) { schemaStack = new Stack(); } schemaStack.push(this); notation = schema.getNotationByName(name, deep, schemaStack); if (notation != null) { return notation; } } } } else { return notation; } } return notation; } /** * Protected method that allows safe (non-recursive schema loading). It looks for a type with constraints. * * @param name * @param deep * @param schemaStack * @return the type. */ protected XmlSchemaType getTypeByName(QName name, boolean deep, Stack schemaStack) { if (schemaStack != null && schemaStack.contains(this)) { // recursive schema - just return null return null; } XmlSchemaType type = schemaTypes.get(name); if (deep) { if (type == null) { // search the imports for (XmlSchemaExternal item : externals) { XmlSchema schema = getSchema(item); if (schema != null) { // create an empty stack - push the current parent // use the protected method to process the schema if (schemaStack == null) { schemaStack = new Stack(); } schemaStack.push(this); type = schema.getTypeByName(name, deep, schemaStack); if (type != null) { return type; } } } } else { return type; } } return type; } String getSyntacticalTargetNamespace() { return syntacticalTargetNamespace; } void setLogicalTargetNamespace(String logicalTargetNamespace) { this.logicalTargetNamespace = logicalTargetNamespace; } void setParent(XmlSchemaCollection parent) { this.parent = parent; } void setSyntacticalTargetNamespace(String syntacticalTargetNamespace) { this.syntacticalTargetNamespace = syntacticalTargetNamespace; } void setVersion(String version) { this.version = version; } /** * Get a schema from an import * * @param includeOrImport * @return return the schema object. */ private XmlSchema getSchema(Object includeOrImport) { XmlSchema schema; if (includeOrImport instanceof XmlSchemaImport) { schema = ((XmlSchemaImport)includeOrImport).getSchema(); } else if (includeOrImport instanceof XmlSchemaInclude) { schema = ((XmlSchemaInclude)includeOrImport).getSchema(); } else { // skip ? schema = null; } return schema; } /** * Load the default options * * @param options - the map of */ private void loadDefaultOptions(Map options) { options.put(OutputKeys.OMIT_XML_DECLARATION, "yes"); options.put(OutputKeys.INDENT, "yes"); } /** * serialize the schema - this is the method tht does to work * * @param out * @param options */ private void serializeInternal(Writer out, Map options) { try { XmlSchemaSerializer xser = new XmlSchemaSerializer(); xser.setExtReg(this.parent.getExtReg()); Document[] serializedSchemas = xser.serializeSchema(this, false); TransformerFactory trFac = TransformerFactory.newInstance(); try { trFac.setAttribute("indent-number", "4"); } catch (IllegalArgumentException e) { // do nothing - we'll just silently let this pass if it // was not compatible } Source source = new DOMSource(serializedSchemas[0]); Result result = new StreamResult(out); javax.xml.transform.Transformer tr = trFac.newTransformer(); // use the input encoding if there is one if (this.inputEncoding != null && !"".equals(this.inputEncoding)) { tr.setOutputProperty(OutputKeys.ENCODING, this.inputEncoding); } // let these be configured from outside if any is present // Note that one can enforce the encoding by passing the necessary // property in options if (options == null) { options = new HashMap(); loadDefaultOptions(options); } Iterator keys = options.keySet().iterator(); while (keys.hasNext()) { Object key = keys.next(); tr.setOutputProperty((String)key, options.get(key)); } tr.transform(source, result); out.flush(); } catch (TransformerConfigurationException e) { throw new XmlSchemaException(e.getMessage()); } catch (TransformerException e) { throw new XmlSchemaException(e.getMessage()); } catch (XmlSchemaSerializer.XmlSchemaSerializerException e) { throw new XmlSchemaException(e.getMessage()); } catch (IOException e) { throw new XmlSchemaException(e.getMessage()); } } }