diff --git a/jaxb-ri/xjc/src/com/sun/tools/xjc/ModelLoader.java b/jaxb-ri/xjc/src/com/sun/tools/xjc/ModelLoader.java index 0c0fb09..f71896f 100644 --- a/jaxb-ri/xjc/src/com/sun/tools/xjc/ModelLoader.java +++ b/jaxb-ri/xjc/src/com/sun/tools/xjc/ModelLoader.java @@ -61,6 +61,7 @@ import com.sun.tools.xjc.reader.xmlschema.parser.CustomizationContextChecker; import com.sun.tools.xjc.reader.xmlschema.parser.IncorrectNamespaceURIChecker; import com.sun.tools.xjc.reader.xmlschema.parser.SchemaConstraintChecker; import com.sun.tools.xjc.reader.xmlschema.parser.XMLSchemaInternalizationLogic; +import com.sun.tools.xjc.reader.xmlschema.parser.XMLSchemaVersionControlFilter; import com.sun.tools.xjc.util.ErrorReceiverFilter; import com.sun.xml.xsom.XSSchemaSet; import com.sun.xml.xsom.parser.JAXPParser; @@ -264,6 +265,7 @@ public final class ModelLoader { handler = wrapBy( new IncorrectNamespaceURIChecker(errorReceiver), handler ); handler = wrapBy( new CustomizationContextChecker(errorReceiver), handler ); // handler = wrapBy( new VersionChecker(controller), handler ); + handler = wrapBy( new XMLSchemaVersionControlFilter(), handler); baseParser.parse( source, handler, errorHandler, entityResolver ); } @@ -478,6 +480,7 @@ public final class ModelLoader { // set up the chain of handlers. handler = wrapBy( new SpeculationChecker(), handler ); handler = wrapBy( new VersionChecker(null,errorReceiver,entityResolver), handler ); + handler = wrapBy( new XMLSchemaVersionControlFilter(), handler); base.parse( source, handler, errorHandler, entityResolver ); } diff --git a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/MessageBundle.properties b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/MessageBundle.properties index 35fc71e..18a4fa8 100644 --- a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/MessageBundle.properties +++ b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/MessageBundle.properties @@ -54,14 +54,17 @@ SchemaConstraintChecker.UnableToCheckCorrectness = \ In the future compilation, add the '-nv' option to skip the correctness check \ for faster compilation. - - +SchemaConstraintChecker.VersionTransformFail = \ + Unable to check the correctness of the schema because there was an internal error \ + when performing conditional inclusion (schema versioning) transform. IncorrectNamespaceURIChecker.WarnIncorrectURI = \ No JAXB customization was detected in the schema but the prefix "jaxb" is used for \ other namespace URIs. If you did intend to use JAXB customization, make sure the namespace URI is "{0}" +XMLSchemaVersionResolver.BadURI = \ + The URI "{0}" could not be parsed. - - +XMLSchemaVersionControlFilter.BadQName = \ + Cannot resolve the QName "{0}". diff --git a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/Messages.java b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/Messages.java index c8ae744..fb3a9a8 100644 --- a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/Messages.java +++ b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/Messages.java @@ -64,4 +64,13 @@ class Messages static final String WARN_UNABLE_TO_CHECK_CORRECTNESS = // 0 args "SchemaConstraintChecker.UnableToCheckCorrectness"; + + static final String WARN_VERSION_TRANSFORM_FAIL = // 0 args + "SchemaConstraintChecker.VersionTransformFail"; + + static final String ERR_BAD_URI = // 1 args + "XMLSchemaVersionResolver.BadURI"; + + static final String ERR_BAD_QNAME = // 1 args + "XMLSchemaVersionControlFilter.BadQName"; } diff --git a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/SchemaConstraintChecker.java b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/SchemaConstraintChecker.java index 128da36..5cd9649 100644 --- a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/SchemaConstraintChecker.java +++ b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/SchemaConstraintChecker.java @@ -56,6 +56,7 @@ import org.w3c.dom.ls.LSResourceResolver; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import org.xml.sax.helpers.XMLReaderFactory; import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; @@ -103,12 +104,16 @@ public class SchemaConstraintChecker { } try { - sf.newSchema(getSchemaSource(schemas)); + sf.setResourceResolver(new XMLSchemaVersionControlResolver(sf.getResourceResolver(), errorFilter)); + sf.newSchema(getSchemaSource(schemas)); } catch (SAXException e) { // TODO: we haven't thrown exceptions from here before. should we just trap them and return false? hadErrors = true; } catch( OutOfMemoryError e) { errorHandler.warning(null,Messages.format(Messages.WARN_UNABLE_TO_CHECK_CORRECTNESS)); + } catch (VersionControlTransformError e) { + e.printStackTrace(); + errorHandler.warning(null,Messages.format(Messages.WARN_VERSION_TRANSFORM_FAIL)); } return !(hadErrors || errorFilter.hadError()); @@ -121,10 +126,11 @@ public class SchemaConstraintChecker { * @param schemas array of {@link InputSource InputSource} * @return array of {@link Source Source} */ - private static Source[] getSchemaSource(InputSource[] schemas) { + private static Source[] getSchemaSource(InputSource[] schemas) throws SAXException { SAXSource[] sources = new SAXSource[schemas.length]; - for (int i = 0; i < schemas.length; i++) - sources[i] = new SAXSource(schemas[i]); + for (int i = 0; i < schemas.length; i++) { + sources[i] = new SAXSource(new XMLSchemaVersionControlFilter(XMLReaderFactory.createXMLReader()), schemas[i]); + } return sources; } diff --git a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/VersionControlTransformError.java b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/VersionControlTransformError.java new file mode 100644 index 0000000..3b95bf5 --- /dev/null +++ b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/VersionControlTransformError.java @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.tools.xjc.reader.xmlschema.parser; + +/** + * Indicates a serious problem has occurred when performing the + * Conditional Inclusion transformation. + * + * @author + * Jorge L. Williams (jorge.williams@rackspace.com) + **/ + +public class VersionControlTransformError extends Error { + public VersionControlTransformError() { + super(); + } + + public VersionControlTransformError(String message) { + super(message); + } + + public VersionControlTransformError(String message, Throwable cause) { + super(message, cause); + } + + public VersionControlTransformError(Throwable cause) { + super(cause); + } +} diff --git a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaInternalizationLogic.java b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaInternalizationLogic.java index d11c2a1..32a4897 100644 --- a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaInternalizationLogic.java +++ b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaInternalizationLogic.java @@ -78,7 +78,7 @@ public class XMLSchemaInternalizationLogic implements InternalizationLogic { } public XMLFilterImpl createExternalReferenceFinder(DOMForest parent) { - return new ReferenceFinder(parent); + return new XMLSchemaVersionControlFilter(new ReferenceFinder(parent)); } public boolean checkIfValidTargetNode(DOMForest parent, Element bindings, Element target) { diff --git a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaVersion.java b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaVersion.java new file mode 100644 index 0000000..a7bffb2 --- /dev/null +++ b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaVersion.java @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.tools.xjc.reader.xmlschema.parser; + +import java.util.Set; +import java.math.BigDecimal; +import javax.xml.namespace.QName; + +/** + * Describes an XML Schema Version. + * + * @author + * Jorge L. Williams (jorge.williams@rackspace.com) + * + */ +public interface XMLSchemaVersion { + /** + * @return The version number of this schema. + */ + public BigDecimal getVersionNumber(); + /** + * @return The types supported by this version. For example + * {@code xsd:int}. + */ + public Set getSupportedTypes(); + /** + * @return The facets supported by this version. For examlpe + * {@code xsd:maxLength}. + */ + public Set getSupportedFacets(); +} diff --git a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaVersion1_0.java b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaVersion1_0.java new file mode 100644 index 0000000..4e0cd49 --- /dev/null +++ b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaVersion1_0.java @@ -0,0 +1,145 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.tools.xjc.reader.xmlschema.parser; + +import java.util.HashSet; +import java.util.Set; +import java.math.BigDecimal; +import javax.xml.namespace.QName; + +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; + +/** + * Describes XML Schema Version 1.0. + * + * @author + * Jorge L. Williams (jorge.williams@rackspace.com) + * + */ +public class XMLSchemaVersion1_0 implements XMLSchemaVersion { + /** + * XSD version number. + */ + private static final BigDecimal VERSION = new BigDecimal(1.0); + + /** + * The supported types in XML Schema 1.0. + */ + private static final Set SUPPORTED_TYPES = new HashSet(); + + static { + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"anyType")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"anySimpleType")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"anyURI")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"base64Binary")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"boolean")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"byte")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"date")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"dateTime")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"decimal")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"double")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"duration")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"ENTITIES")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"ENTITY")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"float")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"gDay")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"gMonth")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"gMonthDay")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"gYear")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"gYearMonth")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"hexBinary")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"ID")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"IDREF")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"IDREFS")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"int")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"integer")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"language")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"long")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"Name")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"NCName")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"negativeInteger")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"NMTOKEN")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"NMTOKENS")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"nonNegativeInteger")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"nonPositiveInteger")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"normalizedString")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"NOTATION")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"positiveInteger")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"QName")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"short")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"string")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"time")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"token")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"unsignedByte")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"unsignedInt")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"unsignedLong")); + SUPPORTED_TYPES.add(new QName(W3C_XML_SCHEMA_NS_URI,"unsignedShort")); + } + + /** + * The supported facets in XML Schema 1.0. + */ + private static final Set SUPPORTED_FACETS = new HashSet(); + + static { + SUPPORTED_FACETS.add(new QName(W3C_XML_SCHEMA_NS_URI,"enumeration")); + SUPPORTED_FACETS.add(new QName(W3C_XML_SCHEMA_NS_URI,"fractionDigits")); + SUPPORTED_FACETS.add(new QName(W3C_XML_SCHEMA_NS_URI,"length")); + SUPPORTED_FACETS.add(new QName(W3C_XML_SCHEMA_NS_URI,"maxExclusive")); + SUPPORTED_FACETS.add(new QName(W3C_XML_SCHEMA_NS_URI,"maxInclusive")); + SUPPORTED_FACETS.add(new QName(W3C_XML_SCHEMA_NS_URI,"maxLength")); + SUPPORTED_FACETS.add(new QName(W3C_XML_SCHEMA_NS_URI,"minExclusive")); + SUPPORTED_FACETS.add(new QName(W3C_XML_SCHEMA_NS_URI,"minInclusive")); + SUPPORTED_FACETS.add(new QName(W3C_XML_SCHEMA_NS_URI,"minLength")); + SUPPORTED_FACETS.add(new QName(W3C_XML_SCHEMA_NS_URI,"pattern")); + SUPPORTED_FACETS.add(new QName(W3C_XML_SCHEMA_NS_URI,"totalDigits")); + SUPPORTED_FACETS.add(new QName(W3C_XML_SCHEMA_NS_URI,"whiteSpace")); + } + + @Override + public BigDecimal getVersionNumber() { return VERSION; } + + @Override + public Set getSupportedTypes() { return SUPPORTED_TYPES; } + + @Override + public Set getSupportedFacets() { return SUPPORTED_FACETS; } +} diff --git a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaVersionControlFilter.java b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaVersionControlFilter.java new file mode 100644 index 0000000..88f0a89 --- /dev/null +++ b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaVersionControlFilter.java @@ -0,0 +1,416 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.tools.xjc.reader.xmlschema.parser; + +import java.util.List; +import java.util.LinkedList; +import java.util.Deque; +import java.util.Set; +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; + +import java.math.BigDecimal; + +import javax.xml.namespace.QName; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.XMLReader; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.XMLFilterImpl; + +/** + * Implements Conditional Inclusion of an XML Schema version as + * defined by http://www.w3.org/TR/xmlschema11-1/#cip. + * + * By default this filter performs conditional inclusion of XML Schema + * version 1.0. + * + * @author + * Jorge L. Williams (jorge.williams@rackspace.com) + * + */ + +public class XMLSchemaVersionControlFilter extends XMLFilterImpl { + /** + * XML Schema Versioning namespace. + */ + private static final String VC_NAMESPACE="http://www.w3.org/2007/XMLSchema-versioning"; + + private static final QName VC_MIN_VERSION = new QName(VC_NAMESPACE, "minVersion"); + private static final QName VC_MAX_VERSION = new QName(VC_NAMESPACE, "maxVersion"); + private static final QName VC_TYPE_AVAILABLE = new QName(VC_NAMESPACE, "typeAvailable"); + private static final QName VC_TYPE_UNAVAILABLE = new QName(VC_NAMESPACE, "typeUnavailable"); + private static final QName VC_FACET_AVAILABLE = new QName(VC_NAMESPACE, "facetAvailable"); + private static final QName VC_FACET_UNAVAILABLE = new QName(VC_NAMESPACE, "facetUnavailable"); + + /** + * The version to conditionally include. + */ + private XMLSchemaVersion version; + + /** + * A stack of elements that have been skipped. If the stack is not + * empty then we are skipping elements. + */ + private Deque skippedElements = new LinkedList(); + + /** + * A map from XML prefixes to namespace URIs. + */ + private Map prefixMap = new HashMap(); + + /** + * The current locator. + */ + private Locator locator; + + /** + * Constructs an XMLSchemaVersionControlFilter that conditionally + * includes XSD version 1.0. + */ + public XMLSchemaVersionControlFilter() { + super(); + // + // By default we conditionally include XSD 1.0. + // + this.version = new XMLSchemaVersion1_0(); + } + + /** + * Constructs an XMLSchemaVersionControlFilter that conditionally + * includes XSD version 1.0. + * + * @param parent The parent XML Reader. + */ + public XMLSchemaVersionControlFilter(XMLReader parent) { + super(parent); + // + // By default we conditionally include XSD 1.0. + // + this.version = new XMLSchemaVersion1_0(); + } + + /** + * Constructs an XMLSchemaVersionControlFilter that conditionally + * includes an XSD version. + * + * @param parent The parent XML Reader. + * @param version The XSD version to conditionally include. + */ + public XMLSchemaVersionControlFilter(XMLReader parent, XMLSchemaVersion version) { + super(parent); + this.version = version; + } + + /** + * Given the QName of an attribute, returns the attributes value. + * + * @param name The fully qualified name of the attribute. + * @param atts The list of attributes to seach. + * @return The attribute value of null if the attribute does not + * exist in the list. + */ + private String getAttribute(QName name, Attributes atts) { + return atts.getValue(name.getNamespaceURI(), name.getLocalPart()); + } + + /** + * @param atts A list of attributes contained by an element. + * @return true if the list of attributes contans a {@code + * vc:minVersion} attribute and it indicates that the element + * should be skipped based on the the current XSD version. + */ + private Boolean shouldSkipMinVersion(Attributes atts) { + Boolean ret=false; + String minVersion=getAttribute(VC_MIN_VERSION, atts); + + if (minVersion != null) { + ret = version.getVersionNumber().compareTo(new BigDecimal(minVersion)) < 0; + } + return ret; + } + + /** + * @param atts A list of attributes contained by an element. + * @return true if the list of attributes contans a {@code + * vc:maxVersion} attribute and it indicates that the element + * should be skipped based on the the current XSD version. + */ + private Boolean shouldSkipMaxVersion(Attributes atts) { + Boolean ret=false; + String maxVersion=getAttribute(VC_MAX_VERSION, atts); + + if (maxVersion != null) { + ret = !(version.getVersionNumber().compareTo(new BigDecimal(maxVersion)) < 0); + } + return ret; + } + + /** + * @param atts A list of attributes contained by an element. + * @return true if the list of attributes contans a {@code + * vc:typeAvailable} attribute and it indicates that the element + * should be skipped based on the the current XSD version. + */ + private Boolean shouldSkipTypeAvailable(Attributes atts) throws SAXException { + Boolean ret=false; + String typeAvailable=getAttribute(VC_TYPE_AVAILABLE, atts); + Set supportedTypes = version.getSupportedTypes(); + + if (typeAvailable != null) { + for (QName type : parseQNameList(typeAvailable)) { + ret = ret | !supportedTypes.contains(type); + if (ret) break; + } + } + return ret; + } + + /** + * @param atts A list of attributes contained by an element. + * @return true if the list of attributes contans a {@code + * vc:typeUnavailable} attribute and it indicates that the element + * should be skipped based on the the current XSD version. + */ + private Boolean shouldSkipTypeUnavailable(Attributes atts) throws SAXException { + Boolean ret=false; + String typeUnavailable=getAttribute(VC_TYPE_UNAVAILABLE, atts); + Set supportedTypes = version.getSupportedTypes(); + + if (typeUnavailable != null) { + List types = parseQNameList(typeUnavailable); + int count=0, all=0; + for (QName type : types) { + if (supportedTypes.contains(type)) { count++; } + all++; + } + ret = (count == all); //if true, all types are available. + } + + return ret; + } + + /** + * @param atts A list of attributes contained by an element. + * @return true if the list of attributes contans a {@code + * vc:facetAvailable} attribute and it indicates that the element + * should be skipped based on the the current XSD version. + */ + private Boolean shouldSkipFacetAvailable(Attributes atts) throws SAXException { + Boolean ret=false; + String facetAvailable=getAttribute(VC_FACET_AVAILABLE, atts); + Set supportedFacets = version.getSupportedFacets(); + + if (facetAvailable != null) { + for (QName facet : parseQNameList(facetAvailable)) { + ret = ret | !supportedFacets.contains(facet); + if (ret) break; + } + } + return ret; + } + + /** + * @param atts A list of attributes contained by an element. + * @return true if the list of attributes contans a {@code + * vc:facetUnavailable} attribute and it indicates that the element + * should be skipped based on the the current XSD version. + */ + private Boolean shouldSkipFacetUnavailable(Attributes atts) throws SAXException { + Boolean ret=false; + String facetUnavailable=getAttribute(VC_FACET_UNAVAILABLE, atts); + Set supportedFacets = version.getSupportedFacets(); + + if (facetUnavailable != null) { + List facets = parseQNameList(facetUnavailable); + int count=0, all=0; + for (QName facet : facets) { + if (supportedFacets.contains(facet)) { count++; } + all++; + } + ret = (count == all); //if true, all facets are available. + } + + return ret; + } + + /** + * @param atts A list of attributes contained by an element. + * @return true if the list of attributes contans a version control + * attribute and it indicates that the element should be skipped + * based on the the current XSD version. + */ + private Boolean shouldSkipElement(Attributes atts) throws SAXException { + return + shouldSkipMinVersion(atts) || + shouldSkipMaxVersion(atts) || + shouldSkipTypeAvailable(atts) || + shouldSkipTypeUnavailable(atts) || + shouldSkipFacetAvailable(atts) || + shouldSkipFacetUnavailable(atts); + } + + /** + * Parses a list of QNames. + * + * @param qnl A whitespace seperated list of QNames in a string. + * @return A list of resolved QNames + * @throws org.xml.sax.SAXParseException if there is a formatting + * error in a QName or if a QName prefix cannot be resolved. + */ + private List parseQNameList(String qnl) throws SAXParseException { + List ret = new LinkedList(); + + String[] names = qnl.trim().split("\\s+"); + for (String name : names) { + String[] parts = name.split(":"); + switch (parts.length) { + case 1: + ret.add (createQName("", parts[0])); + break; + case 2: + ret.add (createQName(parts[0], parts[1])); + break; + default: + throw new SAXParseException(Messages.format(Messages.ERR_BAD_QNAME, name), locator); + } + } + + return ret; + } + + /** + * Creates a QName. + * + * @param prefix The prefix of the QName + * @param localPart The local part of the QName + * @return The resolved QName + * @throw org.xml.sax.SAXParseException if the QName prefix cannot + * be resolved. + */ + private QName createQName(String prefix, String localPart) throws SAXParseException { + if (!prefixMap.containsKey(prefix)) { + throw new SAXParseException(Messages.format(Messages.ERR_BAD_QNAME, prefix+":"+localPart), locator); + } + return new QName(prefixMap.get(prefix), localPart); + } + + @Override + public void startElement(String uri, String localName, + String qName, Attributes atts) + throws SAXException { + if (skippedElements.isEmpty() && !shouldSkipElement(atts)) { + super.startElement(uri, localName, qName, atts); + } else { + skippedElements.push(new QName(uri, localName)); + } + } + + @Override + public void endElement(String uri, String localName, String qName) + throws SAXException { + if (skippedElements.isEmpty()) { + super.endElement(uri, localName, qName); + } else { + skippedElements.pop(); + } + } + + @Override + public void startPrefixMapping(String prefix, String uri) + throws SAXException { + if (skippedElements.isEmpty()) { + super.startPrefixMapping(prefix, uri); + prefixMap.put(prefix, uri); + } + } + + @Override + public void endPrefixMapping(String prefix) + throws SAXException { + if (skippedElements.isEmpty()) { + super.endPrefixMapping(prefix); + prefixMap.remove(prefix); + } + } + + @Override + public void characters(char[] ch, int start, int length) + throws SAXException { + if (skippedElements.isEmpty()) { + super.characters(ch, start, length); + } + } + + @Override + public void ignorableWhitespace(char[] ch, int start, int length) + throws SAXException { + if (skippedElements.isEmpty()) { + super.ignorableWhitespace(ch, start, length); + } + } + + @Override + public void processingInstruction(String target, String data) + throws SAXException { + if (skippedElements.isEmpty()) { + super.processingInstruction(target, data); + } + } + + @Override + public void skippedEntity(String name) + throws SAXException { + if (skippedElements.isEmpty()) { + super.skippedEntity(name); + } + } + + @Override + public void setDocumentLocator(Locator locator) { + super.setDocumentLocator(locator); + this.locator = locator; + } +} diff --git a/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaVersionControlResolver.java b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaVersionControlResolver.java new file mode 100644 index 0000000..13d6a80 --- /dev/null +++ b/jaxb-ri/xjc/src/com/sun/tools/xjc/reader/xmlschema/parser/XMLSchemaVersionControlResolver.java @@ -0,0 +1,151 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.tools.xjc.reader.xmlschema.parser; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +import java.net.URI; +import java.net.URISyntaxException; + +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; + +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stream.StreamResult; + +import com.sun.tools.xjc.ErrorReceiver; + +import org.w3c.dom.DOMImplementation; + +import org.w3c.dom.bootstrap.DOMImplementationRegistry; + +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSInput; +import org.w3c.dom.ls.LSResourceResolver; + +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.XMLReaderFactory; + +import static org.w3c.dom.ls.DOMImplementationLS.MODE_SYNCHRONOUS; +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; + +public class XMLSchemaVersionControlResolver implements LSResourceResolver { + private LSResourceResolver parent; + private DOMImplementationLS domLSImpl; + private TransformerFactory transFactory; + private ErrorReceiver errorHandler; + + public XMLSchemaVersionControlResolver(LSResourceResolver parent, ErrorReceiver errorHandler) throws VersionControlTransformError { + this.parent = parent; + this.errorHandler = errorHandler; + transFactory = TransformerFactory.newInstance(); + try { + domLSImpl = (DOMImplementationLS) DOMImplementationRegistry.newInstance().getDOMImplementation("XML 3.0 LS"); + } catch (Exception e) { + throw new VersionControlTransformError(e); + } + if (domLSImpl == null) { + throw new VersionControlTransformError("DOM XML 3.0 LS unsupported"); + } + } + + private LSInput createParentInput(String publicId, String systemId, + String baseURI) { + + LSInput ret = domLSImpl.createLSInput(); + ret.setPublicId(publicId); + ret.setSystemId(systemId); + ret.setBaseURI(baseURI); + + return ret; + } + + @Override + public LSInput resolveResource(String type, String namespaceURI, + String publicId, String systemId, + String baseURI) { + LSInput ret = null; + LSInput pinput = (parent == null) ? createParentInput(publicId, systemId, baseURI) : + parent.resolveResource(type, namespaceURI, publicId, systemId, baseURI); + + if (type.equals(W3C_XML_SCHEMA_NS_URI)) { + try { + URI loadURI = new URI(pinput.getSystemId()); + if (pinput.getBaseURI() != null) { + URI loadBaseURI = new URI(pinput.getBaseURI()); + loadURI = loadBaseURI.resolve(loadURI); + } + + // + // Read XML passing it through the versioning filter, + // capture the resulting bytes. + // + ByteArrayOutputStream retOut = new ByteArrayOutputStream(); + transFactory.newTransformer().transform (new SAXSource(new XMLSchemaVersionControlFilter(XMLReaderFactory.createXMLReader()), + new InputSource(loadURI.toString())), + new StreamResult(retOut)); + + // + // Create LSInput from the resulting bytes. + // + ret = createParentInput(publicId, systemId, baseURI); + ret.setByteStream(new ByteArrayInputStream(retOut.toByteArray())); + + } catch (URISyntaxException e) { + errorHandler.error(Messages.format(Messages.ERR_BAD_URI, e.getInput()), e); + } catch (SAXException e) { + errorHandler.error(e); + } catch (TransformerConfigurationException e) { + throw new VersionControlTransformError(e); + } catch (TransformerException e) { + errorHandler.error(e); + } + } else { + ret = pinput; + } + + return ret; + } +}