--- old/test/javax/xml/jaxp/unittest/TEST.properties 2015-10-26 15:21:46.618717106 -0700 +++ new/test/javax/xml/jaxp/unittest/TEST.properties 2015-10-26 15:21:46.514711944 -0700 @@ -1,6 +1,8 @@ # jaxp test uses TestNG TestNG.dirs = . +lib.dirs = /javax/xml/jaxp/libs + # Declare module dependency modules=java.xml --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/AltCatalog.java 2015-10-26 15:21:46.878730011 -0700 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +/** + * Represents an alternative catalog entry. + * + * @since 9 + */ +class AltCatalog extends BaseEntry { + URI catalogURI; + + AltCatalog(CatalogEntryType type, String base) { + super(type, base); + } + + /** + * Set the catalog attribute. If the value of the catalog attribute is relative, it + * must be made absolute with respect to the base URI currently in effect. + * + * @param catalog The catalog attribute value. + * @throws CatalogException if converting to catalog URI failed + */ + void setCatalog(String catalog) { + URL url = verifyURI("catalog", baseURI, catalog); + try { + catalogURI = url.toURI(); + } catch (URISyntaxException ex) { + CatalogMessages.reportRunTimeError(CatalogMessages.ERR_OTHER, ex); + } + + } + + /** + * Returns the catalog attribute as an URI String. + * @return The value of the catalog attribute + */ + String getCatalogId() { + return catalogURI.toASCIIString(); + } + + /** + * Returns the catalog attribute as an URI. + * @return The value of the catalog attribute + */ + URI getCatalogURI() { + return catalogURI; + } + + /** + * Matches the specified id with the entry. Returns the match if it + * is successful and the length of the start String is longer than the + * longest of any previous match. + * + * @param id The id to be matched. + * @param currentMatch The length of start String of previous match if any. + * @return The replacement URI if the match is successful, null if not. + */ + public URI matchURI(String id, int currentMatch) { + return null; + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/BaseEntry.java 2015-10-26 15:21:47.210746490 -0700 @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Objects; +import jdk.xml.internal.SecuritySupport; + +/** + * Represents a general Catalog entry. + * + * @since 9 + */ +abstract class BaseEntry { + final String SLASH = "/"; + + CatalogEntryType type; + + //The id attribute + String id; + + //The attribute to be matched, e.g. systemId + String matchId; + + //The baseURI attribute + URL baseURI; + + //Indicates whether the base attribute is specified + boolean baseSpecified = false; + + /** + * CatalogEntryType represents catalog entry types. + */ + static enum CatalogEntryType { + + CATALOG("catalogfile"), + CATALOGENTRY("catalog"), + GROUP("group"), + PUBLIC("public"), + SYSTEM("system"), + REWRITESYSTEM("rewriteSystem"), + SYSTEMSUFFIX("systemSuffix"), + DELEGATEPUBLIC("delegatePublic"), + DELEGATESYSTEM("delegateSystem"), + URI("uri"), + REWRITEURI("rewriteURI"), + URISUFFIX("uriSuffix"), + DELEGATEURI("delegateURI"), + NEXTCATALOG("nextCatalog"); + + final String literal; + + CatalogEntryType(String literal) { + this.literal = literal; + } + + public boolean isType(String type) { + return literal.equals(type); + } + + static public CatalogEntryType getType(String entryType) { + for (CatalogEntryType type : CatalogEntryType.values()) { + if (type.isType(entryType)) { + return type; + } + } + return null; + } + } + + /** + * Constructs a CatalogEntry + * + * @param type The type of the entry + */ + public BaseEntry(CatalogEntryType type) { + this.type = Objects.requireNonNull(type); + } + + /** + * Constructs a CatalogEntry + * + * @param type The type of the entry + * @param base The base URI + */ + public BaseEntry(CatalogEntryType type, String base) { + this.type = Objects.requireNonNull(type); + setBaseURI(base); + } + + /** + * Returns the type of the entry + * + * @return The type of the entry + */ + public CatalogEntryType getType() { + return type; + } + + /** + * Sets the entry type + * + * @param type The entry type + */ + public void setType(CatalogEntryType type) { + this.type = type; + } + + /** + * Returns the id of the entry + * + * @return The id of the entry + */ + public String getId() { + return id; + } + + /** + * Set the entry Id + * + * @param id The Id attribute + */ + public void setId(String id) { + this.id = id; + } + + /** + * Sets the base URI for the entry + * + * @param base The base URI + */ + public final void setBaseURI(String base) { + baseURI = verifyURI("base", null, base); + } + + /** + * Gets the base URI for the entry + * + * @return The base URI as a string. + */ + public URL getBaseURI() { + return baseURI; + } + + /** + * Gets the attribute used for matching + * + * @return The value of the field + */ + public String getMatchId() { + return matchId; + } + + /** + * Sets the matchId field + * @param matchId The value of the Id + */ + public void setMatchId(String matchId) { + this.matchId = matchId; + } + + /** + * Matches the specified string with the identifier attribute of the entry. + * + * @param match The identifier attribute to be matched + * @return The replacement URI if a matching entry is found, null if not. + */ + public String match(String match) { + return null; + } + + /** + * Try to match the specified id with the entry. Return the match if it + * is successful and the length of the start String is longer than the + * longest of any previous match. + * + * @param id The id to be matched. + * @param currentMatch The length of start String of previous match if any. + * @return The replacement URI if the match is successful, null if not. + */ + public String match(String id, int currentMatch) { + return null; + } + + /** + * Verifies the specified URI. + * + * @param arg The name of the argument + * @param uri The URI to be verified + * @return The URI created from the specified uri + * @throws IllegalArgumentException if the specified uri is null, + * or an URL can not be created based on the specified base and uri + */ + URL verifyURI(String arg, URL base, String uri) { + if (uri == null) { + CatalogMessages.reportIAE(new Object[]{uri, arg}, null); + } + + URL url = null; + uri = Normalizer.normalizeURI(uri); + + try { + if (base != null) { + url = new URL(base, uri); + } else { + url = new URL(uri); + } + } catch (MalformedURLException e) { + CatalogMessages.reportIAE(new Object[]{uri, arg}, e); + } + return url; + } + + /** + * Replace backslashes with forward slashes. (URLs always use forward + * slashes.) + * + * @param sysid The input system identifier. + * @return The same system identifier with backslashes turned into forward + * slashes. + */ + protected String fixSlashes(String sysid) { + return sysid.replace('\\', '/'); + } + + /** + * Construct an absolute URI from a relative one, using the current base + * URI. + * + * @param sysid The (possibly relative) system identifier + * @return The system identifier made absolute with respect to the current + * {@link #base}. + */ + protected String makeAbsolute(String sysid) { + URL local = null; + + sysid = fixSlashes(sysid); + /** + * try { local = new URL(base, sysid); } catch (MalformedURLException e) + * { catalogManager.debug.message(1, "Malformed URL on system + * identifier", sysid); } + */ + if (local != null) { + return local.toString(); + } else { + return sysid; + } + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/Catalog.java 2015-10-26 15:21:47.594765549 -0700 @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.xml.catalog; + +import java.util.stream.Stream; + +/** + * The Catalog class represents an entity Catalog as defined by + * + * XML Catalogs, OASIS Standard V1.1, 7 October 2005. + *

+ * A catalog is an XML file that contains a root {@code catalog} entry with a list + * of catalog entries. The entries can also be grouped with a {@code group} entry. + * The catalog and group entries may specify {@code prefer} and {@code xml:base} + * attributes that set preference of public or system type of entries and base URI + * to resolve relative URIs. + * + *

+ * A catalog can be used in two situations: + *

+ *

+ * For case 1, the standard defines 6 External Identifier Entries:
+ * {@code public, system, rewriteSystem, systemSuffix, delegatePublic, and + * delegateSystem}. + *

+ * While for case 2, it defines 4 URI Entries:
+ * {@code uri, rewriteURI, uriSuffix and delegateURI}. + *

+ * In addition to the above entry types, a catalog may define nextCatalog + * entries to add additional catalog entry files. + *

