diff --git a/src/java.xml/share/classes/javax/xml/catalog/CatalogImpl.java b/src/java.xml/share/classes/javax/xml/catalog/CatalogImpl.java index 72cd64fca3d..540ddb4931f 100644 --- a/src/java.xml/share/classes/javax/xml/catalog/CatalogImpl.java +++ b/src/java.xml/share/classes/javax/xml/catalog/CatalogImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, 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 @@ -41,6 +41,7 @@ import static javax.xml.catalog.BaseEntry.CatalogEntryType; import static javax.xml.catalog.CatalogFeatures.DEFER_TRUE; import javax.xml.catalog.CatalogFeatures.Feature; import static javax.xml.catalog.CatalogMessages.formatMessage; +import javax.xml.catalog.CatalogResolver.NotFoundAction; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; @@ -59,8 +60,8 @@ class CatalogImpl extends GroupEntry implements Catalog { //Value of the defer attribute to determine if alternative catalogs are read boolean isDeferred = true; - //Value of the resolve attribute - ResolveType resolveType = ResolveType.STRICT; + //Value of the resolve attribute mapped to the resolver's action type + NotFoundAction resolveType = NotFoundAction.STRICT; //indicate whether the Catalog is empty boolean isEmpty; @@ -259,7 +260,7 @@ class CatalogImpl extends GroupEntry implements Catalog { * @param value The value of the resolve attribute */ public final void setResolve(String value) { - resolveType = ResolveType.getType(value); + resolveType = NotFoundAction.getType(value); } /** @@ -267,7 +268,7 @@ class CatalogImpl extends GroupEntry implements Catalog { * * @return The value of the resolve attribute */ - public final ResolveType getResolve() { + public final NotFoundAction getResolve() { return resolveType; } diff --git a/src/java.xml/share/classes/javax/xml/catalog/CatalogManager.java b/src/java.xml/share/classes/javax/xml/catalog/CatalogManager.java index f8ce697b266..7491410a0af 100644 --- a/src/java.xml/share/classes/javax/xml/catalog/CatalogManager.java +++ b/src/java.xml/share/classes/javax/xml/catalog/CatalogManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, 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 @@ -77,6 +77,12 @@ public final class CatalogManager { /** * Creates an instance of a {@code CatalogResolver} using the specified catalog. * + * @apiNote The {@code CatalogResolver} created by this method delegates to + * the underlying {@code catalog}'s RESOLVE property. The {@code CatalogResolver} + * created by {@link #catalogResolver(Catalog, CatalogResolver.NotFoundAction) + * catalogResover(Catalog, CatalogResolver.NotFoundAction)} is based on the + * specified action type when it is unable to resolve a reference. + * * @param catalog the catalog instance * @return an instance of a {@code CatalogResolver} */ @@ -85,6 +91,28 @@ public final class CatalogManager { return new CatalogResolverImpl(catalog); } + /** + * Creates a {@code CatalogResolver} that resolves external references with the given + * {@code catalog} and {@link CatalogResolver.NotFoundAction action} type + * that determines the behavior when unable to resolve a reference. + *
+ * The {@link CatalogResolver.NotFoundAction action} types are mapped to the values
+ * of the {@link CatalogFeatures.Feature#RESOLVE RESOLVE} property.
+ *
+ * @param catalog the catalog instance
+ * @param action the action to be taken when unable to resolve a reference
+ *
+ * @return a {@code CatalogResolver} with the {@code catalog} and {@code action} type
+ *
+ * @since 22
+ */
+ public static CatalogResolver catalogResolver(Catalog catalog, CatalogResolver.NotFoundAction action) {
+ if (catalog == null) CatalogMessages.reportNPEOnNull("catalog", null);
+ if (action == null) CatalogMessages.reportNPEOnNull("action", null);
+
+ return new CatalogResolverImpl(catalog, action);
+ }
+
/**
* Creates an instance of a {@code CatalogResolver} using the specified feature
* settings and uri(s) to one or more catalog files.
diff --git a/src/java.xml/share/classes/javax/xml/catalog/CatalogReader.java b/src/java.xml/share/classes/javax/xml/catalog/CatalogReader.java
index 64b2ea85071..1305beeceaa 100644
--- a/src/java.xml/share/classes/javax/xml/catalog/CatalogReader.java
+++ b/src/java.xml/share/classes/javax/xml/catalog/CatalogReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023, 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
@@ -159,7 +159,7 @@ class CatalogReader extends DefaultHandler implements EntityResolver, URIResolve
CatalogFeatures.DEFER_TRUE : CatalogFeatures.DEFER_FALSE;
}
if (resolve == null) {
- resolve = catalog.getResolve().literal;
+ resolve = catalog.getResolve().toString();
}
//override property settings with those from the catalog file
catalog.setResolve(resolve);
@@ -172,7 +172,7 @@ class CatalogReader extends DefaultHandler implements EntityResolver, URIResolve
return;
} else {
inGroup = true;
- group = new GroupEntry(catalog, base, prefer);
+ group = new GroupEntry(catalog, Util.getAbsoluteURI(catalog.systemId, base), prefer);
catalog.addEntry(group);
return;
}
diff --git a/src/java.xml/share/classes/javax/xml/catalog/CatalogResolver.java b/src/java.xml/share/classes/javax/xml/catalog/CatalogResolver.java
index f542f429c0a..3fd4b6a7858 100644
--- a/src/java.xml/share/classes/javax/xml/catalog/CatalogResolver.java
+++ b/src/java.xml/share/classes/javax/xml/catalog/CatalogResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023, 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
@@ -235,4 +235,55 @@ public interface CatalogResolver extends EntityResolver, XMLResolver,
public LSInput resolveResource(String type, String namespaceUri,
String publicId, String systemId, String baseUri);
+ /**
+ * Defines the actions that a CatalogResolver may take when it is unable to
+ * resolve an external reference. The actions are mapped to the string values
+ * of the {@link CatalogFeatures.Feature#RESOLVE RESOLVE} property.
+ *
+ * @since 22
+ */
+ public static enum NotFoundAction {
+ /**
+ * Indicates that the processing should continue as defined by the
+ * {@link CatalogFeatures.Feature#RESOLVE RESOLVE} property.
+ */
+ CONTINUE {
+ @Override
+ public String toString() { return "continue"; }
+ },
+ /**
+ * Indicates that the reference is skipped as defined by the
+ * {@link CatalogFeatures.Feature#RESOLVE RESOLVE} property.
+ */
+ IGNORE {
+ @Override
+ public String toString() { return "ignore"; }
+ },
+ /**
+ * Indicates that the resolver should throw a CatalogException as defined
+ * by the {@link CatalogFeatures.Feature#RESOLVE RESOLVE} property.
+ */
+ STRICT {
+ @Override
+ public String toString() { return "strict"; }
+ };
+
+ /**
+ * Returns the action type mapped to the specified
+ * {@link CatalogFeatures.Feature#RESOLVE resolve} property.
+ *
+ * @param resolve the value of the RESOLVE property
+ * @return the action type
+ */
+ static public NotFoundAction getType(String resolve) {
+ for (NotFoundAction type : NotFoundAction.values()) {
+ if (type.toString().equals(resolve)) {
+ return type;
+ }
+ }
+ CatalogMessages.reportIAE(CatalogMessages.ERR_INVALID_ARGUMENT,
+ new Object[]{resolve, "RESOLVE"}, null);
+ return null;
+ }
+ }
}
diff --git a/src/java.xml/share/classes/javax/xml/catalog/CatalogResolverImpl.java b/src/java.xml/share/classes/javax/xml/catalog/CatalogResolverImpl.java
index 062148e406a..8026da21043 100644
--- a/src/java.xml/share/classes/javax/xml/catalog/CatalogResolverImpl.java
+++ b/src/java.xml/share/classes/javax/xml/catalog/CatalogResolverImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023, 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
@@ -51,6 +51,8 @@ import org.xml.sax.XMLReader;
*/
final class CatalogResolverImpl implements CatalogResolver {
Catalog catalog;
+ // resolution action type
+ NotFoundAction resolveType;
/**
* Construct an instance of the CatalogResolver from a Catalog.
@@ -58,9 +60,25 @@ final class CatalogResolverImpl implements CatalogResolver {
* @param catalog A Catalog.
*/
public CatalogResolverImpl(Catalog catalog) {
- this.catalog = catalog;
+ this(catalog, null);
}
+ /**
+ * Construct an instance of the CatalogResolver from a Catalog and the
+ * {@link CatalogResolver.NotFoundAction action} type.
+ *
+ * @param catalog a Catalog object
+ * @param action the action type
+ */
+ public CatalogResolverImpl(Catalog catalog, NotFoundAction action) {
+ this.catalog = catalog;
+ // Note: can only happen in this impl
+ if (action == null) {
+ resolveType = ((CatalogImpl) catalog).getResolve();
+ } else {
+ resolveType = action;
+ }
+ }
/*
Implements the EntityResolver interface
*/
@@ -91,7 +109,6 @@ final class CatalogResolverImpl implements CatalogResolver {
return new InputSource(resolvedSystemId);
}
- GroupEntry.ResolveType resolveType = ((CatalogImpl) catalog).getResolve();
switch (resolveType) {
case IGNORE:
return new InputSource(new StringReader(""));
@@ -145,7 +162,6 @@ final class CatalogResolverImpl implements CatalogResolver {
//Report error or return the URI as is when no match is found
if (result == null) {
- GroupEntry.ResolveType resolveType = c.getResolve();
switch (resolveType) {
case IGNORE:
return new SAXSource(new InputSource(new StringReader("")));
@@ -229,7 +245,6 @@ final class CatalogResolverImpl implements CatalogResolver {
}
- GroupEntry.ResolveType resolveType = ((CatalogImpl) catalog).getResolve();
switch (resolveType) {
case IGNORE:
return null;
@@ -250,7 +265,6 @@ final class CatalogResolverImpl implements CatalogResolver {
return new LSInputImpl(is.getSystemId());
}
- GroupEntry.ResolveType resolveType = ((CatalogImpl) catalog).getResolve();
switch (resolveType) {
case IGNORE:
return null;
diff --git a/src/java.xml/share/classes/javax/xml/catalog/GroupEntry.java b/src/java.xml/share/classes/javax/xml/catalog/GroupEntry.java
index 1082f00450e..3350a0dc8bf 100644
--- a/src/java.xml/share/classes/javax/xml/catalog/GroupEntry.java
+++ b/src/java.xml/share/classes/javax/xml/catalog/GroupEntry.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023, 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
@@ -107,34 +107,6 @@ class GroupEntry extends BaseEntry {
}
}
- /**
- * 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
*
diff --git a/src/java.xml/share/classes/javax/xml/catalog/Util.java b/src/java.xml/share/classes/javax/xml/catalog/Util.java
index 50a72aed620..3bf6e31f98b 100644
--- a/src/java.xml/share/classes/javax/xml/catalog/Util.java
+++ b/src/java.xml/share/classes/javax/xml/catalog/Util.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023, 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
@@ -28,6 +28,7 @@ import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
+import java.net.URL;
import java.util.Iterator;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -267,4 +268,32 @@ class Util {
Util.validateUrisSyntax(value.split(";"));
}
}
+
+ /**
+ * Returns the absolute form of the specified uri after resolving it against
+ * the base. Returns the uri as is if it's already absolute.
+ *
+ * @param base the base, that is the system id of the catalog within the
+ * Catalog implementation
+ * @param uri the specified uri
+ * @return the absolute form of the specified uri
+ */
+ @SuppressWarnings("deprecation")
+ static String getAbsoluteURI(String base, String uri) {
+ String temp = "";
+ try {
+ URL baseURL = new URL(base);
+ URI specURI = URI.create(uri);
+
+ if (specURI.isAbsolute()) {
+ temp = specURI.toURL().toString();
+ } else {
+ temp = (new URL(baseURL, uri)).toString();
+ }
+ } catch (MalformedURLException ex) {
+ // shouldn't happen since inputs are validated, report error in case
+ CatalogMessages.reportError(CatalogMessages.ERR_INVALID_CATALOG);
+ }
+ return temp;
+ }
}
diff --git a/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogResolverTest.java b/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogResolverTest.java
new file mode 100644
index 00000000000..2eba917506e
--- /dev/null
+++ b/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogResolverTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2023, 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.net.URI;
+import java.nio.file.Paths;
+import javax.xml.catalog.Catalog;
+import javax.xml.catalog.CatalogException;
+import javax.xml.catalog.CatalogFeatures;
+import javax.xml.catalog.CatalogManager;
+import javax.xml.catalog.CatalogResolver;
+import javax.xml.catalog.CatalogResolver.NotFoundAction;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Listeners;
+import org.testng.annotations.Test;
+import org.xml.sax.InputSource;
+
+/*
+ * @test
+ * @bug 8316996
+ * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
+ * @run testng/othervm catalog.CatalogResolverTest
+ * @summary Tests CatalogResolver functions. See CatalogTest for existing basic
+ * functional tests.
+ */
+@Listeners({jaxp.library.FilePolicy.class})
+public class CatalogResolverTest extends CatalogSupportBase {
+ static final String KEY_FILES = "javax.xml.catalog.files";
+ static final String SYSTEM_ID = "http://openjdk_java_net/xml/catalog/dtd/system.dtd";
+
+ /*
+ * Initializing fields
+ */
+ @BeforeClass
+ public void setUpClass() throws Exception {
+ super.setUp();
+ }
+
+ /*
+ DataProvider: data used to verify the RESOLVE property, including the valid
+ values and the effect of overriding that on the Catalog.
+ Data columns:
+ resolve property for the Catalog, resolve property for the CatalogResolver,
+ system ID to be resolved, expected result, expected exception
+ */
+ @DataProvider(name = "factoryMethodInput")
+ public Object[][] getInputs() throws Exception {
+
+ return new Object[][]{
+ // Valid values and overriding verification
+ // RESOLVE=strict but expected match
+ {"continue", NotFoundAction.STRICT, SYSTEM_ID, "system.dtd", null},
+ // RESOLVE=strict plus no match: expect exception
+ {"continue", NotFoundAction.STRICT, "bogusID", "", CatalogException.class},
+ // RESOLVE=ignore, continue: expect no match but without an exception
+ // Note that these tests do not differentiate empty InputSource from
+ // null, in both cases, the returned ID is null
+ {"strict", NotFoundAction.IGNORE, "bogusID", null, null},
+ {"strict", NotFoundAction.CONTINUE, "bogusID", null, null},
+ };
+ }
+
+ @DataProvider(name = "NPETest")
+ public Object[][] getNPETest() throws Exception {
+ return new Object[][]{
+ {null, null},
+ {getCatalog("ignore"), null},
+ };
+ }
+
+ /**
+ * Tests the factory method for creating CatalogResolver with an
+ * {@link javax.xml.catalog.CatalogResolver.NotFoundAction action} type.
+ * The 2-arg {@link javax.xml.catalog.CatalogManager#catalogResolver(
+ * javax.xml.catalog.Catalog, javax.xml.catalog.CatalogResolver.NotFoundAction)
+ * catalogResolver} method adds the action type to be used for determining
+ * the behavior instead of relying on the underlying catalog.
+ *
+ * @param cResolve the resolve property set on the Catalog object
+ * @param action the resolve property set on the CatalogResolver to override
+ * that of the Catalog
+ * @param systemId the system ID to be resolved
+ * @param expectedResult the expected result
+ * @param expectedThrow the expected exception
+ * @throws Exception if the test fails
+ */
+ @Test(dataProvider = "factoryMethodInput")
+ public void testResolveProperty(String cResolve, NotFoundAction action,
+ String systemId, String expectedResult, Class