| /* |
| * 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 org.apache.ws.commons.schema.constants.Constants; |
| import org.apache.ws.commons.schema.utils.NamespaceContextOwner; |
| import org.apache.ws.commons.schema.utils.NamespacePrefixList; |
| import org.w3c.dom.Document; |
| |
| import javax.xml.namespace.QName; |
| import javax.xml.transform.*; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.transform.stream.StreamResult; |
| import javax.xml.transform.stream.StreamSource; |
| import java.io.*; |
| import java.util.Map; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Stack; |
| |
| |
| /** |
| * Contains the definition of a schema. All XML Schema definition language (XSD) |
| * elements are children of the schema element. Represents the World Wide Web |
| * Consortium (W3C) schema element |
| */ |
| |
| // Oct 15th - momo - initial impl |
| // Oct 17th - vidyanand - add SimpleType + element |
| // Oct 18th - momo - add ComplexType |
| // Oct 19th - vidyanand - handle external |
| // Dec 6th - Vidyanand - changed RuntimeExceptions thrown to XmlSchemaExceptions |
| // Jan 15th - Vidyanand - made changes to SchemaBuilder.handleElement to look for an element ref. |
| // Feb 20th - Joni - Change the getXmlSchemaFromLocation schema |
| // variable to name s. |
| // Feb 21th - Joni - Port to XMLDomUtil and Tranformation. |
| |
| public class XmlSchema extends XmlSchemaAnnotated implements NamespaceContextOwner { |
| private static final String UTF_8_ENCODING = "UTF-8"; |
| static final String SCHEMA_NS = "http://www.w3.org/2001/XMLSchema"; |
| XmlSchemaForm attributeFormDefault, elementFormDefault; |
| |
| XmlSchemaObjectTable attributeGroups, |
| attributes, elements, groups, |
| notations, schemaTypes; |
| XmlSchemaDerivationMethod blockDefault, finalDefault; |
| XmlSchemaObjectCollection includes, items; |
| boolean isCompiled; |
| String syntacticalTargetNamespace, logicalTargetNamespace, version; |
| String schema_ns_prefix = ""; |
| XmlSchemaCollection parent; |
| |
| private NamespacePrefixList namespaceContext; |
| //keep the encoding of the input |
| private String inputEncoding; |
| |
| public void setInputEncoding(String encoding){ |
| this.inputEncoding = encoding; |
| } |
| /** |
| * Creates new XmlSchema |
| * Create a new XmlSchema. The schema is <i>not</i> added to the parent collection, |
| * since it has no target namespace when created through this constructor. |
| * Call {@link XmlSchema#XmlSchema(String, XmlSchemaCollection)} instead. |
| * |
| * @param parent the parent XmlSchemaCollection |
| * @deprecated |
| */ |
| public XmlSchema(XmlSchemaCollection parent) { |
| this(null, null, parent); |
| } |
| |
| /** |
| * Create a schema that is not a member of a collection. |
| */ |
| public XmlSchema() { |
| this(null, null, null); |
| } |
| |
| /** |
| * Create a new schema and record it as a member of a schema collection. |
| * @param namespace the target namespace. |
| * @param systemId the system ID for the schema. |
| * @param parent the parent collection. |
| */ |
| public XmlSchema(String namespace, String systemId, XmlSchemaCollection parent) { |
| this.parent = parent; |
| if (namespace == null) { |
| namespace = ""; |
| } |
| this.parent = parent; |
| attributeFormDefault = new XmlSchemaForm(XmlSchemaForm.UNQUALIFIED); |
| elementFormDefault = new XmlSchemaForm(XmlSchemaForm.UNQUALIFIED); |
| blockDefault = new XmlSchemaDerivationMethod(Constants.BlockConstants.NONE); |
| finalDefault = new XmlSchemaDerivationMethod(Constants.BlockConstants.NONE); |
| items = new XmlSchemaObjectCollection(); |
| includes = new XmlSchemaObjectCollection(); |
| elements = new XmlSchemaObjectTable(); |
| attributeGroups = new XmlSchemaObjectTable(); |
| attributes = new XmlSchemaObjectTable(); |
| groups = new XmlSchemaObjectTable(); |
| notations = new XmlSchemaObjectTable(); |
| schemaTypes = new XmlSchemaObjectTable(); |
| |
| syntacticalTargetNamespace = logicalTargetNamespace = namespace; |
| 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); |
| } |
| } |
| } |
| |
| public XmlSchema(String namespace, XmlSchemaCollection parent) { |
| this(namespace, namespace, parent); |
| |
| } |
| |
| public XmlSchemaForm getAttributeFormDefault() { |
| return attributeFormDefault; |
| } |
| |
| public void setAttributeFormDefault(XmlSchemaForm value) { |
| attributeFormDefault = value; |
| } |
| |
| public XmlSchemaObjectTable getAttributeGroups() { |
| return attributeGroups; |
| } |
| |
| public XmlSchemaObjectTable getAttributes() { |
| return attributes; |
| } |
| |
| public XmlSchemaDerivationMethod getBlockDefault() { |
| return blockDefault; |
| } |
| |
| public void setBlockDefault(XmlSchemaDerivationMethod blockDefault) { |
| this.blockDefault = blockDefault; |
| } |
| |
| public XmlSchemaForm getElementFormDefault() { |
| return elementFormDefault; |
| } |
| |
| public void setElementFormDefault(XmlSchemaForm elementFormDefault) { |
| this.elementFormDefault = elementFormDefault; |
| } |
| |
| public XmlSchemaObjectTable getElements() { |
| return elements; |
| } |
| |
| |
| protected XmlSchemaElement getElementByName(QName name, boolean deep, |
| Stack schemaStack) { |
| if (schemaStack != null && schemaStack.contains(this)) { |
| // recursive schema - just return null |
| return null; |
| } else { |
| XmlSchemaElement element = (XmlSchemaElement) elements |
| .getItem(name); |
| if (deep) { |
| if (element == null) { |
| // search the imports |
| for (Iterator includedItems = includes.getIterator(); includedItems |
| .hasNext();) { |
| |
| XmlSchema schema = getSchema(includedItems.next()); |
| |
| 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; |
| } |
| } |
| |
| /** |
| * get an element by the name in the local schema |
| * |
| * @param name |
| * @return |
| */ |
| public XmlSchemaElement getElementByName(String name) { |
| QName nameToSearchFor = new QName(this.getTargetNamespace(),name); |
| return this.getElementByName(nameToSearchFor, false, null); |
| } |
| |
| /** |
| * Look for a element by its qname. Searches through all the schemas |
| * @param name |
| * @return |
| */ |
| public XmlSchemaElement getElementByName(QName name) { |
| return this.getElementByName(name, true, null); |
| } |
| |
| /** |
| * protected method that allows safe (non-recursive schema loading) |
| * |
| * @param name |
| * @param deep |
| * @param schemaStack |
| * @return |
| */ |
| protected XmlSchemaType getTypeByName(QName name, boolean deep, |
| Stack schemaStack) { |
| if (schemaStack != null && schemaStack.contains(this)) { |
| // recursive schema - just return null |
| return null; |
| } else { |
| XmlSchemaType type = (XmlSchemaType) schemaTypes.getItem(name); |
| |
| if (deep) { |
| if (type == null) { |
| // search the imports |
| for (Iterator includedItems = includes.getIterator(); includedItems |
| .hasNext();) { |
| |
| XmlSchema schema = getSchema(includedItems.next()); |
| |
| 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; |
| } |
| } |
| |
| /** |
| * Search this schema and all the imported/included ones |
| * for the given Qname |
| * @param name |
| * @return |
| */ |
| public XmlSchemaType getTypeByName(QName name) { |
| return getTypeByName(name, true, null); |
| } |
| |
| /** |
| * |
| * @param name |
| * @return |
| */ |
| public XmlSchemaType getTypeByName(String name) { |
| QName nameToSearchFor = new QName(this.getTargetNamespace(),name); |
| return getTypeByName(nameToSearchFor, false, null); |
| } |
| |
| /** |
| * Get a schema from an import |
| * |
| * @param includeOrImport |
| * @return |
| */ |
| 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; |
| } |
| |
| |
| |
| public XmlSchemaDerivationMethod getFinalDefault() { |
| return finalDefault; |
| } |
| |
| public void setFinalDefault(XmlSchemaDerivationMethod finalDefault) { |
| this.finalDefault = finalDefault; |
| } |
| |
| public XmlSchemaObjectTable getGroups() { |
| return groups; |
| } |
| |
| public XmlSchemaObjectCollection getIncludes() { |
| return includes; |
| } |
| |
| public boolean isCompiled() { |
| return isCompiled; |
| } |
| |
| public XmlSchemaObjectCollection getItems() { |
| return items; |
| } |
| |
| public XmlSchemaObjectTable getNotations() { |
| return notations; |
| } |
| |
| public XmlSchemaObjectTable getSchemaTypes() { |
| return schemaTypes; |
| } |
| |
| public String getTargetNamespace() { |
| return syntacticalTargetNamespace; |
| } |
| |
| public void setTargetNamespace(String targetNamespace) { |
| if (!targetNamespace.equals("")) { |
| syntacticalTargetNamespace = logicalTargetNamespace = targetNamespace; |
| } |
| } |
| |
| public String getVersion() { |
| return version; |
| } |
| |
| public void compile(ValidationEventHandler eh) { |
| |
| } |
| |
| /** |
| * Serialize the schema into the given output stream |
| * @param out - the output stream to write to |
| */ |
| public void write(OutputStream out) { |
| try { |
| 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)); |
| } |
| } catch (UnsupportedEncodingException e) { |
| //log the error and just write it without the encoding |
| write(new OutputStreamWriter(out)); |
| } |
| |
| } |
| |
| /** |
| * Serialize the schema into the given output stream |
| * @param out - the output stream to write to |
| * @param options - a map of options |
| */ |
| public void write(OutputStream out, Map options) { |
| try { |
| if (this.inputEncoding!= null && |
| !"".equals(this.inputEncoding)){ |
| write(new OutputStreamWriter(out,this.inputEncoding),options); |
| }else{ |
| write(new OutputStreamWriter(out,UTF_8_ENCODING),options); |
| } |
| } catch (UnsupportedEncodingException e) { |
| //log the error and just write it without the encoding |
| write(new OutputStreamWriter(out)); |
| } |
| |
| } |
| |
| /** |
| * Serialie the schema to a given writer |
| * @param writer - the writer to write this |
| */ |
| public void write(Writer writer,Map options) { |
| serialize_internal(this, writer,options); |
| } |
| /** |
| * Serialie the schema to a given writer |
| * @param writer - the writer to write this |
| */ |
| public void write(Writer writer) { |
| serialize_internal(this, writer,null); |
| } |
| |
| 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(e.getMessage()); |
| } |
| } |
| |
| /** |
| * serialize the schema - this is the method tht does to work |
| * @param schema |
| * @param out |
| * @param options |
| */ |
| private void serialize_internal(XmlSchema schema, Writer out, Map options) { |
| |
| try { |
| XmlSchemaSerializer xser = new XmlSchemaSerializer(); |
| xser.setExtReg(this.parent.getExtReg()); |
| Document[] serializedSchemas = xser.serializeSchema(schema, 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 (schema.inputEncoding!= null && |
| !"".equals(schema.inputEncoding)){ |
| tr.setOutputProperty(OutputKeys.ENCODING,schema.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, (String)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()); |
| } |
| } |
| |
| /** |
| * 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"); |
| } |
| |
| public void addType(XmlSchemaType type) { |
| QName qname = type.getQName(); |
| if (schemaTypes.contains(qname)) { |
| throw new RuntimeException("Schema for namespace '" + |
| syntacticalTargetNamespace + "' already contains type '" + |
| qname.getLocalPart()); |
| } |
| schemaTypes.add(qname, type); |
| } |
| |
| public NamespacePrefixList getNamespaceContext() { |
| return namespaceContext; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Override the equals(Object) method with equivalence checking |
| * that is specific to this class. |
| */ |
| public boolean equals(Object what) { |
| |
| //Note: this method may no longer be required when line number/position are used correctly in XmlSchemaObject. |
| //Currently they are simply initialized to zero, but they are used in XmlSchemaObject.equals |
| //which can result in a false positive (e.g. if a WSDL contains 2 inlined schemas). |
| |
| if (what == this) { |
| return true; |
| } |
| |
| //If the inherited behaviour determines that the objects are NOT equal, return false. |
| //Otherwise, do some further equivalence checking. |
| |
| if(!super.equals(what)) { |
| return false; |
| } |
| |
| if (!(what instanceof XmlSchema)) { |
| return false; |
| } |
| |
| XmlSchema xs = (XmlSchema) what; |
| |
| if (this.id != null) { |
| if (!this.id.equals(xs.id)) { |
| return false; |
| } |
| } else { |
| if (xs.id != null) { |
| return false; |
| } |
| } |
| |
| if (this.syntacticalTargetNamespace != null) { |
| if (!this.syntacticalTargetNamespace.equals(xs.syntacticalTargetNamespace)) { |
| return false; |
| } |
| } else { |
| if (xs.syntacticalTargetNamespace != null) { |
| return false; |
| } |
| } |
| |
| //TODO decide if further schema content should be checked for equivalence. |
| |
| return true; |
| } |
| public String getInputEncoding() { |
| return inputEncoding; |
| } |
| } |