+ * + * @since 9 + */ +public interface Catalog { + + /** + * Attempts to find a matching entry in the catalog by systemId. + * + *

+ * The method searches through the system-type entries, including {@code system, + * rewriteSystem, systemSuffix, delegateSystem}, and {@code group} entries in the + * current catalog in order to find a match. + *

+ * Resolution follows the steps listed below:
+ *

+ * + * @param systemId the system identifier of the entity to be matched + * + * @return an URI string if a mapping is found, or null otherwise + */ + public String matchSystem(String systemId); + + /** + * Attempts to find a matching entry in the catalog by publicId. The method + * searches through the public-type entries, including {@code public, + * delegatePublic}, and {@code group} entries in the current catalog in order to find + * a match. + *

+ * Refer to the description about + * Feature PREFER in the table Catalog Features in class + * {@link CatalogFeatures}. Public entries are only considered if the + * {@code prefer} is {@code public} and {@code system} entries are not found. + *

+ * Resolution follows the steps listed below:
+ *

+ * + * @param publicId the public identifier of the entity to be matched + * @see CatalogFeatures.Feature + * @return an URI string if a mapping is found, or null otherwise + */ + public String matchPublic(String publicId); + + /** + * Attempts to find a matching entry in the catalog by the uri element. + * + *

+ * The method searches through the uri-type entries, including {@code uri, + * rewriteURI, uriSuffix, delegateURI} and {@code group} entries in the current + * catalog in order to find a match. + * + *

+ * Resolution follows the steps listed below:
+ *

+ * + * @param uri the URI reference of the entity to be matched + * + * @return an URI string if a mapping is found, or null otherwise + */ + public String matchURI(String uri); + + /** + * Returns a sequential Stream of alternative Catalogs specified using the + * {@code nextCatalog} entries in the current catalog, and as the input of + * catalog files excluding the current catalog (that is, the first in the + * input list) when the Catalog object is created by the {@link CatalogManager}. + *

+ * The order of Catalogs in the returned stream is the same as the order + * in which the corresponding {@code nextCatalog} entries appear in the + * current catalog. The alternative catalogs from the input file list are + * appended to the end of the stream in the order they are entered. + * + * @return a sequential Stream of Catalogs + */ + public Stream catalogs(); + +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogEntry.java 2015-10-26 15:21:47.922781828 -0700 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +/** + * Represents the catalog element or entry of a catalog file + * + * @since 9 + */ +class CatalogEntry extends GroupEntry { + + /** + * Construct a catalog entry. + * + * @param base The baseURI attribute + * @param attributes The attributes + */ + public CatalogEntry(String base, String... attributes) { + super(base, attributes); + setType(CatalogEntryType.CATALOGENTRY); + } + + @Override + public String match(String match) { + throw new UnsupportedOperationException("Unsupported operation."); + } + +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogException.java 2015-10-26 15:21:48.246797910 -0700 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +/** + * The exception class handles errors that may happen while processing or using + * a catalog. + * + * @since 9 + */ +public class CatalogException extends RuntimeException { + + private static final long serialVersionUID = 653231525876459057L; + + /** + * Constructs a new CatalogException with the specified detail message. The + * cause is not initialized, and may subsequently be initialized by a call + * to {@link #initCause}. + * + * @param message the detail message + */ + public CatalogException(String message) { + super(message); + } + + /** + * Constructs a new CatalogException with the specified detail message and + * cause. + * + * @param message the detail message (which is saved for later retrieval by + * the {@link #getMessage()} method) + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A {@code null} value is permitted, and + * indicates that the cause is nonexistent or unknown.) + */ + public CatalogException(String message, Throwable cause) { + super(message, cause); + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogFeatures.java 2015-10-26 15:21:48.574814190 -0700 @@ -0,0 +1,534 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import jdk.xml.internal.SecuritySupport; + +/** + * The CatalogFeatures holds a collection of features and properties. + *

+ * + *

Catalog Features

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
FeatureDescriptionProperty NameSystem Property [1]jaxp.properties [1]Value [2]Action
TypeValue
FILESA semicolon-delimited list of catalog files. Relative file paths are + * considered relative to ${user.dir}. + * javax.xml.catalog.filesjavax.xml.catalog.filesjavax.xml.catalog.filesStringFile paths + * Reads the first catalog as the current catalog; Loads others if no match + * is found in the current catalog including delegate catalogs if any. + *
PREFERIndicates the preference between the public and system + * identifiers. The default value is public [3].javax.xml.catalog.preferN/AN/AString{@code system}Searches system entries for a match; Searches public entries when + * external identifier specifies only a public identifier
{@code public}Searches system entries for a match; Searches public entries when + * there is no matching system entry.
DEFERIndicates that the alternative catalogs including those + * specified in delegate entries or nextCatalog are not read until they are + * needed. The default value is true.javax.xml.catalog.defer [4]javax.xml.catalog.deferjavax.xml.catalog.deferString{@code true}Loads alternative catalogs as needed. + *
{@code false}Loads all catalogs[5].
RESOLVEDetermines the action if there is no matching entry found after + * all of the specified catalogs are exhausted. The default is strict.javax.xml.catalog.resolve [4]javax.xml.catalog.resolvejavax.xml.catalog.resolveString{@code strict}Throws CatalogException if there is no match. + *
{@code continue}Allows the XML parser to continue as if there is no match. + *
{@code ignore}Tells the XML parser to skip the external references if there no match. + *
+ *

+ * [1] There is no System property for the features that marked as "N/A". + * + *

+ * [2] The value shall be exactly as listed in this table, case-sensitive. + * Any unspecified value will result in {@link IllegalArgumentException}. + *

+ * [3] The Catalog specification defined complex rules on + * + * the prefer attribute. Although the prefer can be public or system, the + * specification actually made system the preferred option, that is, no matter + * the option, a system entry is always used if found. Public entries are only + * considered if the prefer is public and system entries are not found. It is + * therefore recommended that the prefer attribute be set as public + * (which is the default). + *

+ * [4] Although non-standard attributes in the OASIS Catalog specification, + * {@code defer} and {@code resolve} are recognized by the Java Catalog API the + * same as the {@code prefer} as being an attribute in the catalog entry of the + * main catalog. Note that only the attributes specified for the catalog entry + * of the main Catalog file will be used. + *

+ * [5] If the intention is to share an entire catalog store, it may be desirable to + * set the property {@code javax.xml.catalog.defer} to false to allow the entire + * catalog to be pre-loaded. + *

+ *

Scope and Order

+ * Features and properties can be set through the catalog file, the Catalog API, + * system properties, and {@code jaxp.properties}, with a preference in the same order. + *

+ * Properties that are specified as attributes in the catalog file for the + * catalog and group entries shall take preference over any of the other settings. + * For example, if a {@code prefer} attribute is set in the catalog file as in + * {@code }, any other input for the "prefer" property + * is not necessary or will be ignored. + *

+ * Properties set through the Catalog API override those that may have been set + * by system properties and/or in {@code jaxp.properties}. In case of multiple + * interfaces, the latest in a procedure shall take preference. For + * {@link Feature#FILES}, this means that the path(s) specified through the methods + * of the {@link CatalogManager} will override any that may have been entered + * through the {@link Builder}. + * + *

+ * System properties when set shall override those in {@code jaxp.properties}. + *

+ * The {@code jaxp.properties} file is typically in the conf directory of the Java + * installation. The file is read only once by the JAXP implementation and + * its values are then cached for future use. If the file does not exist + * when the first attempt is made to read from it, no further attempts are + * made to check for its existence. It is not possible to change the value + * of any properties in {@code jaxp.properties} after it has been read. + *

+ * A CatalogFeatures instance can be created through its builder as illustrated + * in the following sample code: + *

{@code
+                CatalogFeatures f = CatalogFeatures.builder()
+                        .with(Feature.FILES, "catalog.xml")
+                        .with(Feature.PREFER, "public")
+                        .with(Feature.DEFER, "true")
+                        .with(Feature.RESOLVE, "ignore")
+                        .build();
+ * }
+ * + * @since 9 + */ +public class CatalogFeatures { + + /** + * The constant name of the javax.xml.catalog.files property. See the property table for more details. + */ + static final String CATALOG_FILES = "javax.xml.catalog.files"; + + /** + * The javax.xml.catalog.prefer property. See the property table for more details. + */ + static final String CATALOG_PREFER = "javax.xml.catalog.prefer"; + + /** + * Determines whether or not delegated catalogs and nextCatalog will be read + * when the current catalog is loaded. + */ + static final String CATALOG_DEFER = "javax.xml.catalog.defer"; + + /** + * Determines the action if there is no matching entry found after + * all of the specified catalogs are exhausted. + */ + static final String CATALOG_RESOLVE = "javax.xml.catalog.resolve"; + + //values for the prefer property + static final String PREFER_SYSTEM = "system"; + static final String PREFER_PUBLIC = "public"; + + //values for the defer property + static final String DEFER_TRUE = "true"; + static final String DEFER_FALSE = "false"; + + //values for the Resolve property + static final String RESOLVE_STRICT = "strict"; + static final String RESOLVE_CONTINUE = "continue"; + static final String RESOLVE_IGNORE = "ignore"; + + /** + * A Feature type as defined in the + * Catalog Features table. + */ + public static enum Feature { + /** + * The {@code javax.xml.catalog.files} property as described in + * item FILES of the + * Catalog Features table. + */ + FILES(CATALOG_FILES, null, true), + /** + * The {@code javax.xml.catalog.prefer} property as described in + * item PREFER of the + * Catalog Features table. + */ + PREFER(CATALOG_PREFER, PREFER_PUBLIC, false), + /** + * The {@code javax.xml.catalog.defer} property as described in + * item DEFER of the + * Catalog Features table. + */ + DEFER(CATALOG_DEFER, DEFER_TRUE, true), + /** + * The {@code javax.xml.catalog.resolve} property as described in + * item RESOLVE of the + * Catalog Features table. + */ + RESOLVE(CATALOG_RESOLVE, RESOLVE_STRICT, true); + + private final String name; + private final String defaultValue; + private String value; + private final boolean hasSystem; + + /** + * Constructs a CatalogFeature instance. + * @param name the name of the feature + * @param value the value of the feature + * @param hasSystem a flag to indicate whether the feature is supported + * with a System property + */ + Feature(String name, String value, boolean hasSystem) { + this.name = name; + this.defaultValue = value; + this.hasSystem = hasSystem; + } + + /** + * Checks whether the specified property is equal to the current property. + * @param propertyName the name of a property + * @return true if the specified property is the current property, false + * otherwise + */ + boolean equalsPropertyName(String propertyName) { + return name.equals(propertyName); + } + + /** + * Returns the name of the corresponding System Property. + * + * @return the name of the System Property + */ + public String getPropertyName() { + return name; + } + + /** + * Returns the default value of the property. + * @return the default value of the property + */ + String defaultValue() { + return defaultValue; + } + + /** + * Returns the value of the property. + * @return the value of the property + */ + String getValue() { + return value; + } + + /** + * Checks whether System property is supported for the feature. + * @return true it is supported, false otherwise + */ + boolean hasSystemProperty() { + return hasSystem; + } + } + + /** + * States of the settings of a property, in the order: default value, + * jaxp.properties file, jaxp system properties, and jaxp api properties + */ + static enum State { + /** represents the default state of a feature. */ + DEFAULT("default"), + /** indicates the value of the feature is read from jaxp.properties. */ + JAXPDOTPROPERTIES("jaxp.properties"), + /** indicates the value of the feature is read from its System property. */ + SYSTEMPROPERTY("system property"), + /** indicates the value of the feature is specified through the API. */ + APIPROPERTY("property"), + /** indicates the value of the feature is specified as a catalog attribute. */ + CATALOGATTRIBUTE("catalog attribute"); + + final String literal; + + State(String literal) { + this.literal = literal; + } + + String literal() { + return literal; + } + } + + /** + * Values of the properties + */ + private String[] values; + + /** + * States of the settings for each property + */ + private State[] states; + + /** + * Private class constructor + */ + private CatalogFeatures() { + } + + /** + * Returns a CatalogFeatures instance with default settings. + * @return a default CatalogFeatures instance + */ + public static CatalogFeatures defaults() { + return CatalogFeatures.builder().build(); + } + + /** + * Constructs a new CatalogFeatures instance with the builder. + * + * @param builder the builder to build the CatalogFeatures + */ + CatalogFeatures(Builder builder) { + init(); + setProperty(Feature.FILES.ordinal(), State.APIPROPERTY, builder.files); + setProperty(Feature.PREFER.ordinal(), State.APIPROPERTY, builder.prefer); + setProperty(Feature.DEFER.ordinal(), State.APIPROPERTY, builder.defer); + setProperty(Feature.RESOLVE.ordinal(), State.APIPROPERTY, builder.resolve); + } + + /** + * Returns the value of the specified feature. + * + * @param cf the type of the Catalog feature + * @return the value of the feature + */ + public String get(Feature cf) { + return values[cf.ordinal()]; + } + + /** + * Initializes the supported properties + */ + private void init() { + values = new String[Feature.values().length]; + states = new State[Feature.values().length]; + for (Feature cf : Feature.values()) { + setProperty(cf.ordinal(), State.DEFAULT, cf.defaultValue()); + } + //read system properties or jaxp.properties + readSystemProperties(); + } + + /** + * Sets the value of a property by its index, updates only if it shall override. + * + * @param index the index of the property + * @param state the state of the property + * @param value the value of the property + * @throws IllegalArgumentException if the value is invalid + */ + private void setProperty(int index, State state, String value) { + if (value != null && value.length() != 0) { + if (index == Feature.PREFER.ordinal()) { + if (!value.equals(PREFER_SYSTEM) && !value.equals(PREFER_PUBLIC)) { + CatalogMessages.reportIAE(new Object[]{value, Feature.PREFER.name()}, null); + } + } else if (index == Feature.DEFER.ordinal()) { + if (!value.equals(DEFER_TRUE) && !value.equals(DEFER_FALSE)) { + CatalogMessages.reportIAE(new Object[]{value, Feature.DEFER.name()}, null); + } + } else if (index == Feature.RESOLVE.ordinal()) { + if (!value.equals(RESOLVE_STRICT) && !value.equals(RESOLVE_CONTINUE) + && !value.equals(RESOLVE_IGNORE)) { + CatalogMessages.reportIAE(new Object[]{value, Feature.RESOLVE.name()}, null); + } + } + if (states[index] == null || state.compareTo(states[index]) >= 0) { + values[index] = value; + states[index] = state; + } + } + } + + /** + * Reads from system properties, or those in jaxp.properties + */ + private void readSystemProperties() { + for (Feature cf : Feature.values()) { + getSystemProperty(cf, cf.getPropertyName()); + } + } + + /** + * Reads from system properties, or those in jaxp.properties + * + * @param cf the type of the property + * @param sysPropertyName the name of system property + */ + private boolean getSystemProperty(Feature cf, String sysPropertyName) { + if (cf.hasSystemProperty()) { + String value = SecuritySupport.getSystemProperty(sysPropertyName); + if (value != null && !value.equals("")) { + setProperty(cf.ordinal(), State.SYSTEMPROPERTY, value); + return true; + } + + value = SecuritySupport.readJAXPProperty(sysPropertyName); + if (value != null && !value.equals("")) { + setProperty(cf.ordinal(), State.JAXPDOTPROPERTIES, value); + return true; + } + } + return false; + } + + /** + * Returns an instance of the builder for creating the CatalogFeatures object. + * + * @return an instance of the builder + */ + public static Builder builder() { + return new CatalogFeatures.Builder(); + } + + /** + * The Builder class for building the CatalogFeatures object. + */ + public static class Builder { + /** + * Variables for the features supported by CatalogFeatures. + */ + String files, prefer, defer, resolve; + + /** + * Instantiation of Builder is not allowed. + */ + private Builder() {} + + /** + * Sets the value to a specified Feature. + * @param feature the Feature to be set + * @param value the value to be set for the Feature + * @return this Builder instance + * @throws IllegalArgumentException if the value is not valid for the + * Feature or has the wrong syntax for the {@code javax.xml.catalog.files} + * property + */ + public Builder with(Feature feature, String value) { + switch (feature) { + case FILES : + files = value; + break; + case PREFER : + prefer = value; + break; + case DEFER : + defer = value; + break; + case RESOLVE : + resolve = value; + break; + } + return this; + } + + /** + * Returns a CatalogFeatures object built by this builder. + * + * @return an instance of CatalogFeatures + */ + public CatalogFeatures build() { + return new CatalogFeatures(this); + } + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogImpl.java 2015-10-26 15:21:49.134841985 -0700 @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import static javax.xml.catalog.BaseEntry.CatalogEntryType; +import static javax.xml.catalog.CatalogFeatures.DEFER_TRUE; +import javax.xml.catalog.CatalogFeatures.Feature; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.SAXException; + +/** + * Implementation of the Catalog. + * + * @since 9 + */ +class CatalogImpl extends GroupEntry implements Catalog { + + //Catalog level, 0 means the top catalog + int level = 0; + + //Value of the defer attribute to determine if alternative catalogs are read + boolean isDeferred = true; + + //Value of the resolve attribute + ResolveType resolveType = ResolveType.STRICT; + + //indicate whether the Catalog is empty + boolean isEmpty; + + //Current parsed Catalog + int current = 0; + + //System Id for this catalog + String systemId; + + //The parent + CatalogImpl parent = null; + + /* + A list of catalog entry files from the input, excluding the current catalog. + Paths in the List are normalized. + */ + List inputFiles; + + //A list of catalogs specified using the nextCatalog element + List nextCatalogs; + + //reuse the parser + SAXParser parser; + + /** + * Construct a Catalog with specified path. + * + * @param file The path to a catalog file. + * @throws CatalogException If an error happens while parsing the specified + * catalog file. + */ + public CatalogImpl(CatalogFeatures f, String... file) throws CatalogException { + this(null, f, file); + } + + /** + * Construct a Catalog with specified path. + * + * @param parent The parent catalog + * @param file The path to a catalog file. + * @throws CatalogException If an error happens while parsing the specified + * catalog file. + */ + public CatalogImpl(CatalogImpl parent, CatalogFeatures f, String... file) throws CatalogException { + super(CatalogEntryType.CATALOG); + this.parent = parent; + if (parent == null) { + level = 0; + } else { + level = parent.level + 1; + } + if (f == null) { + this.features = CatalogFeatures.defaults(); + } else { + this.features = f; + } + setPrefer(features.get(Feature.PREFER)); + setDeferred(features.get(Feature.DEFER)); + setResolve(features.get(Feature.RESOLVE)); + + //Path of catalog files + String[] catalogFile = file; + if (level == 0 + && (file == null || (file.length == 0 || file[0] == null))) { + String files = features.get(Feature.FILES); + if (files != null) { + catalogFile = files.split(";[ ]*"); + } + } + + /* + In accordance with 8. Resource Failures of the Catalog spec, missing + Catalog entry files are to be ignored. + */ + if ((catalogFile != null && catalogFile.length > 0)) { + int start = 0; + URI uri = null; + for (String temp : catalogFile) { + uri = getSystemId(temp); + start++; + if (verifyCatalogFile(uri)) { + systemId = uri.toASCIIString(); + break; + } + } + + //Save the rest of input files as alternative catalogs + if (level == 0 && catalogFile.length > start) { + inputFiles = new ArrayList<>(); + for (int i = start; i < catalogFile.length; i++) { + if (catalogFile[i] != null) { + inputFiles.add(catalogFile[i]); + } + } + } + + if (systemId != null) { + parse(systemId); + } + } + } + + /** + * Resets the Catalog instance to its initial state. + */ + @Override + public void reset() { + super.reset(); + current = 0; + if (level == 0) { + catalogsSearched.clear(); + } + entries.stream().filter((entry) -> (entry.type == CatalogEntryType.GROUP)).forEach((entry) -> { + ((GroupEntry) entry).reset(); + }); + + if (parent != null) { + this.loadedCatalogs = parent.loadedCatalogs; + this.catalogsSearched = parent.catalogsSearched; + } + } + + /** + * Returns whether this Catalog instance is the top (main) Catalog. + * + * @return true if the instance is the top Catalog, false otherwise + */ + boolean isTop() { + return level == 0; + } + + /** + * Gets the parent of this catalog. + * + * @returns The parent catalog + */ + public Catalog getParent() { + return this.parent; + } + + /** + * Sets the defer property. If the value is null or empty, or any String + * other than the defined, it will be assumed as the default value. + * + * @param value The value of the defer attribute + */ + public final void setDeferred(String value) { + isDeferred = DEFER_TRUE.equals(value); + } + + /** + * Queries the defer attribute + * + * @return true if the prefer attribute is set to system, false if not. + */ + public boolean isDeferred() { + return isDeferred; + } + + /** + * Sets the resolve property. If the value is null or empty, or any String + * other than the defined, it will be assumed as the default value. + * + * @param value The value of the resolve attribute + */ + public final void setResolve(String value) { + resolveType = ResolveType.getType(value); + } + + /** + * Gets the value of the resolve attribute + * + * @return The value of the resolve attribute + */ + public final ResolveType getResolve() { + return resolveType; + } + + /** + * Marks the Catalog as being searched already. + */ + void markAsSearched() { + catalogsSearched.add(systemId); + } + + /** + * Parses the catalog. + * + * @param systemId The systemId of the catalog + * @throws CatalogException if parsing the catalog failed + */ + private void parse(String systemId) { + if (parser == null) { + parser = getParser(); + } + + try { + CatalogReader reader = new CatalogReader(this, parser); + parser.parse(systemId, reader); + } catch (SAXException | IOException ex) { + CatalogMessages.reportRunTimeError(CatalogMessages.ERR_PARSING_FAILED, ex); + } + } + + /** + * Resolves the specified file path to an absolute systemId. If it is + * relative, it shall be resolved using the base or user.dir property if + * base is not specified. + * + * @param file The specified file path + * @return The systemId of the file + * @throws CatalogException if the specified file path can not be converted + * to a system id + */ + private URI getSystemId(String file) { + URL filepath; + if (file != null && file.length() > 0) { + try { + File f = new File(file); + if (baseURI != null && !f.isAbsolute()) { + filepath = new URL(baseURI, fixSlashes(file)); + return filepath.toURI(); + } else { + return resolveURI(file); + } + } catch (MalformedURLException | URISyntaxException e) { + CatalogMessages.reportRunTimeError(CatalogMessages.ERR_INVALID_PATH, + new Object[]{file}, e); + } + } + return null; + } + + /** + * Resolves the specified uri. If the uri is relative, makes it absolute by + * the user.dir directory. + * + * @param uri The specified URI. + * @return The resolved URI + */ + private URI resolveURI(String uri) throws MalformedURLException { + if (uri == null) { + uri = ""; + } + + URI temp = toURI(uri); + String str = temp.toASCIIString(); + String base = str.substring(0, str.lastIndexOf('/') + 1); + baseURI = new URL(str); + + return temp; + } + + /** + * Converts an URI string or file path to URI. + * + * @param uri an URI string or file path + * @return an URI + */ + private URI toURI(String uri) { + URI temp = null; + try { + URL url = new URL(uri); + temp = url.toURI(); + } catch (MalformedURLException | URISyntaxException mue) { + File file = new File(uri); + temp = file.toURI(); + } + return temp; + } + + /** + * Returns a SAXParser instance + * @return a SAXParser instance + * @throws CatalogException if constructing a SAXParser failed + */ + private SAXParser getParser() { + SAXParser p = null; + try { + SAXParserFactory spf = new SAXParserFactoryImpl(); + spf.setNamespaceAware(true); + spf.setValidating(false); + spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + p = spf.newSAXParser(); + } catch (ParserConfigurationException | SAXException e) { + CatalogMessages.reportRunTimeError(CatalogMessages.ERR_PARSING_FAILED, e); + } + return p; + } + + /** + * Indicate that the catalog is empty + * + * @return True if the catalog is empty; False otherwise. + */ + public boolean isEmpty() { + return isEmpty; + } + + @Override + public Stream catalogs() { + Iterator iter = new Iterator() { + Catalog nextCatalog = null; + + //Current index of the input files + int inputFilesIndex = 0; + + //Next catalog + int nextCatalogIndex = 0; + + @Override + public boolean hasNext() { + if (nextCatalog != null) { + return true; + } else { + nextCatalog = nextCatalog(); + return (nextCatalog != null); + } + } + + @Override + public Catalog next() { + if (nextCatalog != null || hasNext()) { + Catalog catalog = nextCatalog; + nextCatalog = null; + return catalog; + } else { + throw new NoSuchElementException(); + } + } + + /** + * Returns the next alternative catalog. + * + * @return the next catalog if any + */ + private Catalog nextCatalog() { + Catalog c = null; + + //Check those specified in nextCatalogs + if (nextCatalogs != null) { + while (c == null && nextCatalogIndex < nextCatalogs.size()) { + c = getCatalog(nextCatalogs.get(nextCatalogIndex++).getCatalogURI()); + } + } + + //Check the input list + if (c == null && inputFiles != null) { + while (c == null && inputFilesIndex < inputFiles.size()) { + c = getCatalog(getSystemId(inputFiles.get(inputFilesIndex++))); + } + } + + return c; + } + }; + + return StreamSupport.stream(Spliterators.spliteratorUnknownSize( + iter, Spliterator.ORDERED | Spliterator.NONNULL), false); + } + + /** + * Adds the catalog to the nextCatalog list + * + * @param catalog a catalog specified in a nextCatalog entry + */ + void addNextCatalog(NextCatalog catalog) { + if (catalog == null) { + return; + } + + if (nextCatalogs == null) { + nextCatalogs = new ArrayList<>(); + } + + nextCatalogs.add(catalog); + } + + /** + * Loads all alternative catalogs. + */ + void loadNextCatalogs() { + //loads catalogs specified in nextCatalogs + if (nextCatalogs != null) { + for (NextCatalog next : nextCatalogs) { + getCatalog(next.getCatalogURI()); + } + } + + //loads catalogs from the input list + if (inputFiles != null) { + for (String file : inputFiles) { + getCatalog(getSystemId(file)); + } + } + } + + /** + * Returns a Catalog object by the specified path. + * + * @param path the path to a catalog + * @return a Catalog object + */ + Catalog getCatalog(URI uri) { + if (uri == null) { + return null; + } + + Catalog c = null; + String path = uri.toASCIIString(); + + if (verifyCatalogFile(uri)) { + c = getLoadedCatalog(path); + if (c == null) { + c = new CatalogImpl(this, features, path); + saveLoadedCatalog(path, c); + } + } + return c; + } + + /** + * Saves a loaded Catalog. + * + * @param catalogId the catalogId associated with the Catalog object + * @param c the Catalog to be saved + */ + void saveLoadedCatalog(String catalogId, Catalog c) { + loadedCatalogs.put(catalogId, c); + } + + /** + * Returns a count of all loaded catalogs, including delegate catalogs. + * + * @return a count of all loaded catalogs + */ + int loadedCatalogCount() { + return loadedCatalogs.size() + delegateCatalogs.size(); + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogManager.java 2015-10-26 15:21:49.466858463 -0700 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + + +/** + * The Catalog Manager manages the creation of XML Catalogs and Catalog Resolvers. + * + * @since 9 + */ +public final class CatalogManager { + /** + * Creating CatalogManager instance is not allowed. + */ + private CatalogManager() { + } + + /** + * Creates a Catalog object using the specified feature settings and path to + * a catalog file. If the features is null, the default features will be used. + * If the path is empty, System property {@code javax.xml.catalog.files} will + * be read to locate the initial list of catalog files. + *

+ * If more than one catalog files are specified through the path argument or + * {@code javax.xml.catalog.files} property, the first entry is considered + * the main catalog, while others are treated as alternative catalogs after + * those referenced by the {@code nextCatalog} elements in the main catalog. + * + * @param features the catalog features + * @param path path(s) to one or more catalogs. + * + * @return a catalog instance + * @throws CatalogException If no catalog can be found whether through the + * specified path or the System property {@code javax.xml.catalog.files}, or + * an error occurs while parsing the catalog + */ + public static Catalog catalog(CatalogFeatures features, String... path) { + return new CatalogImpl(features, path); + } + + /** + * Creates an instance of a CatalogResolver using the specified catalog. + * + * @param catalog the catalog instance + * @return an instance of a CatalogResolver + */ + public static CatalogResolver catalogResolver(Catalog catalog) { + if (catalog == null) CatalogMessages.reportNPEOnNull("catalog", null); + return new CatalogResolverImpl(catalog); + } + + /** + * Creates an instance of a CatalogUriResolver using the specified catalog. + * + * @param catalog the catalog instance + * @return an instance of a CatalogResolver + */ + public static CatalogUriResolver catalogUriResolver(Catalog catalog) { + if (catalog == null) CatalogMessages.reportNPEOnNull("catalog", null); + return new CatalogUriResolverImpl(catalog); + } + + /** + * Creates an instance of a CatalogResolver using the specified feature settings + * and path to a catalog file. If the features is null, the default features will + * be used. If the path is empty, System property {@code javax.xml.catalog.files} + * will be read to locate the initial list of catalog files. + *

+ * If more than one catalog files are specified through the path argument or + * {@code javax.xml.catalog.files} property, the first entry is considered + * the main catalog, while others are treated as alternative catalogs after + * those referenced by the {@code nextCatalog} elements in the main catalog. + * + * @param features the catalog features + * @param path the path(s) to one or more catalogs + * + * @return an instance of a CatalogResolver + * @throws CatalogException If no catalog can be found whether through the + * specified path or the System property {@code javax.xml.catalog.files}, or + * an error occurs while parsing the catalog + */ + public static CatalogResolver catalogResolver(CatalogFeatures features, String... path) { + Catalog catalog = catalog(features, path); + return new CatalogResolverImpl(catalog); + } + + /** + * Creates an instance of a CatalogUriResolver using the specified feature settings + * and path to a catalog file. If the features is null, the default features will + * be used. If the path is empty, System property {@code javax.xml.catalog.files} + * will be read to locate the initial list of catalog files. + *

+ * If more than one catalog files are specified through the path argument or + * {@code javax.xml.catalog.files} property, the first entry is considered + * the main catalog, while others are treated as alternative catalogs after + * those referenced by the {@code nextCatalog} elements in the main catalog. + * + * @param features the catalog features + * @param path the path(s) to one or more catalogs + * + * @return an instance of a CatalogResolver + * @throws CatalogException If no catalog can be found whether through the + * specified path or the System property {@code javax.xml.catalog.files}, or + * an error occurs while parsing the catalog + */ + public static CatalogUriResolver catalogUriResolver(CatalogFeatures features, String... path) { + Catalog catalog = catalog(features, path); + return new CatalogUriResolverImpl(catalog); + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogMessages.java 2015-10-26 15:21:49.826876331 -0700 @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import jdk.xml.internal.SecuritySupport; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Catalog Error messages + * + * @since 9 + */ +final class CatalogMessages { + + public static final String ERR_INVALID_CATALOG = "InvalidCatalog"; + public static final String ERR_INVALID_ENTRY_TYPE = "InvalidEntryType"; + public static final String ERR_INVALID_ARGUMENT = "InvalidArgument"; + public static final String ERR_NULL_ARGUMENT = "NullArgument"; + public static final String ERR_CIRCULAR_REFERENCE = "CircularReference"; + public static final String ERR_INVALID_PATH = "InvalidPath"; + public static final String ERR_PARSER_CONF = "ParserConf"; + public static final String ERR_PARSING_FAILED = "ParsingFailed"; + public static final String ERR_NO_CATALOG = "NoCatalogFound"; + public static final String ERR_NO_MATCH = "NoMatchFound"; + public static final String ERR_NO_URI_MATCH = "NoMatchURIFound"; + public static final String ERR_CREATING_URI = "FailedCreatingURI"; + public static final String ERR_OTHER = "OtherError"; + + static final String bundleName = CatalogMessages.class.getPackage().getName() + ".CatalogMessages"; + static ResourceBundle resourceBundle; + + /** + * Reports an error. + * @param key the message key + */ + static void reportError(String key) { + reportError(key, null); + } + + /** + * Reports an error. + * @param key the message key + * @param arguments the message replacement text arguments. The order of the + * arguments must match that of the placeholders in the actual message. + */ + static void reportError(String key, Object[] arguments) { + throw new CatalogException(formatMessage(key, arguments)); + } + + /** + * Reports a CatalogException. + * @param key the message key + * @param arguments the message replacement text arguments. The order of the + * arguments must match that of the placeholders in the actual message. + */ + static void reportRunTimeError(String key, Object[] arguments) { + throw new CatalogException(formatMessage(key, arguments)); + } + + /** + * Reports a CatalogException. + * @param key the message key + * @param cause the cause if any + */ + static void reportRunTimeError(String key, Throwable cause) { + throw new CatalogException(formatMessage(key, null), cause); + } + + /** + * Reports a CatalogException. + * @param key the message key + * @param arguments the message replacement text arguments. The order of the + * arguments must match that of the placeholders in the actual message. + * @param cause the cause if any + */ + static void reportRunTimeError(String key, Object[] arguments, Throwable cause) { + throw new CatalogException(formatMessage(key, arguments), cause); + } + + /** + * Reports IllegalArgumentException if the argument is null. + * + * @param name the name of the argument + * @param value the value of the argument + */ + static void reportIAEOnNull(String name, String value) { + if (value == null) { + throw new IllegalArgumentException( + formatMessage(ERR_INVALID_ARGUMENT, new Object[]{null, name})); + } + } + + /** + * Reports NullPointerException if the argument is null. + * + * @param name the name of the argument + * @param value the value of the argument + */ + static void reportNPEOnNull(String name, String value) { + if (value == null) { + throw new NullPointerException( + formatMessage(ERR_NULL_ARGUMENT, new Object[]{name})); + } + } + + /** + * Reports IllegalArgumentException + * @param arguments the arguments for formating the error message + * @param cause the cause if any + */ + static void reportIAE(Object[] arguments, Throwable cause) { + throw new IllegalArgumentException( + formatMessage(ERR_INVALID_ARGUMENT, arguments), cause); + } + + /** + * Format a message with the specified arguments using the default locale + * information. + * + * @param key the message key + * @param arguments the message replacement text arguments. The order of the + * arguments must match that of the placeholders in the actual message. + * + * @return the formatted message + * + * @throws MissingResourceException If the message with the specified key + * cannot be found + */ + static String formatMessage(String key, Object[] arguments) { + return formatMessage(key, arguments, Locale.getDefault()); + } + + /** + * Format a message with the specified arguments using the given locale + * information. + * + * @param key the message key + * @param arguments the message replacement text arguments. The order of the + * arguments must match that of the placeholders in the actual message. + * @param locale the locale of the message + * + * @return the formatted message + * + * @throws MissingResourceException If the message with the specified key + * cannot be found + */ + static String formatMessage(String key, Object[] arguments, Locale locale) { + return SecuritySupport.getErrorMessage(locale, bundleName, key, arguments); + } + + /** + * Returns sanitized URI. + * @param uri an URI to be sanitized + */ + static String sanitize(String uri) { + if (uri == null) { + return null; + } + String temp; + int p; + p = uri.lastIndexOf("/"); + if (p > 0 && p < uri.length()) { + return uri.substring(p + 1); + } + return uri; + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogMessages.properties 2015-10-26 15:21:50.154892611 -0700 @@ -0,0 +1,43 @@ +# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +# Messages for message reporting +BadMessageKey = The error message corresponding to the message key can not be found. +FormatFailed = An internal error occurred while formatting the following message:\n + +#invalid catalog file +InvalidCatalog = The document element of a catalog must be catalog. +InvalidEntryType = The entry type ''{0}'' is not valid. +CircularReference = Circular reference is not allowed: ''{0}''. + +#errors +InvalidArgument = The specified argument ''{0}'' (case sensitive) for ''{1}'' is not valid. +NullArgument = The argument ''{0}'' can not be null. +InvalidPath = The path ''{0}'' is invalid. +ParserConf = Unexpected error while configuring a SAX parser. +ParsingFailed = Failed to parse the catalog file. +NoCatalogFound = No Catalog is specified. +NoMatchFound = No match found for publicId ''{0}'' and systemId ''{1}''. +NoMatchURIFound = No match found for href ''{0}'' and base ''{1}''. +FailedCreatingURI = Can not construct URI using href ''{0}'' and base ''{1}''. +OtherError = Unexpected error. \ No newline at end of file --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogReader.java 2015-10-26 15:21:50.482908891 -0700 @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.io.StringReader; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import javax.xml.catalog.BaseEntry.CatalogEntryType; +import javax.xml.parsers.SAXParser; +import javax.xml.transform.Source; +import javax.xml.transform.TransformerException; +import javax.xml.transform.URIResolver; +import javax.xml.transform.sax.SAXSource; +import org.xml.sax.Attributes; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * CatalogReader handles SAX events while parsing through a catalog file to + * create a catalog object. + * + * @since 9 + */ +class CatalogReader extends DefaultHandler implements EntityResolver, URIResolver { + /** URI of the W3C XML Schema for OASIS XML Catalog files. */ + public static final String xmlCatalogXSD = "http://www.oasis-open.org/committees/entity/release/1.0/catalog.xsd"; + + /** Public identifier for OASIS XML Catalog files. */ + public static final String xmlCatalogPubId = "-//OASIS//DTD XML Catalogs V1.0//EN"; + + /** + * The namespace name defined by the OASIS Standard + */ + public static final String NAMESPACE_OASIS = "urn:oasis:names:tc:entity:xmlns:xml:catalog"; + + //Indicate whether the root element has been found + boolean seenRoot; + + //Indicate that the parser is in a group entry + boolean inGroup; + + //The Catalog instance + CatalogImpl catalog; + + //The parser for reading the catalog + SAXParser parser; + + //The current catalog entry + CatalogEntry catalogEntry; + + //The current group + GroupEntry group; + + //The current entry + BaseEntry entry; + + //remove this variable once 8136778 is committed + boolean ignoreTheCatalog = false; + + /** + * Constructs an instance with a Catalog object and parser. + * + * @param catalog The Catalog object that represents a catalog + */ + @SuppressWarnings("unchecked") + public CatalogReader(Catalog catalog, SAXParser parser) { + this.catalog = (CatalogImpl) catalog; + this.parser = parser; + } + + /** + * Returns when the specified path is valid. + * @param path a path + * @return true if the path is valid, false otherwise + */ + boolean isValidPath(String path) { + boolean valid = true; + try { + Path p = Paths.get(new URI(path)); + if (!p.toFile().exists()) { + valid = false; + } + } catch (URISyntaxException ex) { + valid = false; + } + + return valid; + } + + @Override + public void startElement(String namespaceURI, + String localName, + String qName, + Attributes atts) + throws SAXException { + + //ignore the catalog if it's not compliant. See section 8, item 3 of the spec. + if (ignoreTheCatalog) return; + if (!NAMESPACE_OASIS.equals(namespaceURI)) { +//wait till 8136778 is committed +// parser.stop(); + ignoreTheCatalog = true; + return; + } + + + CatalogEntryType type = CatalogEntryType.getType(localName); + if (type == null) { + CatalogMessages.reportError(CatalogMessages.ERR_INVALID_ENTRY_TYPE, + new Object[]{localName}); + } + if (type != CatalogEntryType.CATALOGENTRY) { + if (!seenRoot) { + CatalogMessages.reportError(CatalogMessages.ERR_INVALID_CATALOG); + } + } + + String base = atts.getValue("xml:base"); + if (base == null) { + if (inGroup) { + base = group.getBaseURI().toString(); + } else { + if (type == CatalogEntryType.CATALOGENTRY) { + base = catalog.getBaseURI().toString(); + } else { + base = catalogEntry.getBaseURI().toString(); + } + } + } else { + base = Normalizer.normalizeURI(base); + } + + //parse the catalog and group entries + if (type == CatalogEntryType.CATALOGENTRY + || type == CatalogEntryType.GROUP) { + String prefer = atts.getValue("prefer"); + if (prefer == null) { + if (type == CatalogEntryType.CATALOGENTRY) { + //use the general setting + prefer = catalog.isPreferPublic() ? + CatalogFeatures.PREFER_PUBLIC : CatalogFeatures.PREFER_SYSTEM; + } else { + //Group inherit from the catalog entry + prefer = catalogEntry.isPreferPublic() ? + CatalogFeatures.PREFER_PUBLIC : CatalogFeatures.PREFER_SYSTEM; + } + } + + if (type == CatalogEntryType.CATALOGENTRY) { + seenRoot = true; + if (catalog.isTop()) { + String defer = atts.getValue("defer"); + String resolve = atts.getValue("resolve"); + if (defer == null) { + defer = catalog.isDeferred() ? + CatalogFeatures.DEFER_TRUE : CatalogFeatures.DEFER_FALSE; + } + if (resolve == null) { + resolve = catalog.getResolve().literal; + } + catalog.setResolve(resolve); + catalogEntry = new CatalogEntry(base, prefer, defer, resolve); + } else { + catalogEntry = new CatalogEntry(base, prefer); + } + return; + } else { + inGroup = true; + group = new GroupEntry(catalog, base, prefer); + catalog.addEntry(group); + return; + } + } + + //parse entries other than the catalog and group entries + switch (type) { + case PUBLIC: + entry = new PublicEntry(base, atts.getValue("publicId"), atts.getValue("uri")); + break; + case SYSTEM: + entry = new SystemEntry(base, atts.getValue("systemId"), atts.getValue("uri")); + break; + case REWRITESYSTEM: + entry = new RewriteSystem(base, atts.getValue("systemIdStartString"), atts.getValue("rewritePrefix")); + break; + case SYSTEMSUFFIX: + entry = new SystemSuffix(base, atts.getValue("systemIdSuffix"), atts.getValue("uri")); + break; + case DELEGATEPUBLIC: + entry = new DelegatePublic(base, atts.getValue("publicIdStartString"), atts.getValue("catalog")); + break; + case DELEGATESYSTEM: + entry = new DelegateSystem(base, atts.getValue("systemIdStartString"), atts.getValue("catalog")); + break; + case URI: + entry = new UriEntry(base, atts.getValue("name"), atts.getValue("uri")); + break; + case REWRITEURI: + entry = new RewriteUri(base, atts.getValue("uriStartString"), atts.getValue("rewritePrefix")); + break; + case URISUFFIX: + entry = new UriSuffix(base, atts.getValue("uriSuffix"), atts.getValue("uri")); + break; + case DELEGATEURI: + entry = new DelegateUri(base, atts.getValue("uriStartString"), atts.getValue("catalog")); + break; + case NEXTCATALOG: + entry = new NextCatalog(base, atts.getValue("catalog")); + break; + } + + if (type == CatalogEntryType.NEXTCATALOG) { + catalog.addNextCatalog((NextCatalog) entry); + } else if (inGroup) { + group.addEntry(entry); + } else { + catalog.addEntry(entry); + } + + } + + /** + * Handles endElement event + */ + @Override + public void endElement(String namespaceURI, String localName, String qName) + throws SAXException { + if (ignoreTheCatalog) return; + + CatalogEntryType type = CatalogEntryType.getType(localName); + if (type == CatalogEntryType.GROUP) { + inGroup = false; + } else if (type == CatalogEntryType.CATALOGENTRY) { + /* + Done reading the catalog file. + Load delegate and alternative catalogs if defer is false. + */ + if (!catalog.isDeferred()) { + catalog.loadDelegateCatalogs(); + catalog.loadNextCatalogs(); + } + } + } + + + /** + * Skips external DTD since resolving external DTD is not required + * by the specification. + */ + @Override + public InputSource resolveEntity(String publicId, String systemId) { + return new InputSource(new StringReader("")); + } + + /** + * Skips external references since resolution is not required + * by the specification. + */ + @Override + public Source resolve(String href, String base) + throws TransformerException { + return new SAXSource(new InputSource(new StringReader(""))); + } + +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogResolver.java 2015-10-26 15:21:50.810925171 -0700 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; + +/** + * A SAX EntityResolver that uses catalogs to resolve references. + * + * @since 9 + */ +public interface CatalogResolver extends EntityResolver { + + /** + * The method searches through the catalog entries in the main and + * alternative catalogs to attempt to find a match with the specified publicId + * or systemId. + *

+ * For resolving external entities, system entries will be matched before + * the public entries. + *

+ * The {@code prefer} attribute: if the {@code prefer} is public, + * and there is no match found through the system entries, public entries + * will be considered. If it is not specified, the {@code prefer} is public + * by default (Note that by the OASIS standard, system entries will always + * be considered first when the external system identifier is specified. + * Prefer public means that public entries will be matched when both system + * and public identifiers are specified. In general therefore, prefer + * public is recommended.) + * + * @param publicId the public identifier of the external entity being + * referenced, or null if none was supplied + * + * @param systemId the system identifier of the external entity being + * referenced. A system identifier is required on all external entities. XML + * requires a system identifier on all external entities, so this value is + * always specified. + * + * @return a {@link org.xml.sax.InputSource} object if a mapping is found. If no mapping is + * found, returns a {@link org.xml.sax.InputSource} object containing an empty + * {@link java.io.Reader} if the {@code javax.xml.catalog.resolve} property + * is set to {@code ignore}; returns null if the + * {@code javax.xml.catalog.resolve} property is set to {@code continue}. + * + * @throws CatalogException if no mapping is found and + * {@code javax.xml.catalog.resolve} is specified as strict + */ + @Override + public InputSource resolveEntity(String publicId, String systemId); +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogResolverImpl.java 2015-10-26 15:21:51.134941252 -0700 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.io.StringReader; +import java.util.Iterator; +import org.xml.sax.InputSource; + +/** + * A SAX EntityResolver/JAXP URIResolver that uses catalogs. + * + *

+ * This class implements both a SAX EntityResolver and a JAXP URIResolver. + * + * + * @since 9 + */ +final class CatalogResolverImpl implements CatalogResolver { + Catalog catalog; + + /** + * Construct an instance of the CatalogResolver from a Catalog. + * + * @param catalog A Catalog. + */ + public CatalogResolverImpl(Catalog catalog) { + this.catalog = catalog; + } + + @Override + public InputSource resolveEntity(String publicId, String systemId) { + //Normalize publicId and systemId + systemId = Normalizer.normalizeURI(systemId); + publicId = Normalizer.normalizePublicId(Normalizer.decodeURN(publicId)); + + //check whether systemId is an urn + if (systemId != null && systemId.startsWith("urn:publicid:")) { + systemId = Normalizer.decodeURN(systemId); + if (publicId != null && !publicId.equals(systemId)) { + systemId = null; + } else { + publicId = systemId; + systemId = null; + } + } + + CatalogImpl c = (CatalogImpl)catalog; + String resolvedSystemId = resolve(c, publicId, systemId); + + if (resolvedSystemId != null) { + return new InputSource(resolvedSystemId); + } + + GroupEntry.ResolveType resolveType = ((CatalogImpl) catalog).getResolve(); + switch (resolveType) { + case IGNORE: + return new InputSource(new StringReader("")); + case STRICT: + CatalogMessages.reportError(CatalogMessages.ERR_NO_MATCH, + new Object[]{publicId, systemId}); + } + + //no action, allow the parser to continue + return null; + } + + /** + * Resolves the publicId or systemId to one specified in the catalog. + * @param catalog the catalog + * @param publicId the publicId + * @param systemId the systemId + * @return the resolved systemId if a match is found, null otherwise + */ + String resolve(CatalogImpl catalog, String publicId, String systemId) { + String resolvedSystemId = null; + + //search the current catalog + catalog.reset(); + if (systemId != null) { + resolvedSystemId = catalog.matchSystem(systemId); + } + if (resolvedSystemId == null) { + resolvedSystemId = catalog.matchPublic(publicId); + } + + //mark the catalog as having been searched before trying alternatives + catalog.markAsSearched(); + + //search alternative catalogs + if (resolvedSystemId == null) { + Iterator iter = catalog.catalogs().iterator(); + while (iter.hasNext()) { + resolvedSystemId = resolve((CatalogImpl)iter.next(), publicId, systemId); + if (resolvedSystemId != null) { + break; + } + + } + } + + return resolvedSystemId; + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogUriResolver.java 2015-10-26 15:21:51.458957333 -0700 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import javax.xml.transform.Source; +import javax.xml.transform.URIResolver; + +/** + * A JAXP URIResolver that uses catalogs to resolve references. + * + * @since 9 + */ +public interface CatalogUriResolver extends URIResolver { + + /** + * The method searches through the catalog entries in the main and + * alternative catalogs to attempt to find a match with the specified URI. + * + * @param href an href attribute, which may be relative or absolute + * @param base The base URI against which the href attribute will be made + * absolute if the absolute URI is required + * + * @return a {@link javax.xml.transform.Source} object if a mapping is found. + * If no mapping is found, returns a {@link javax.xml.transform.Source} object + * containing an empty {@link java.io.Reader} if the + * {@code javax.xml.catalog.resolve} property is set to {@code ignore}; + * returns a {@link javax.xml.transform.Source} object with the original URI + * (href, or href resolved with base if base is not null) if the + * {@code javax.xml.catalog.resolve} property is set to {@code continue}. + * + * @throws CatalogException if no mapping is found and + * {@code javax.xml.catalog.resolve} is specified as strict + */ + @Override + public Source resolve(String href, String base); +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogUriResolverImpl.java 2015-10-26 15:21:51.790973812 -0700 @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl; +import java.io.StringReader; +import java.net.URL; +import java.util.Iterator; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.Source; +import javax.xml.transform.sax.SAXSource; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +/** + * A SAX EntityResolver/JAXP URIResolver that uses catalogs. + *

+ * This class implements both a SAX EntityResolver and a JAXP URIResolver. + * + * + * @since 9 + */ +final class CatalogUriResolverImpl implements CatalogUriResolver { + + Catalog catalog; + CatalogResolverImpl entityResolver; + + /** + * Construct an instance of the CatalogResolver from a Catalog. + * + * @param catalog A Catalog. + */ + public CatalogUriResolverImpl(Catalog catalog) { + this.catalog = catalog; + } + + @Override + public Source resolve(String href, String base) { + if (href == null) return null; + + CatalogImpl c = (CatalogImpl)catalog; + String uri = Normalizer.normalizeURI(href); + String result; + + //remove fragment if any. + int hashPos = uri.indexOf("#"); + if (hashPos >= 0) { + uri = uri.substring(0, hashPos); + } + + //search the current catalog + result = resolve(c, uri); + if (result == null) { + GroupEntry.ResolveType resolveType = c.getResolve(); + switch (resolveType) { + case IGNORE: + return new SAXSource(new InputSource(new StringReader(""))); + case STRICT: + CatalogMessages.reportError(CatalogMessages.ERR_NO_URI_MATCH, + new Object[]{href, base}); + } + try { + URL url = null; + + if (base == null) { + url = new URL(uri); + result = url.toString(); + } else { + URL baseURL = new URL(base); + url = (href.length() == 0 ? baseURL : new URL(baseURL, uri)); + result = url.toString(); + } + } catch (java.net.MalformedURLException mue) { + CatalogMessages.reportError(CatalogMessages.ERR_CREATING_URI, + new Object[]{href, base}); + } + } + + SAXSource source = new SAXSource(); + source.setInputSource(new InputSource(result)); + setEntityResolver(source); + return source; + } + + /** + * Resolves the publicId or systemId to one specified in the catalog. + * @param catalog the catalog + * @param href an href attribute, which may be relative or absolute + * @return the resolved systemId if a match is found, null otherwise + */ + String resolve(CatalogImpl catalog, String href) { + String result = null; + + //search the current catalog + catalog.reset(); + if (href != null) { + result = catalog.matchURI(href); + } + + //mark the catalog as having been searched before trying alternatives + catalog.markAsSearched(); + + //search alternative catalogs + if (result == null) { + Iterator iter = catalog.catalogs().iterator(); + while (iter.hasNext()) { + result = resolve((CatalogImpl)iter.next(), href); + if (result != null) { + break; + } + } + } + + return result; + } + + /** + * Establish an entityResolver for newly resolved URIs. + *

+ * This is called from the URIResolver to set an EntityResolver on the SAX + * parser to be used for new XML documents that are encountered as a result + * of the document() function, xsl:import, or xsl:include. This is done + * because the XSLT processor calls out to the SAXParserFactory itself to + * create a new SAXParser to parse the new document. The new parser does not + * automatically inherit the EntityResolver of the original (although + * arguably it should). Quote from JAXP specification on Class + * SAXTransformerFactory: + *

+ * {@code If an application wants to set the ErrorHandler or EntityResolver + * for an XMLReader used during a transformation, it should use a URIResolver + * to return the SAXSource which provides (with getXMLReader) a reference to + * the XMLReader} + * + */ + private void setEntityResolver(SAXSource source) { + XMLReader reader = source.getXMLReader(); + if (reader == null) { + SAXParserFactory spFactory = new SAXParserFactoryImpl(); + spFactory.setNamespaceAware(true); + try { + reader = spFactory.newSAXParser().getXMLReader(); + } catch (ParserConfigurationException | SAXException ex) { + CatalogMessages.reportRunTimeError(CatalogMessages.ERR_PARSER_CONF, ex); + } + } + if (entityResolver != null) { + entityResolver = new CatalogResolverImpl(catalog); + } + reader.setEntityResolver(entityResolver); + source.setXMLReader(reader); + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/DelegatePublic.java 2015-10-26 15:21:52.118990092 -0700 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.net.URI; + +/** + * Represents a delegatePublic entry. + * + * @since 9 + */ +final class DelegatePublic extends AltCatalog { + String publicIdStartString; + + /** + * Construct a delegatePublic entry. + * @param startString The publicIdStartString attribute. + * @param catalog The catalog attribute. + */ + public DelegatePublic(String base, String startString, String catalog) { + super(CatalogEntryType.DELEGATEPUBLIC, base); + setPublicIdStartString(startString); + setCatalog(catalog); + } + + /** + * Set the publicIdStartString attribute. + * @param startString The publicIdStartString attribute value. + */ + public void setPublicIdStartString (String startString) { + CatalogMessages.reportNPEOnNull("publicIdStartString", startString); + this.publicIdStartString = Normalizer.normalizePublicId(startString); + setMatchId(publicIdStartString); + } + + /** + * Get the publicIdStartString attribute. + * @return The publicIdStartString + */ + public String getPublicIdStartString () { + return publicIdStartString; + } + + /** + * Try to match the specified publicId with the entry. + * + * @param publicId The publicId to be matched. + * @return The URI of the catalog. + */ + @Override + public String match(String publicId) { + return match(publicId, 0); + } + + /** + * Try to match the specified publicId with the entry. Return the match if it + * is successful and the length of the publicIdStartString is longer than the + * longest of any previous match. + * + * @param publicId The publicId to be matched. + * @param currentMatch The length of publicIdStartString of previous match if any. + * @return The replacement URI if the match is successful, null if not. + */ + @Override + public URI matchURI(String publicId, int currentMatch) { + if (publicIdStartString.length() <= publicId.length() && + publicIdStartString.equals(publicId.substring(0, publicIdStartString.length()))) { + if (currentMatch < publicIdStartString.length()) { + return catalogURI; + } + } + return null; + } + +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/DelegateSystem.java 2015-10-26 15:21:52.443006173 -0700 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.net.URI; + +/** + * Represents a delegateSystem entry. + * + * @since 9 + */ +final class DelegateSystem extends AltCatalog { + String systemIdStartString; + + /** + * Construct a delegateSystem entry. + * @param systemIdStartString The systemIdStartString attribute. + * @param catalog The catalog attribute. + */ + public DelegateSystem(String base, String systemIdStartString, String catalog) { + super(CatalogEntryType.DELEGATESYSTEM, base); + setSystemIdStartString(systemIdStartString); + setCatalog(catalog); + } + + /** + * Set the systemIdStartString attribute. + * @param systemIdStartString The systemIdStartString attribute value. + */ + public void setSystemIdStartString (String systemIdStartString) { + CatalogMessages.reportNPEOnNull("systemIdStartString", systemIdStartString); + this.systemIdStartString = Normalizer.normalizeURI(systemIdStartString); + setMatchId(this.systemIdStartString); + } + + /** + * Get the systemIdStartString attribute. + * @return The systemIdStartString + */ + public String getSystemIdStartString () { + return systemIdStartString; + } + + /** + * Try to match the specified systemId with the entry. + * + * @param systemId The systemId to be matched. + * @return The URI of the catalog. + */ + @Override + public String match(String systemId) { + return match(systemId, 0); + } + + /** + * Matches the specified publicId with the entry. Return the match if it + * is successful and the length of the systemIdStartString is longer than the + * longest of any previous match. + * + * @param systemId The systemId to be matched. + * @param currentMatch The length of systemIdStartString of previous match if any. + * @return The replacement URI if the match is successful, null if not. + */ + @Override + public URI matchURI(String systemId, int currentMatch) { + if (systemIdStartString.length() <= systemId.length() && + systemIdStartString.equals(systemId.substring(0, systemIdStartString.length()))) { + if (currentMatch < systemIdStartString.length()) { + return catalogURI; + } + } + return null; + } + +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/DelegateUri.java 2015-10-26 15:21:52.767022254 -0700 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.net.URI; + +/** + * Represents a delegateURI entry. + * + * @since 9 + */ +final class DelegateUri extends AltCatalog { + String uriStartString; + + /** + * Construct a delegateURI entry. + * @param uriStartString The uriStartString attribute. + * @param catalog The catalog attribute. + */ + public DelegateUri(String base, String uriStartString, String catalog) { + super(CatalogEntryType.DELEGATEURI, base); + setURIStartString (uriStartString); + setCatalog(catalog); + } + + /** + * Set the uriStartString attribute. + * + * @param uriStartString The uriStartString attribute value. + */ + public void setURIStartString (String uriStartString) { + CatalogMessages.reportNPEOnNull("uriStartString", uriStartString); + this.uriStartString = Normalizer.normalizeURI(uriStartString); + setMatchId(this.uriStartString); + } + + /** + * Get the uriStartString attribute. + * @return The uriStartString + */ + public String getURIStartString () { + return uriStartString; + } + + /** + * Try to match the specified systemId with the entry. + * + * @param systemId The systemId to be matched. + * @return The URI of the catalog. + */ + @Override + public String match(String systemId) { + return match(systemId, 0); + } + + /** + * Matches the specified systemId with the entry. Return the match if it + * is successful and the length of the uriStartString is longer than the + * longest of any previous match. + * + * @param systemId The systemId to be matched. + * @param currentMatch The length of uriStartString of previous match if any. + * @return The replacement URI if the match is successful, null if not. + */ + @Override + public URI matchURI(String systemId, int currentMatch) { + if (uriStartString.length() <= systemId.length() && + uriStartString.equals(systemId.substring(0, uriStartString.length()))) { + if (currentMatch < uriStartString.length()) { + return catalogURI; + } + } + return null; + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/GroupEntry.java 2015-10-26 15:21:53.095038534 -0700 @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Represents a group entry. + * + * @since 9 + */ +class GroupEntry extends BaseEntry { + static final int ATTRIBUTE_PREFER = 0; + static final int ATTRIBUTE_DEFFER = 1; + static final int ATTRIBUTE_RESOLUTION = 2; + + //Unmodifiable features when the Catalog is created + CatalogFeatures features; + + //Value of the prefer attribute + boolean isPreferPublic = true; + + //The catalog instance this group belongs to + CatalogImpl catalog; + + //A list of all entries in a catalog or group + List entries = new ArrayList<>(); + + //loaded delegated catalog by system id + Map delegateCatalogs = new HashMap<>(); + + //A list of all loaded Catalogs, including this, and next catalogs + Map loadedCatalogs = new HashMap<>(); + + /* + A list of Catalog Ids that have already been searched in a matching + operation. Check this list before constructing new Catalog to avoid circular + reference. + */ + List catalogsSearched = new ArrayList<>(); + + //A flag to indicate whether the current match is a system or uri + boolean isInstantMatch = false; + + //A match of a rewrite type + String rewriteMatch = null; + + //The length of the longest match of a rewrite type + int longestRewriteMatch = 0; + + //A match of a suffix type + String suffixMatch = null; + + //The length of the longest match of a suffix type + int longestSuffixMatch = 0; + + /** + * PreferType represents possible values of the prefer property + */ + public static enum PreferType { + PUBLIC("public"), + SYSTEM("system"); + + final String literal; + + PreferType(String literal) { + this.literal = literal; + } + + public boolean prefer(String prefer) { + return literal.equals(prefer); + } + } + + /** + * PreferType represents possible values of the resolve property + */ + public static enum ResolveType { + STRICT(CatalogFeatures.RESOLVE_STRICT), + CONTINUE(CatalogFeatures.RESOLVE_CONTINUE), + IGNORE(CatalogFeatures.RESOLVE_IGNORE); + + final String literal; + + ResolveType(String literal) { + this.literal = literal; + } + + static public ResolveType getType(String resolveType) { + for (ResolveType type : ResolveType.values()) { + if (type.isType(resolveType)) { + return type; + } + } + return null; + } + + public boolean isType(String type) { + return literal.equals(type); + } + } + + /** + * Constructs a GroupEntry + * + * @param type The type of the entry + */ + public GroupEntry(CatalogEntryType type) { + super(type); + } + + /** + * Constructs a group entry. + * + * @param base The baseURI attribute + * @param attributes The attributes + */ + public GroupEntry(String base, String... attributes) { + this(null, base, attributes); + } + + /** + * Resets the group entry to its initial state. + */ + public void reset() { + isInstantMatch = false; + rewriteMatch = null; + longestRewriteMatch = 0; + suffixMatch = null; + longestSuffixMatch = 0; + } + /** + * Constructs a group entry. + * @param catalog The parent catalog + * @param base The baseURI attribute + * @param attributes The attributes + */ + public GroupEntry(CatalogImpl catalog, String base, String... attributes) { + super(CatalogEntryType.GROUP, base); + setPrefer(attributes[ATTRIBUTE_PREFER]); + this.catalog = catalog; + } + + /** + * Adds an entry. + * + * @param entry The entry to be added. + */ + public void addEntry(BaseEntry entry) { + entries.add(entry); + } + + /** + * Sets the prefer property. If the value is null or empty, or any String + * other than the defined, it will be assumed as the default value. + * + * @param value The value of the prefer attribute + */ + public final void setPrefer(String value) { + isPreferPublic = PreferType.PUBLIC.prefer(value); + } + + /** + * Queries the prefer attribute + * + * @return true if the prefer attribute is set to system, false if not. + */ + public boolean isPreferPublic() { + return isPreferPublic; + } + + /** + * Attempt to find a matching entry in the catalog by systemId. + * + *

+ * The method searches through the system-type entries, including system, + * rewriteSystem, systemSuffix, delegateSystem, and group entries in the + * current catalog in order to find a match. + * + * + * @param systemId The system identifier of the external entity being + * referenced. + * + * @return An URI string if a mapping is found, or null otherwise. + */ + public String matchSystem(String systemId) { + String match = null; + for (BaseEntry entry : entries) { + switch (entry.type) { + case SYSTEM: + match = ((SystemEntry) entry).match(systemId); + //if there's a matching system entry, use it + if (match != null) { + isInstantMatch = true; + return match; + } + break; + case REWRITESYSTEM: + match = ((RewriteSystem) entry).match(systemId, longestRewriteMatch); + if (match != null) { + rewriteMatch = match; + longestRewriteMatch = ((RewriteSystem) entry).getSystemIdStartString().length(); + } + break; + case SYSTEMSUFFIX: + match = ((SystemSuffix) entry).match(systemId, longestSuffixMatch); + if (match != null) { + suffixMatch = match; + longestSuffixMatch = ((SystemSuffix) entry).getSystemIdSuffix().length(); + } + break; + case GROUP: + GroupEntry grpEntry = (GroupEntry) entry; + match = grpEntry.matchSystem(systemId); + if (grpEntry.isInstantMatch) { + //use it if there is a match of the system type + return match; + } else if (grpEntry.longestRewriteMatch > longestRewriteMatch) { + rewriteMatch = match; + } else if (grpEntry.longestSuffixMatch > longestSuffixMatch) { + suffixMatch = match; + } + break; + } + } + + if (longestRewriteMatch > 0) { + return rewriteMatch; + } else if (longestSuffixMatch > 0) { + return suffixMatch; + } + + //if no single match is found, try delegates + return matchDelegate(CatalogEntryType.DELEGATESYSTEM, systemId); + } + + /** + * Attempt to find a matching entry in the catalog by publicId. + * + *

+ * The method searches through the public-type entries, including public, + * delegatePublic, and group entries in the current catalog in order to find + * a match. + * + * + * @param publicId The public identifier of the external entity being + * referenced. + * + * @return An URI string if a mapping is found, or null otherwise. + */ + public String matchPublic(String publicId) { + //as the specification required + if (!isPreferPublic) { + return null; + } + + //match public entries + String match = null; + for (BaseEntry entry : entries) { + switch (entry.type) { + case PUBLIC: + match = ((PublicEntry) entry).match(publicId); + break; + case GROUP: + match = ((GroupEntry) entry).matchPublic(publicId); + break; + } + if (match != null) { + return match; + } + } + + //if no single match is found, try delegates + return matchDelegate(CatalogEntryType.DELEGATEPUBLIC, publicId); + } + + /** + * Attempt to find a matching entry in the catalog by the uri element. + * + *

+ * The method searches through the uri-type entries, including uri, + * rewriteURI, uriSuffix, delegateURI and group entries in the current + * catalog in order to find a match. + * + * + * @param uri The URI reference of a resource. + * + * @return An URI string if a mapping is found, or null otherwise. + */ + public String matchURI(String uri) { + String match = null; + for (BaseEntry entry : entries) { + switch (entry.type) { + case URI: + match = ((UriEntry) entry).match(uri); + if (match != null) { + isInstantMatch = true; + return match; + } + break; + case REWRITEURI: + match = ((RewriteUri) entry).match(uri, longestRewriteMatch); + if (match != null) { + rewriteMatch = match; + longestRewriteMatch = ((RewriteUri) entry).getURIStartString().length(); + } + break; + case URISUFFIX: + match = ((UriSuffix) entry).match(uri, longestSuffixMatch); + if (match != null) { + suffixMatch = match; + longestSuffixMatch = ((UriSuffix) entry).getURISuffix().length(); + } + break; + case GROUP: + GroupEntry grpEntry = (GroupEntry) entry; + match = grpEntry.matchURI(uri); + if (grpEntry.isInstantMatch) { + //use it if there is a match of the uri type + return match; + } else if (grpEntry.longestRewriteMatch > longestRewriteMatch) { + rewriteMatch = match; + } else if (grpEntry.longestSuffixMatch > longestSuffixMatch) { + suffixMatch = match; + } + break; + } + } + + if (longestRewriteMatch > 0) { + return rewriteMatch; + } else if (longestSuffixMatch > 0) { + return suffixMatch; + } + + //if no single match is found, try delegates + return matchDelegate(CatalogEntryType.DELEGATEURI, uri); + } + + /** + * Matches delegatePublic or delegateSystem against the specified id + * + * @param isSystem The flag to indicate whether the delegate is system or + * public + * @param id The system or public id to be matched + * @return The URI string if a mapping is found, or null otherwise. + */ + private String matchDelegate(CatalogEntryType type, String id) { + String match = null; + int longestMatch = 0; + URI catalogId = null; + URI temp; + + //Check delegate types in the current catalog + for (BaseEntry entry : entries) { + if (entry.type == type) { + if (type == CatalogEntryType.DELEGATESYSTEM) { + temp = ((DelegateSystem)entry).matchURI(id, longestMatch); + } else if (type == CatalogEntryType.DELEGATEPUBLIC) { + temp = ((DelegatePublic)entry).matchURI(id, longestMatch); + } else { + temp = ((DelegateUri)entry).matchURI(id, longestMatch); + } + if (temp != null) { + longestMatch = entry.getMatchId().length(); + catalogId = temp; + } + } + } + + //Check delegate Catalogs + if (catalogId != null) { + Catalog delegateCatalog = loadCatalog(catalogId); + + if (delegateCatalog != null) { + if (type == CatalogEntryType.DELEGATESYSTEM) { + match = delegateCatalog.matchSystem(id); + } else if (type == CatalogEntryType.DELEGATEPUBLIC) { + match = delegateCatalog.matchPublic(id); + } else { + match = delegateCatalog.matchURI(id); + } + } + } + + return match; + } + + /** + * Loads all delegate catalogs. + */ + void loadDelegateCatalogs() { + entries.stream() + .filter((entry) -> (entry.type == CatalogEntryType.DELEGATESYSTEM || + entry.type == CatalogEntryType.DELEGATEPUBLIC || + entry.type == CatalogEntryType.DELEGATEURI)) + .map((entry) -> (AltCatalog)entry) + .forEach((altCatalog) -> { + loadCatalog(altCatalog.getCatalogURI()); + }); + } + + /** + * Loads a delegate catalog by the catalogId specified. + * @param catalogId the catalog Id + */ + Catalog loadCatalog(URI catalogURI) { + Catalog delegateCatalog = null; + if (catalogURI != null) { + String catalogId = catalogURI.toASCIIString(); + delegateCatalog = getLoadedCatalog(catalogId); + if (delegateCatalog == null) { + if (verifyCatalogFile(catalogURI)) { + delegateCatalog = new CatalogImpl(catalog, features, catalogId); + delegateCatalogs.put(catalogId, delegateCatalog); + } + } + } + + return delegateCatalog; + } + + /** + * Returns a previously loaded Catalog object if found. + * + * @param catalogId The systemId of a catalog + * @return a Catalog object previously loaded, or null if none in the saved + * list + */ + Catalog getLoadedCatalog(String catalogId) { + Catalog c = null; + + //checl delegate Catalogs + c = delegateCatalogs.get(catalogId); + if (c == null) { + //check other loaded Catalogs + c = loadedCatalogs.get(catalogId); + } + + return c; + } + + + /** + * Verifies that the catalog file represented by the catalogId exists. If it + * doesn't, returns false to ignore it as specified in the Catalog + * specification, section 8. Resource Failures. + *

+ * Verifies that the catalog represented by the catalogId has not been + * searched or is not circularly referenced. + * + * @param catalogId The URI to a catalog + * @throws CatalogException if circular reference is found. + * @return true if the catalogId passed verification, false otherwise + */ + final boolean verifyCatalogFile(URI catalogURI) { + if (catalogURI == null) { + return false; + } + + //Ignore it if it doesn't exist + if (!Files.exists(Paths.get(catalogURI))) { + return false; + } + + String catalogId = catalogURI.toASCIIString(); + if (catalogsSearched.contains(catalogId)) { + CatalogMessages.reportRunTimeError(CatalogMessages.ERR_CIRCULAR_REFERENCE, + new Object[]{CatalogMessages.sanitize(catalogId)}); + } + + return true; + } + +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/NextCatalog.java 2015-10-26 15:21:53.427055012 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +/** + * Represents a nextCatalog entry. + * + * @since 9 + */ +final class NextCatalog extends AltCatalog { + + /** + * Construct a nextCatalog entry. + * @param catalog The catalog attribute. + */ + public NextCatalog(String base, String catalog) { + super(CatalogEntryType.NEXTCATALOG, base); + setCatalog(catalog); + } + + @Override + public String match(String match) { + throw new UnsupportedOperationException("Not supported."); + } + +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/Normalizer.java 2015-10-26 15:21:53.751071094 -0700 @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; + +/** + * The Normalizer is responsible for normalizing Public and System Identifiers + * as specified in section 6.2, 6.3 and 6.4 of the specification + * * + * XML Catalogs, OASIS Standard V1.1, 7 October 2005. + * + * @since 9 + */ +class Normalizer { + + /** + * Normalize a public identifier in accordance with section 6.2 of the + * Catalog specification. + * + *

+ * All strings of white space in public identifiers must be normalized to + * single space characters (#x20), and leading and trailing white space must + * be removed. + * + * @param publicId The unnormalized public identifier + * + * @return The normalized identifier + */ + static String normalizePublicId(String publicId) { + if (publicId == null) return null; + + StringBuilder sb = new StringBuilder(publicId.length()); + char last = 'a'; + for (char c : publicId.toCharArray()) { + //skip beginning and duplicate space + if ((c == ' ') && (sb.length() == 0 || last == ' ')) { + continue; + } + + //replace whitespace with space + if (c == '\t' || c == '\r' || c == '\n') { + if (last != ' ') { + sb.append(' '); + last = ' '; + } + } else { + sb.append(c); + last = c; + } + } + //remove the last space + if (last == ' ') { + sb.deleteCharAt(sb.length() - 1); + } + + return sb.toString(); + } + + /** + * Encode a public identifier as a "publicid" URN. + * + * @param publicId The unnormalized public identifier + * + * @return The normalized identifier + * @throws CatalogException if encoding failed + */ + static String encodeURN(String publicId) { + String urn = normalizePublicId(publicId); + + try { + urn = URLEncoder.encode(urn, "UTF-8"); + urn = urn.replace("::", ";"); + urn = urn.replace("//", ":"); + } catch (UnsupportedEncodingException ex) { + CatalogMessages.reportRunTimeError(CatalogMessages.ERR_OTHER, ex); + } + return "urn:publicid:" + urn; + } + + /** + * Decode a "publicid" URN into a public identifier. + * + * @param urn The urn:publicid: URN + * + * @return The normalized identifier + * @throws CatalogException if decoding failed + */ + static String decodeURN(String urn) { + String publicId; + + if (urn != null && urn.startsWith("urn:publicid:")) { + publicId = urn.substring(13); + } else { + return urn; + } + try { + publicId = publicId.replace(":", "//"); + publicId = publicId.replace(";", "::"); + publicId = URLDecoder.decode(publicId, "UTF-8"); + } catch (UnsupportedEncodingException ex) { + CatalogMessages.reportRunTimeError(CatalogMessages.ERR_OTHER, ex); + } + + return publicId; + } + + /** + * Perform character normalization on a URI reference. + * + * @param uriref The URI reference + * @return The normalized URI reference + */ + static String normalizeURI(String uriref) { + if (uriref == null) { + return null; + } + + byte[] bytes; + uriref = uriref.trim(); + try { + bytes = uriref.getBytes("UTF-8"); + } catch (UnsupportedEncodingException uee) { + // this can't happen + return uriref; + } + + StringBuilder newRef = new StringBuilder(bytes.length); + for (int count = 0; count < bytes.length; count++) { + int ch = bytes[count] & 0xFF; + + if ((ch <= 0x20) // ctrl + || (ch > 0x7F) // high ascii + || (ch == 0x22) // " + || (ch == 0x3C) // < + || (ch == 0x3E) // > + || (ch == 0x5C) // \ + || (ch == 0x5E) // ^ + || (ch == 0x60) // ` + || (ch == 0x7B) // { + || (ch == 0x7C) // | + || (ch == 0x7D) // } + || (ch == 0x7F)) { + newRef.append("%").append(String.format("%02X", ch)); + } else { + newRef.append((char) bytes[count]); + } + } + + return newRef.toString().trim(); + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/PublicEntry.java 2015-10-26 15:21:54.095088168 -0700 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.net.URL; + +/** + * Represents a public entry. + * + * @since 9 + */ +final class PublicEntry extends BaseEntry { + String publicId; + URL uri; + + /** + * Construct a public entry. + * @param publicId The publicId attribute. + * @param uri The uri attribute. + */ + public PublicEntry(String base, String publicId, String uri) { + super(CatalogEntryType.PUBLIC, base); + setPublicId(publicId); + setURI(uri); + } + + /** + * Set the publicId attribute. + * @param publicId The publicId attribute value. + */ + public void setPublicId(String publicId) { + CatalogMessages.reportNPEOnNull("publicId", publicId); + this.publicId = Normalizer.normalizePublicId(publicId); + } + + /** + * Set the uri attribute. If the value of the uri attribute is relative, it + * must be made absolute with respect to the base URI currently in effect. + * The URI reference should not include a fragment identifier. + * @param uri The uri attribute value. + */ + public void setURI(String uri) { + this.uri = verifyURI("uri", baseURI, uri); + } + + /** + * Get the publicId attribute. + * @return The publicId + */ + public String getPublicId() { + return publicId; + } + /** + * Get the uri attribute. + * @return The uri attribute value. + */ + public URL getURI() { + return uri; + } + + @Override + public String match(String publicId) { + if (this.publicId.equals(publicId)) { + return uri.toString(); + } + return null; + } + +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/RewriteSystem.java 2015-10-26 15:21:54.419104249 -0700 @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.net.URL; + +/** + * Represents a rewriteSystem entry. + * + * @since 9 + */ +final class RewriteSystem extends BaseEntry { + String systemIdStartString; + URL rewritePrefix; + + /** + * Construct a rewriteSystem entry. + * + * @param systemIdStartString The systemIdStartString attribute. + * @param rewritePrefix The rewritePrefix attribute. + */ + public RewriteSystem(String base, String systemIdStartString, String rewritePrefix) { + super(CatalogEntryType.REWRITESYSTEM, base); + setSystemIdStartString (systemIdStartString); + setRewritePrefix(rewritePrefix); + } + + /** + * Set the systemIdStartString attribute. + * @param systemIdStartString The systemIdStartString attribute value. + */ + public void setSystemIdStartString (String systemIdStartString) { + CatalogMessages.reportNPEOnNull("systemIdStartString", systemIdStartString); + this.systemIdStartString = Normalizer.normalizeURI(systemIdStartString); + } + + /** + * Set the rewritePrefix attribute. If the value of the rewritePrefix attribute + * is relative, it must be made absolute with respect to the base URI currently in effect. + * @param rewritePrefix The rewritePrefix attribute value. + */ + public void setRewritePrefix(String rewritePrefix) { + this.rewritePrefix = verifyURI("setRewritePrefix", baseURI, rewritePrefix); + } + + /** + * Get the systemIdStartString attribute. + * @return The systemIdStartString + */ + public String getSystemIdStartString () { + return systemIdStartString; + } + /** + * Get the rewritePrefix attribute. + * @return The rewritePrefix attribute value. + */ + public URL getRewritePrefix() { + return rewritePrefix; + } + + + /** + * Try to match the specified systemId with the entry. Return the match if it + * is successful and the length of the systemIdStartString is longer than the + * longest of any previous match. + * + * @param systemId The systemId to be matched. + * @param currentMatch The length of systemIdStartString of previous match if any. + * @return The replacement URI if the match is successful, null if not. + */ + public String match(String systemId, int currentMatch) { + if (systemIdStartString.length() <= systemId.length() && + systemIdStartString.equals(systemId.substring(0, systemIdStartString.length()))) { + if (currentMatch < systemIdStartString.length()) { + String prefix = rewritePrefix.toExternalForm(); + if (!prefix.endsWith(SLASH) && !systemId.startsWith(SLASH)) { + return prefix + SLASH + systemId.substring(systemIdStartString.length()); + } else { + return prefix + systemId.substring(systemIdStartString.length()); + } + } + } + return null; + } + + /** + * Try to match the specified systemId with the entry. + * + * @param systemId The systemId to be matched. + * @return The replacement URI if the match is successful, null if not. + */ + @Override + public String match(String systemId) { + return match(systemId, 0); + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/RewriteUri.java 2015-10-26 15:21:54.747120529 -0700 @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.net.URL; + +/** + * Represents a rewriteURI entry. + * + * @since 9 + */ +final class RewriteUri extends BaseEntry { + String uriStartString; + URL rewritePrefix; + + /** + * Construct a rewriteURI entry. + * @param uriStartString The uriStartString attribute. + * @param rewritePrefix The rewritePrefix attribute. + */ + public RewriteUri(String base, String uriStartString, String rewritePrefix) { + super(CatalogEntryType.REWRITEURI, base); + setURIStartString (uriStartString); + setRewritePrefix(rewritePrefix); + } + + /** + * Set the uriStartString attribute. + * @param uriStartString The uriStartString attribute value. + */ + public void setURIStartString (String uriStartString) { + CatalogMessages.reportNPEOnNull("uriStartString", uriStartString); + this.uriStartString = Normalizer.normalizeURI(uriStartString); + } + + /** + * Set the rewritePrefix attribute. If the value of the rewritePrefix attribute + * is relative, it must be made absolute with respect to the base URI currently in effect. + * + * @param rewritePrefix The rewritePrefix attribute value. + */ + public void setRewritePrefix(String rewritePrefix) { + this.rewritePrefix = verifyURI("setRewritePrefix", baseURI, rewritePrefix); + } + + /** + * Get the uriStartString attribute. + * @return The uriStartString + */ + public String getURIStartString () { + return uriStartString; + } + /** + * Get the rewritePrefix attribute. + * @return The rewritePrefix attribute value. + */ + public URL getRewritePrefix() { + return rewritePrefix; + } + + /** + * Try to match the specified systemId with the entry. Return the match if it + * is successful and the length of the systemIdStartString is longer than the + * longest of any previous match. + * + * @param systemId The systemId to be matched. + * @param currentMatch The length of uriStartString of previous match if any. + * @return The replacement URI if the match is successful, null if not. + */ + @Override + public String match(String systemId, int currentMatch) { + if (uriStartString.length() <= systemId.length() && + uriStartString.equals(systemId.substring(0, uriStartString.length()))) { + if (currentMatch < uriStartString.length()) { + String prefix = rewritePrefix.toExternalForm(); + if (!prefix.endsWith(SLASH) && !systemId.startsWith(SLASH)) { + return prefix + SLASH + systemId.substring(uriStartString.length()); + } else { + return prefix + systemId.substring(uriStartString.length()); + } + } + } + return null; + } + + @Override + public String match(String systemId) { + return match(systemId, 0); + } + +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/SystemEntry.java 2015-10-26 15:21:55.075136809 -0700 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.net.URL; + +/** + * Represents a system entry. + * + * @since 9 + */ +final class SystemEntry extends BaseEntry { + String systemId; + URL uri; + + /** + * Construct a system entry. + * + * @param systemId The systemId attribute. + * @param uri The uri attribute. + */ + public SystemEntry(String base, String systemId, String uri) { + super(CatalogEntryType.SYSTEM, base); + setSystemId(systemId); + setURI(uri); + } + + /** + * Set the systemId attribute. + * @param systemId The systemId attribute value. + */ + public void setSystemId(String systemId) { + CatalogMessages.reportNPEOnNull("systemId", systemId); + this.systemId = Normalizer.normalizeURI(systemId); + } + + /** + * Set the uri attribute. If the value of the uri attribute is relative, it + * must be made absolute with respect to the base URI currently in effect. + * The URI reference should not include a fragment identifier. + * @param uri The uri attribute value. + */ + public void setURI(String uri) { + this.uri = verifyURI("setURI", baseURI, uri); + } + + /** + * Get the systemId attribute. + * @return The systemId + */ + public String getSystemId() { + return systemId; + } + /** + * Get the uri attribute. + * @return The uri attribute value. + */ + public URL getURI() { + return uri; + } + + /** + * Try to match the specified string with the entry + * + * @param systemId The systemId to be matched + * @return The replacement URI if the match is successful, null if not. + */ + @Override + public String match(String systemId) { + if (this.systemId.equals(systemId)) { + return uri.toString(); + } + return null; + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/SystemSuffix.java 2015-10-26 15:21:55.403153089 -0700 @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.net.URL; + +/** + * Represents a systemSuffix entry. + * + * @since 9 + */ +final class SystemSuffix extends BaseEntry { + String systemIdSuffix; + URL uri; + + /** + * Construct a systemSuffix entry. + * @param systemIdSuffix The systemIdSuffix attribute. + * @param uri The uri attribute. + */ + public SystemSuffix(String base, String systemIdSuffix, String uri) { + super(CatalogEntryType.SYSTEMSUFFIX, base); + setSystemIdSuffix(systemIdSuffix); + setURI(uri); + } + + /** + * Set the systemIdSuffix attribute. + * @param systemIdSuffix The systemIdSuffix attribute value. + */ + public void setSystemIdSuffix(String systemIdSuffix) { + CatalogMessages.reportNPEOnNull("systemIdSuffix", systemIdSuffix); + this.systemIdSuffix = Normalizer.normalizeURI(systemIdSuffix); + } + + /** + * Set the uri attribute. If the value of the uri attribute is relative, it + * must be made absolute with respect to the base URI currently in effect. + * The URI reference should not include a fragment identifier. + * @param uri The uri attribute value. + */ + public void setURI(String uri) { + this.uri = verifyURI("setURI", baseURI, uri); + } + + /** + * Get the systemIdSuffix attribute. + * @return The systemIdSuffix + */ + public String getSystemIdSuffix () { + return systemIdSuffix; + } + /** + * Get the uri attribute. + * @return The uri attribute value. + */ + public URL getURI() { + return uri; + } + + /** + * Try to match the specified systemId with the entry. Return the match if it + * is successful and the length of the systemIdSuffix is longer than the longest + * of any previous match. + * + * @param systemId The systemId to be matched. + * @param currentMatch The length of systemIdSuffix of previous match if any. + * @return The replacement URI if the match is successful, null if not. + */ + @Override + public String match(String systemId, int currentMatch) { + if (systemId.endsWith(systemIdSuffix)) { + if (currentMatch < systemIdSuffix.length()) { + return uri.toString(); + } + } + return null; + } + + /** + * Try to match the specified systemId with the entry. + * + * @param systemId The systemId to be matched. + * @return The replacement URI if the match is successful, null if not. + */ + @Override + public String match(String systemId) { + return match(systemId, 0); + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/UriEntry.java 2015-10-26 15:21:55.731169369 -0700 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.net.URL; + +/** + * Represents an uriEntry entry. + * + * @since 9 + */ +final class UriEntry extends BaseEntry { + String name; + URL uri; + + /** + * Construct a group entry. + * @param name The name attribute. + * @param uri The uri attribute. + */ + public UriEntry(String base, String name, String uri) { + super(CatalogEntryType.URI, base); + setName(name); + setURI(uri); + } + + /** + * Set the name attribute. + * @param name The name attribute value. + */ + public void setName(String name) { + CatalogMessages.reportNPEOnNull("name", name); + this.name = Normalizer.normalizeURI(name); + } + + /** + * Set the uri attribute. If the value of the uri attribute is relative, it + * must be made absolute with respect to the base URI currently in effect. + * + * @param uri The uri attribute value. + */ + public void setURI(String uri) { + this.uri = verifyURI("setURI", baseURI, uri); + } + + /** + * Get the name attribute. + * @return The name + */ + public String getName() { + return name; + } + /** + * Get the uri attribute. + * @return The uri attribute value. + */ + public URL getURI() { + return uri; + } + + @Override + public String match(String name) { + if (this.name.equals(name)) { + return uri.toString(); + } + return null; + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/UriSuffix.java 2015-10-26 15:21:56.059185648 -0700 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import java.net.URL; + +/** + * Represents a uriSuffix entry. + * + * @since 9 + */ +final class UriSuffix extends BaseEntry { + String uriSuffix; + URL uri; + + /** + * Construct a group entry. + * @param uriSuffix The uriSuffix attribute. + * @param uri The uri attribute. + */ + public UriSuffix(String base, String uriSuffix, String uri) { + super(CatalogEntryType.URISUFFIX, base); + setURISuffix (uriSuffix); + setURI(uri); + } + + /** + * Set the uriSuffix attribute. + * @param uriSuffix The uriSuffix attribute value. + */ + public void setURISuffix(String uriSuffix) { + CatalogMessages.reportNPEOnNull("uriSuffix", uriSuffix); + this.uriSuffix = Normalizer.normalizeURI(uriSuffix); + } + + /** + * Set the uri attribute. If the value of the uri attribute is relative, it + * must be made absolute with respect to the base URI currently in effect. + * The URI reference should not include a fragment identifier. + * @param uri The uri attribute value. + */ + public void setURI(String uri) { + this.uri = verifyURI("setURI", baseURI, uri); + } + + /** + * Get the uriSuffix attribute. + * @return The uriSuffix + */ + public String getURISuffix () { + return uriSuffix; + } + /** + * Get the uri attribute. + * @return The uri attribute value. + */ + public String getURI() { + return uri.toString(); + } + + /** + * Try to match the specified systemId with the entry. Return the match if it + * is successful and the length of the uriSuffix is longer than the longest + * of any previous match. + * + * @param systemId The systemId to be matched. + * @param currentMatch The length of uriSuffix of previous match if any. + * @return The replacement URI if the match is successful, null if not. + */ + @Override + public String match(String systemId, int currentMatch) { + if (systemId.endsWith(uriSuffix)) { + if (currentMatch < uriSuffix.length()) { + return uri.toString(); + } + } + return null; + } + + @Override + public String match(String systemId) { + return match(systemId, 0); + } + +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/Util.java 2015-10-26 15:21:56.383201730 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.catalog; + +import jdk.xml.internal.SecuritySupport; + +/** + * + * @since 9 + */ +class Util { + /** + * Find catalog file paths by reading the system property, and then + * jaxp.properties if the system property is not specified. + * + * @param sysPropertyName the name of system property + * @return the catalog file paths, or null if not found. + */ + static public String[] getCatalogFiles(String sysPropertyName) { + String value = SecuritySupport.getJAXPSystemProperty(sysPropertyName); + if (value != null && !value.equals("")) { + return value.split(";"); + } + return null; + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/package.html 2015-10-26 15:21:56.707217811 -0700 @@ -0,0 +1,44 @@ + + + + + + + + Provides the classes for implementing + + XML Catalogs OASIS Standard V1.1, 7 October 2005. + +

+ Unless otherwise noted, passing a null argument to + a constructor or method in any class or interface in this package will + cause a NullPointerException to be thrown. +

+ +@since 9 + + + --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java 2015-10-26 15:21:57.071235877 -0700 @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.xml.internal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.text.MessageFormat; +import java.util.Locale; +import java.util.Properties; +import java.util.ResourceBundle; + +/** + * This class contains utility methods for reading resources in the JAXP packages + */ +public class SecuritySupport { + /** + * Cache for properties in java.home/conf/jaxp.properties + */ + static final Properties cacheProps = new Properties(); + + /** + * Flag indicating whether java.home/conf/jaxp.properties has been read + */ + static volatile boolean firstTime = true; + + private SecuritySupport() {} + + public static String getErrorMessage(Locale locale, String bundle, String key, + Object[] arguments) { + ResourceBundle rb; + if (locale != null) { + rb = ResourceBundle.getBundle(bundle,locale); + } else { + rb = ResourceBundle.getBundle(bundle); + } + + String msg = rb.getString(key); + if (arguments != null) { + msg = MessageFormat.format(msg, arguments); + } + return msg; + } + + /** + * Reads JAXP system property with privilege + * + * @param propName the name of the property + * @return the value of the property + */ + public static String getSystemProperty(final String propName) { + return + AccessController.doPrivileged( + (PrivilegedAction) () -> (String)System.getProperty(propName)); + } + + /** + * Reads JAXP system property in this order: system property, + * $java.home/conf/jaxp.properties if the system property is not specified + * + * @param propName the name of the property + * @return the value of the property + */ + public static String getJAXPSystemProperty(String propName) { + String value = getSystemProperty(propName); + if (value == null) { + value = readJAXPProperty(propName); + } + return value; + } + + /** + * Reads the specified property from $java.home/conf/jaxp.properties + * + * @param propName the name of the property + * @return the value of the property + */ + public static String readJAXPProperty(String propName) { + String value = null; + InputStream is = null; + try { + if (firstTime) { + synchronized (cacheProps) { + if (firstTime) { + String configFile = getSystemProperty("java.home") + File.separator + + "conf" + File.separator + "jaxp.properties"; + File f = new File(configFile); + if (getFileExists(f)) { + is = getFileInputStream(f); + cacheProps.load(is); + } + firstTime = false; + } + } + } + value = cacheProps.getProperty(propName); + + } catch (IOException ex) { + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException ex) {} + } + } + + return value; + } + +//------------------- private methods --------------------------- + static boolean getFileExists(final File f) { + return (AccessController.doPrivileged((PrivilegedAction) () + -> f.exists() ? Boolean.TRUE : Boolean.FALSE)); + } + + static FileInputStream getFileInputStream(final File file) + throws FileNotFoundException { + try { + return AccessController.doPrivileged((PrivilegedExceptionAction) () + -> new FileInputStream(file)); + } catch (PrivilegedActionException e) { + throw (FileNotFoundException) e.getException(); + } + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/CatalogTest.java 2015-10-26 15:21:57.403252356 -0700 @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package catalog; + +import java.io.IOException; +import javax.xml.catalog.CatalogFeatures; +import javax.xml.catalog.CatalogFeatures.Feature; +import javax.xml.catalog.CatalogManager; +import javax.xml.catalog.CatalogResolver; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import static jaxp.library.JAXPTestUtilities.getPathByClassName; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.w3c.dom.Element; +import org.xml.sax.Attributes; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.ext.DefaultHandler2; + +/* + * @bug 8081248 + * @summary Tests basic Catalog functions. + */ + +public class CatalogTest { + /* + Tests basic catalog feature by using a CatalogResolver instance to + resolve a DTD reference to a locally specified DTD file. If the resolution + is successful, the Handler shall return the value of the entity reference + that matches the expected value. + */ + @Test(dataProvider = "catalog") + public void testCatalogResolver(String test, String expected, String catalogFile, String xml, SAXParser saxParser) { + String catalog = null; + if (catalogFile != null) { + catalog = getClass().getResource(catalogFile).getFile(); + } + String url = getClass().getResource(xml).getFile(); + try { + CatalogResolver cr = CatalogManager.catalogResolver(null, catalog); + XMLReader reader = saxParser.getXMLReader(); + reader.setEntityResolver(cr); + MyHandler handler = new MyHandler(saxParser); + reader.setContentHandler(handler); + reader.parse(url); + System.out.println(test + ": expected [" + expected + "] <> actual [" + handler.getResult() + "]"); + Assert.assertEquals(handler.getResult(), expected); + } catch (SAXException | IOException e) { + Assert.fail(e.getMessage()); + } + } + + /* + Verifies that when there's no match, in this case only an invalid + catalog is provided, the resolver will throw an exception by default. + */ + @Test + public void testInvalidCatalog() { + String catalog = getClass().getResource("catalog_invalid.xml").getFile(); + + String test = "testInvalidCatalog"; + try { + CatalogResolver resolver = CatalogManager.catalogResolver(null, catalog); + String actualSystemId = resolver.resolveEntity(null, "http://remote/xml/dtd/sys/alice/docAlice.dtd").getSystemId(); + } catch (Exception e) { + String msg = e.getMessage(); + if (msg != null) { + if (msg.contains("No match found for publicId")) { + Assert.assertEquals(msg, "No match found for publicId 'null' and systemId 'http://remote/xml/dtd/sys/alice/docAlice.dtd'."); + System.out.println(test + ": expected [No match found for publicId 'null' and systemId 'http://remote/xml/dtd/sys/alice/docAlice.dtd'.]"); + System.out.println("actual [" + msg + "]"); + } + } + } + } + + /* + Verifies that if resolve is "ignore", an empty InputSource will be returned + when there's no match. The systemId is then null. + */ + @Test + public void testIgnoreInvalidCatalog() { + String catalog = getClass().getResource("catalog_invalid.xml").getFile(); + CatalogFeatures f = CatalogFeatures.builder() + .with(Feature.FILES, catalog) + .with(Feature.PREFER, "public") + .with(Feature.DEFER, "true") + .with(Feature.RESOLVE, "ignore") + .build(); + + String test = "testInvalidCatalog"; + try { + CatalogResolver resolver = CatalogManager.catalogResolver(f, ""); + String actualSystemId = resolver.resolveEntity(null, "http://remote/xml/dtd/sys/alice/docAlice.dtd").getSystemId(); + System.out.println("testIgnoreInvalidCatalog: expected [null]"); + System.out.println("testIgnoreInvalidCatalog: expected [null]"); + System.out.println("actual [" + actualSystemId + "]"); + Assert.assertEquals(actualSystemId, null); + } catch (Exception e) { + Assert.fail(e.getMessage()); + } + } + + + /* + DataProvider: provides test name, expected string, the catalog, and XML + document. + */ + @DataProvider(name = "catalog") + Object[][] getCatalog() { + return new Object[][]{ + {"testSystem", "Test system entry", "catalog.xml", "system.xml", getParser()}, + {"testRewriteSystem", "Test rewritesystem entry", "catalog.xml", "rewritesystem.xml", getParser()}, + {"testRewriteSystem1", "Test rewritesystem entry", "catalog.xml", "rewritesystem1.xml", getParser()}, + {"testSystemSuffix", "Test systemsuffix entry", "catalog.xml", "systemsuffix.xml", getParser()}, + {"testDelegateSystem", "Test delegatesystem entry", "catalog.xml", "delegatesystem.xml", getParser()}, + {"testPublic", "Test public entry", "catalog.xml", "public.xml", getParser()}, + {"testDelegatePublic", "Test delegatepublic entry", "catalog.xml", "delegatepublic.xml", getParser()}, + }; + } + + SAXParser getParser() { + SAXParser saxParser = null; + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + saxParser = factory.newSAXParser(); + } catch (ParserConfigurationException | SAXException e) { + } + + return saxParser; + } + + + /** + * SAX handler + */ + public class MyHandler extends DefaultHandler2 implements ErrorHandler { + + StringBuilder textContent = new StringBuilder(); + SAXParser saxParser; + + MyHandler(SAXParser saxParser) { + textContent.setLength(0); + this.saxParser = saxParser; + } + + String getResult() { + return textContent.toString(); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + textContent.delete(0, textContent.length()); + try { + System.out.println("Element: " + uri + ":" + localName + " " + qName); + } catch (Exception e) { + throw new SAXException(e); + } + + } + + @Override + public void characters(char ch[], int start, int length) throws SAXException { + textContent.append(ch, start, length); + } + } +} --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/catalog.xml 2015-10-26 15:21:57.727268437 -0700 @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/catalog_invalid.xml 2015-10-26 15:21:58.067285313 -0700 @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/delegatepublic.xml 2015-10-26 15:21:58.415302585 -0700 @@ -0,0 +1,5 @@ + + + +Test &delegatepublic; entry \ No newline at end of file --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/delegatesystem.xml 2015-10-26 15:21:58.739318666 -0700 @@ -0,0 +1,5 @@ + + + +Test &delegatesystem; entry \ No newline at end of file --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/files/delegatecatalog.xml 2015-10-26 15:21:59.075335343 -0700 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/files/delegatepublic.dtd 2015-10-26 15:21:59.407351822 -0700 @@ -0,0 +1,5 @@ + + + + + --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/files/delegatesystem.dtd 2015-10-26 15:21:59.731367903 -0700 @@ -0,0 +1,5 @@ + + + + + --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/files/rewritesystem.dtd 2015-10-26 15:22:00.059384183 -0700 @@ -0,0 +1,5 @@ + + + + + --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/files/systemsuffix.dtd 2015-10-26 15:22:00.391400661 -0700 @@ -0,0 +1,5 @@ + + + + + --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/public.dtd 2015-10-26 15:22:00.723417139 -0700 @@ -0,0 +1,5 @@ + + + + + --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/public.xml 2015-10-26 15:22:01.055433618 -0700 @@ -0,0 +1,6 @@ + + + + +Test &public; entry \ No newline at end of file --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/rewritesystem.xml 2015-10-26 15:22:01.383449898 -0700 @@ -0,0 +1,5 @@ + + + +Test &rewritesystem; entry \ No newline at end of file --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/rewritesystem1.xml 2015-10-26 15:22:01.707465979 -0700 @@ -0,0 +1,5 @@ + + + +Test &rewritesystem; entry \ No newline at end of file --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/system.dtd 2015-10-26 15:22:02.043482656 -0700 @@ -0,0 +1,5 @@ + + + + + --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/system.xml 2015-10-26 15:22:02.371498935 -0700 @@ -0,0 +1,5 @@ + + + +Test &system; entry \ No newline at end of file --- /dev/null 2015-04-26 17:31:22.381138994 -0700 +++ new/test/javax/xml/jaxp/unittest/catalog/systemsuffix.xml 2015-10-26 15:22:02.699515216 -0700 @@ -0,0 +1,5 @@ + + + +Test &systemsuffix; entry \ No newline at end of file