You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2026/08/26 12:21:41 UTC

(commons-xml) branch main updated (51879f6 -> 14ec662)

This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/commons-xml.git


    from 51879f6  Fix merge conflict in changes.xml
     new f6583c7  Sort members
     new c5b04f0  Normalize the term for a new object from "freah" to "new".
     new 972d94d  Remove noisy editor comments
     new 8e00317  Tests don't print to the console by default.
     new a2a3a31  Javadoc
     new 80c5a0a  Javadoc
     new 641d267  Fail-fast in constructor on null delegate input.
     new 14ec662  Merge branch 'main' of https://github.com/apache/commons-xml into main

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 AGENTS.md                                          |   2 +-
 .../commons/xml/FallbackIgnoreEntityResolver2.java |  84 +++--
 .../xml/FallbackIgnoreLSResourceResolver.java      |  40 ++-
 .../commons/xml/FallbackIgnoreURIResolver.java     |  54 ++-
 .../commons/xml/FallbackIgnoreXMLResolver.java     |  38 ++-
 .../commons/xml/HardeningDocumentBuilder.java      |  48 +--
 .../xml/HardeningDocumentBuilderFactory.java       |  23 +-
 .../org/apache/commons/xml/HardeningException.java |  30 +-
 .../org/apache/commons/xml/HardeningSAXParser.java |  49 +--
 .../commons/xml/HardeningSAXParserFactory.java     |  23 +-
 .../org/apache/commons/xml/HardeningSchema.java    |  16 +-
 .../apache/commons/xml/HardeningSchemaFactory.java |  73 ++--
 .../org/apache/commons/xml/HardeningTemplates.java |  38 ++-
 .../commons/xml/HardeningTemplatesHandler.java     |  82 +++--
 .../apache/commons/xml/HardeningTransformer.java   |  80 +++--
 .../commons/xml/HardeningTransformerFactory.java   | 169 +++++-----
 .../commons/xml/HardeningTransformerHandler.java   | 110 +++---
 .../org/apache/commons/xml/HardeningValidator.java |   9 +-
 .../commons/xml/HardeningValidatorHandler.java     |  97 +++---
 .../org/apache/commons/xml/HardeningXMLFilter.java |  11 +-
 .../commons/xml/HardeningXMLInputFactory.java      | 178 +++++-----
 .../org/apache/commons/xml/HardeningXMLReader.java |  32 +-
 .../org/apache/commons/xml/HardeningXPath.java     |  18 +-
 .../commons/xml/HardeningXPathExpression.java      |  10 +-
 .../apache/commons/xml/HardeningXPathFactory.java  |  18 +-
 .../org/apache/commons/xml/SAXParserHardener.java  |   2 +-
 .../java/org/apache/commons/xml/SaxonProvider.java |  42 +--
 .../apache/commons/xml/TransformerHardener.java    |   2 +-
 .../java/org/apache/commons/xml/XmlFactories.java  |  14 +-
 src/site/markdown/index.md                         |   4 +-
 src/site/markdown/threat_model.md                  |   2 +-
 .../commons/xml/AssociatedStylesheetTest.java      |  16 +-
 .../org/apache/commons/xml/AttackTestSupport.java  |  38 +--
 .../org/apache/commons/xml/DenyUnresolvedTest.java |  10 +-
 .../commons/xml/EntityResolverFloorTest.java       | 372 ++++++++++-----------
 .../org/apache/commons/xml/ResetHardeningTest.java |  28 +-
 .../apache/commons/xml/SaxSurfaceTestSupport.java  |  26 +-
 .../commons/xml/SaxonAlternateFactoryTest.java     |  18 +-
 .../apache/commons/xml/SchemaLocationDomTest.java  |  52 +--
 .../commons/xml/SchemaLocationPropertyTest.java    |  76 ++---
 .../apache/commons/xml/SchemaLocationSaxTest.java  |  56 ++--
 .../apache/commons/xml/ShadingFootprintTest.java   | 124 +++----
 .../apache/commons/xml/TemplatesHandlerTest.java   |  14 +-
 .../apache/commons/xml/TransformerHandlerTest.java |  20 +-
 .../xml/UnsupportedXmlImplementationTest.java      |  30 +-
 .../java/org/apache/commons/xml/XIncludeTest.java  | 194 +++++------
 .../java/org/apache/commons/xml/XMLFilterTest.java |  48 +--
 .../apache/commons/xml/XPathInputSourceTest.java   |  14 +-
 .../org/apache/commons/xml/XmlFactoriesTest.java   |  46 +--
 49 files changed, 1402 insertions(+), 1178 deletions(-)


(commons-xml) 05/08: Javadoc

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-xml.git

commit a2a3a316c855f7eea6c746c8350d7e283cc81941
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Aug 26 07:59:14 2026 -0400

    Javadoc
---
 .../commons/xml/FallbackIgnoreEntityResolver2.java | 40 +++++++++++++--------
 .../xml/FallbackIgnoreLSResourceResolver.java      | 21 ++++++++---
 .../commons/xml/FallbackIgnoreURIResolver.java     | 41 +++++++++++++++-------
 .../commons/xml/FallbackIgnoreXMLResolver.java     | 10 ++++++
 4 files changed, 81 insertions(+), 31 deletions(-)

diff --git a/src/main/java/org/apache/commons/xml/FallbackIgnoreEntityResolver2.java b/src/main/java/org/apache/commons/xml/FallbackIgnoreEntityResolver2.java
index 0b036b9..676f3a5 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreEntityResolver2.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreEntityResolver2.java
@@ -30,9 +30,10 @@
 
 /**
  * Entity resolver that consults an optional caller-supplied resolver and ignores (resolves to empty) whatever the caller does not resolve.
- *
- * <p>The canonical hardening floor, and the entity-resolution counterpart of the JAXP 1.5 {@code ACCESS_EXTERNAL_*} properties. Every floor
- * ({@link FallbackIgnoreLSResourceResolver}, {@link FallbackIgnoreURIResolver} and {@link FallbackIgnoreXMLResolver}) shares two defining properties:</p>
+ * <p>
+ * The canonical hardening floor, and the entity-resolution counterpart of the JAXP 1.5 {@code ACCESS_EXTERNAL_*} properties. Every floor
+ * ({@link FallbackIgnoreLSResourceResolver}, {@link FallbackIgnoreURIResolver} and {@link FallbackIgnoreXMLResolver}) shares two defining properties:
+ * </p>
  * <ol>
  * <li><strong>Non-removable, and it wraps the resolver the caller sets.</strong> The hardened wrappers install one and route a caller-set resolver through
  * {@code setDelegate} rather than letting it replace the floor, so the caller's resolver is consulted first but cannot remove the floor underneath it.</li>
@@ -40,15 +41,17 @@
  * all). This is where a floor departs from stock JAXP: normally an unresolved lookup falls back to the processor's built-in resolution and the resource is
  * <em>fetched</em>; a floor instead resolves it to <em>empty</em> content, so the parse continues without the external fetch and without a leak.</li>
  * </ol>
- *
- * <p>The hardened DOM and SAX wrappers install one of these and, when the caller sets their own {@link EntityResolver}, route it through {@link #setDelegate}
+ * <p>
+ * The hardened DOM and SAX wrappers install one of these and, when the caller sets their own {@link EntityResolver}, route it through {@link #setDelegate}
  * rather than letting it replace the floor. A caller therefore opts a specific resource in by returning a non-{@code null} {@link InputSource} from their
  * resolver; anything they leave unresolved (a {@code null} return, or no caller resolver at all) goes to {@link #onUnresolved}, which returns empty content by
- * default.</p>
- *
- * <p>It extends {@link DefaultHandler2} so it is also usable as a {@link org.xml.sax.ext.LexicalHandler}; {@link #getExternalSubset} therefore inherits the
+ * default.
+ * </p>
+ * <p>
+ * It extends {@link DefaultHandler2} so it is also usable as a {@link org.xml.sax.ext.LexicalHandler}; {@link #getExternalSubset} therefore inherits the
  * {@code DefaultHandler2} "no synthetic subset" default. Only {@link #resolveEntity(String, String, String, String) resolveEntity} (the actual external fetch)
- * reaches the ignore fallback.</p>
+ * reaches the ignore fallback.
+ * </p>
  */
 class FallbackIgnoreEntityResolver2 extends DefaultHandler2 {
 
@@ -76,10 +79,20 @@ private static String absolutize(final String baseURI, final String systemId) {
      */
     private EntityResolver delegate;
 
+    /**
+     * Constructs a new ignore-all floor with an optional caller-supplied resolver.
+     *
+     * @param delegate The caller-supplied resolver, or {@code null} for a pure ignore-all floor.
+     */
     FallbackIgnoreEntityResolver2(final EntityResolver delegate) {
         this.delegate = delegate;
     }
 
+    /**
+     * Gets the delegate provided by the constructor or set by {@link #setDelegate}, may be {@code null}.
+     *
+     * @return The delegate provided by the constructor or set by {@link #setDelegate}, may be {@code null}.
+     */
     final EntityResolver getDelegate() {
         return delegate;
     }
@@ -97,8 +110,7 @@ final EntityResolver getDelegate() {
      * @throws SAXException when {@value XmlFactories#THROW_ON_UNRESOLVED} is set: unresolved references are rejected instead of resolved to empty.
      * @throws IOException  never by the default implementation.
      */
-    protected InputSource onUnresolved(final String name, final String publicId, final String baseURI, final String systemId)
-            throws SAXException, IOException {
+    protected InputSource onUnresolved(final String name, final String publicId, final String baseURI, final String systemId) throws SAXException, IOException {
         if (HardeningException.throwOnUnresolved()) {
             throw new SAXException(HardeningException.forbidden(name, null, publicId, systemId, baseURI));
         }
@@ -120,11 +132,11 @@ public final InputSource resolveEntity(final String name, final String publicId,
         return resolved != null ? resolved : onUnresolved(name, publicId, baseURI, systemId);
     }
 
-    private InputSource resolveWithDelegate(final String name, final String publicId, final String baseURI,
-            final String systemId) throws SAXException, IOException {
+    private InputSource resolveWithDelegate(final String name, final String publicId, final String baseURI, final String systemId)
+            throws SAXException, IOException {
         if (delegate != null) {
             return delegate instanceof EntityResolver2 ? ((EntityResolver2) delegate).resolveEntity(name, publicId, baseURI, systemId) :
-                    // We need to resolve the systemId against baseURI, because a plain EntityResolver expects an absolute URI.
+            // We need to resolve the systemId against baseURI, because a plain EntityResolver expects an absolute URI.
                     delegate.resolveEntity(publicId, absolutize(baseURI, systemId));
         }
         return null;
diff --git a/src/main/java/org/apache/commons/xml/FallbackIgnoreLSResourceResolver.java b/src/main/java/org/apache/commons/xml/FallbackIgnoreLSResourceResolver.java
index 3b98643..3c0d764 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreLSResourceResolver.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreLSResourceResolver.java
@@ -27,11 +27,12 @@
 
 /**
  * {@link LSResourceResolver} floor: consults an optional caller-supplied resolver and ignores (resolves to empty) whatever the caller does not resolve.
- *
- * <p>The schema-compile counterpart of {@link FallbackIgnoreEntityResolver2}. The hardened {@link javax.xml.validation.SchemaFactory}, {@link
- * javax.xml.validation.Validator} and {@link javax.xml.validation.ValidatorHandler} wrappers install one of these and route a caller-set resolver through
- * {@link #setDelegate} rather than letting it replace the floor. A caller opts a specific resource in by returning a non-{@code null} {@link LSInput};
- * anything left unresolved resolves to an empty {@link LSInput}, so the external resource is neither fetched nor leaked.</p>
+ * <p>
+ * The schema-compile counterpart of {@link FallbackIgnoreEntityResolver2}. The hardened {@link javax.xml.validation.SchemaFactory},
+ * {@link javax.xml.validation.Validator} and {@link javax.xml.validation.ValidatorHandler} wrappers install one of these and route a caller-set resolver
+ * through {@link #setDelegate} rather than letting it replace the floor. A caller opts a specific resource in by returning a non-{@code null} {@link LSInput};
+ * anything left unresolved resolves to an empty {@link LSInput}, so the external resource is neither fetched nor leaked.
+ * </p>
  */
 final class FallbackIgnoreLSResourceResolver implements LSResourceResolver {
 
@@ -48,10 +49,20 @@ private static DOMImplementationLS domImplementationLS() {
 
     private LSResourceResolver delegate;
 
+    /**
+     * Constructs a new resolver that consults the given delegate and ignores whatever it does not resolve.
+     *
+     * @param delegate optional caller-supplied resolver to consult first; may be {@code null}.
+     */
     FallbackIgnoreLSResourceResolver(final LSResourceResolver delegate) {
         this.delegate = delegate;
     }
 
+    /**
+     * Gets the delegate provided by the constructor or set by {@link #setDelegate}, may be {@code null}.
+     *
+     * @return The delegate provided by the constructor or set by {@link #setDelegate}, may be {@code null}.
+     */
     LSResourceResolver getDelegate() {
         return delegate;
     }
diff --git a/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java b/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java
index 31da5dc..edcd78f 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java
@@ -30,18 +30,21 @@
 
 /**
  * {@link URIResolver} floor: consults an optional caller-supplied resolver and ignores (resolves to empty) whatever the caller does not resolve.
- *
- * <p>The XSLT counterpart of {@link FallbackIgnoreEntityResolver2}, guarding {@code xsl:import}/{@code xsl:include} at compile time and {@code document()} at
+ * <p>
+ * The XSLT counterpart of {@link FallbackIgnoreEntityResolver2}, guarding {@code xsl:import}/{@code xsl:include} at compile time and {@code document()} at
  * transform time. The hardened {@link javax.xml.transform.TransformerFactory} and {@link javax.xml.transform.Transformer} wrappers install one of these and
  * route a caller-set resolver through {@link #setDelegate} rather than letting it replace the floor. A caller opts a specific URI in by returning a
- * non-{@code null} {@link Source}; anything left unresolved resolves to an empty {@link Source}, so the external resource is neither fetched nor leaked.</p>
- *
- * <p>The shape of that empty {@link Source} is supplied by the caller: the default is a well-formed empty DOM document (which every stock TrAX consumer
- * accepts), while the Saxon path supplies {@code EmptySource.getInstance()} so its consumers get the "empty" shape they expect.</p>
- *
- * <p>An opted-in {@link javax.xml.transform.stream.StreamSource} or reader-less {@link javax.xml.transform.sax.SAXSource} is rewritten to carry a hardened
- * reader before it is returned, so the implementation parses the opted-in content on the same floor instead of with an internal reader at its own defaults. A
- * {@link javax.xml.transform.dom.DOMSource} or a {@link javax.xml.transform.sax.SAXSource} carrying the caller's own reader is returned as-is.</p>
+ * non-{@code null} {@link Source}; anything left unresolved resolves to an empty {@link Source}, so the external resource is neither fetched nor leaked.
+ * </p>
+ * <p>
+ * The shape of that empty {@link Source} is supplied by the caller: the default is a well-formed empty DOM document (which every stock TrAX consumer accepts),
+ * while the Saxon path supplies {@code EmptySource.getInstance()} so its consumers get the "empty" shape they expect.
+ * </p>
+ * <p>
+ * An opted-in {@link javax.xml.transform.stream.StreamSource} or reader-less {@link javax.xml.transform.sax.SAXSource} is rewritten to carry a hardened reader
+ * before it is returned, so the implementation parses the opted-in content on the same floor instead of with an internal reader at its own defaults. A
+ * {@link javax.xml.transform.dom.DOMSource} or a {@link javax.xml.transform.sax.SAXSource} carrying the caller's own reader is returned as-is.
+ * </p>
  */
 final class FallbackIgnoreURIResolver implements URIResolver {
 
@@ -62,15 +65,24 @@ private static Document newEmptyDocument() {
 
     private URIResolver delegate;
 
-    /** Produces the empty {@link Source} returned for an unresolved reference; a new value per call keeps callers from mutating a shared Source. */
+    /**
+     * Produces the empty {@link Source} returned for an unresolved reference; a new value per call keeps callers from mutating a shared Source.
+     */
     private final Supplier<Source> emptySource;
 
+    /**
+     * Constructs a new resolver.
+     *
+     * @param delegate optional caller-supplied resolver to consult first; may be {@code null}.
+     */
     FallbackIgnoreURIResolver(final URIResolver delegate) {
         this(delegate, null);
     }
 
     /**
-     * @param delegate the resolver to delegate resolution to.
+     * Constructs a new resolver.
+     *
+     * @param delegate    the resolver to delegate resolution to.
      * @param emptySource the empty-{@link Source} supplier for the ignore outcome, or {@code null} for the default empty DOM document.
      */
     FallbackIgnoreURIResolver(final URIResolver delegate, final Supplier<Source> emptySource) {
@@ -78,6 +90,11 @@ private static Document newEmptyDocument() {
         this.emptySource = emptySource != null ? emptySource : () -> new DOMSource(EMPTY_DOCUMENT);
     }
 
+    /**
+     * Gets the delegate provided by the constructor or set by {@link #setDelegate}, may be {@code null}.
+     *
+     * @return The delegate provided by the constructor or set by {@link #setDelegate}, may be {@code null}.
+     */
     URIResolver getDelegate() {
         return delegate;
     }
diff --git a/src/main/java/org/apache/commons/xml/FallbackIgnoreXMLResolver.java b/src/main/java/org/apache/commons/xml/FallbackIgnoreXMLResolver.java
index 94c24c8..b055a2a 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreXMLResolver.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreXMLResolver.java
@@ -41,10 +41,20 @@ final class FallbackIgnoreXMLResolver implements XMLResolver {
 
     private XMLResolver delegate;
 
+    /**
+     * Constructs a new resolver that consults the given delegate and ignores whatever it does not resolve.
+     *
+     * @param delegate optional caller-supplied resolver to consult first; may be {@code null}.
+     */
     FallbackIgnoreXMLResolver(final XMLResolver delegate) {
         this.delegate = delegate;
     }
 
+    /**
+     * Gets the delegate provided by the constructor or set by {@link #setDelegate}, may be {@code null}.
+     *
+     * @return The delegate provided by the constructor or set by {@link #setDelegate}, may be {@code null}.
+     */
     XMLResolver getDelegate() {
         return delegate;
     }


(commons-xml) 02/08: Normalize the term for a new object from "freah" to "new".

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-xml.git

commit c5b04f04e86c3c94e72e865827efc313035313e3
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Aug 26 07:25:30 2026 -0400

    Normalize the term for a new object from "freah" to "new".
---
 AGENTS.md                                                  |  2 +-
 .../org/apache/commons/xml/FallbackIgnoreURIResolver.java  |  2 +-
 .../java/org/apache/commons/xml/HardeningSAXParser.java    |  2 +-
 .../apache/commons/xml/HardeningTransformerFactory.java    |  2 +-
 .../java/org/apache/commons/xml/HardeningXMLFilter.java    |  2 +-
 .../org/apache/commons/xml/HardeningXMLInputFactory.java   |  2 +-
 .../java/org/apache/commons/xml/SAXParserHardener.java     |  2 +-
 src/main/java/org/apache/commons/xml/XmlFactories.java     | 14 +++++++-------
 src/site/markdown/index.md                                 |  4 ++--
 src/site/markdown/threat_model.md                          |  2 +-
 src/test/java/org/apache/commons/xml/XmlFactoriesTest.java |  2 +-
 11 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md
index 9b78cd5..73692e9 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -47,7 +47,7 @@ Guidance for AI coding agents working in this repository.
 ### Prose
 
 - No em-dashes (`—`) in Javadoc, comments, commit messages, or documentation.
-  Use commas, colons, parentheses, or a fresh sentence instead. This applies to
+  Use commas, colons, parentheses, or a new sentence instead. This applies to
   HTML entities too (`&mdash;`).
 
 ### Commits
diff --git a/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java b/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java
index a462872..31da5dc 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java
@@ -62,7 +62,7 @@ private static Document newEmptyDocument() {
 
     private URIResolver delegate;
 
-    /** Produces the empty {@link Source} returned for an unresolved reference; a fresh value per call keeps callers from mutating a shared Source. */
+    /** Produces the empty {@link Source} returned for an unresolved reference; a new value per call keeps callers from mutating a shared Source. */
     private final Supplier<Source> emptySource;
 
     FallbackIgnoreURIResolver(final URIResolver delegate) {
diff --git a/src/main/java/org/apache/commons/xml/HardeningSAXParser.java b/src/main/java/org/apache/commons/xml/HardeningSAXParser.java
index 5e176b7..e37f3d6 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSAXParser.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSAXParser.java
@@ -34,7 +34,7 @@
  * SAX 1 ({@link #getParser()}) path gets the same hardening. The SAX 1 view matters because some consumers, such as Xalan's identity transformer, still ask
  * for a {@link Parser}.</p>
  *
- * <p>The hardened reader is computed lazily on first access and cached: hardening an {@link XMLReader} can install a fresh wrapper (Android's Expat path), so
+ * <p>The hardened reader is computed lazily on first access and cached: hardening an {@link XMLReader} can install a new wrapper (Android's Expat path), so
  * every parse must run through the same instance. The {@code parse(...)} overloads inherited from {@link SAXParser} dispatch virtually to {@link #getXMLReader()}
  * and {@link #getParser()}, so they too run through the hardened views without further overrides.</p>
  */
diff --git a/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java b/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
index 84775d4..5b8ef0f 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
@@ -45,7 +45,7 @@
  * {@link javax.xml.transform.TransformerFactory} wrapper that rewrites every Source-taking entry point through {@link SAXParserHardener#hardenSource(Source)} before
  * delegating.
  *
- * <p>Used by providers whose underlying TrAX implementation pulls a fresh {@code SAXParserFactory.newInstance()} for any Source that is not already a
+ * <p>Used by providers whose underlying TrAX implementation pulls a new {@code SAXParserFactory.newInstance()} for any Source that is not already a
  * {@link SAXSource} carrying its own {@link XMLReader}, and only sets {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING FSP} on the resulting reader.
  * Wrapping the factory and rewriting the Source upstream guarantees the parse runs through an {@link XmlFactories}-hardened reader instead.</p>
  *
diff --git a/src/main/java/org/apache/commons/xml/HardeningXMLFilter.java b/src/main/java/org/apache/commons/xml/HardeningXMLFilter.java
index 6b99312..eac2f13 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXMLFilter.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXMLFilter.java
@@ -66,7 +66,7 @@ public void parse(final InputSource input) throws SAXException, IOException {
             result.setLexicalHandler((LexicalHandler) handler);
         }
         try {
-            // A fresh HardeningTransformer per parse: the floor is installed on it, and transformers are not reusable across concurrent parses.
+            // A new HardeningTransformer per parse: the floor is installed on it, and transformers are not reusable across concurrent parses.
             final Transformer transformer = templates.newTransformer();
             transformer.transform(new SAXSource(getParent(), input), result);
         } catch (final TransformerException e) {
diff --git a/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java b/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
index a806018..05ffbd7 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
@@ -45,7 +45,7 @@
  * {@code com.ctc.wstx.*Resolver} keys) is routed uniformly: a caller who supplies their own {@link FallbackIgnoreXMLResolver} takes control and it is
  * passed straight to the delegate; otherwise the current resolver on that hook is read, and if it is one of our floors the caller's resolver is set as its
  * {@link FallbackIgnoreXMLResolver#setDelegate delegate} (an opt-in the floor cannot be removed by), or, if the hook is empty, the caller's resolver is
- * wrapped in a fresh floor. This matters because Woodstox does not chain resolvers: when a resolver returns {@code null}, {@code DefaultInputResolver} falls
+ * wrapped in a new floor. This matters because Woodstox does not chain resolvers: when a resolver returns {@code null}, {@code DefaultInputResolver} falls
  * through to fetching the systemId URL itself, so a caller-set resolver that returns {@code null} must still land behind the floor. {@link #getXMLResolver()} and
  * {@code getProperty} report the caller's resolver unwrapped.</p>
  */
diff --git a/src/main/java/org/apache/commons/xml/SAXParserHardener.java b/src/main/java/org/apache/commons/xml/SAXParserHardener.java
index 978b809..08f874b 100644
--- a/src/main/java/org/apache/commons/xml/SAXParserHardener.java
+++ b/src/main/java/org/apache/commons/xml/SAXParserHardener.java
@@ -139,7 +139,7 @@ static Source hardenSource(final Source source) throws TransformerConfigurationE
     }
 
     /**
-     * Creates a fresh hardened, namespace-aware {@link XMLReader} for the TrAX wrappers to parse sources with.
+     * Creates a new hardened, namespace-aware {@link XMLReader} for the TrAX wrappers to parse sources with.
      *
      * @return a hardened reader.
      * @throws TransformerConfigurationException if a hardened reader cannot be obtained.
diff --git a/src/main/java/org/apache/commons/xml/XmlFactories.java b/src/main/java/org/apache/commons/xml/XmlFactories.java
index a94d5df..65b69ac 100644
--- a/src/main/java/org/apache/commons/xml/XmlFactories.java
+++ b/src/main/java/org/apache/commons/xml/XmlFactories.java
@@ -28,7 +28,7 @@
 /**
  * Entry point for obtaining hardened JAXP factories.
  *
- * <p>Every method on this class returns a <em>fresh, hardened</em> factory instance. No caching or pooling is performed; callers on a hot path are responsible
+ * <p>Every method on this class returns a <em>new, hardened</em> factory instance. No caching or pooling is performed; callers on a hot path are responsible
  * for their own caching.</p>
  *
  * <h2>Hardening guarantees</h2>
@@ -81,7 +81,7 @@ public final class XmlFactories {
     static final String THROW_ON_UNRESOLVED = "org.apache.commons.xml.throwOnUnresolved";
 
     /**
-     * Returns a fresh, hardened {@link DocumentBuilderFactory}.
+     * Returns a new, hardened {@link DocumentBuilderFactory}.
      *
      * <p>Beyond the three universal guarantees on {@link XmlFactories}, XInclude resolution is denied by default.
      * When {@link DocumentBuilderFactory#setXIncludeAware(boolean) setXIncludeAware(true)} is called on the returned
@@ -97,7 +97,7 @@ public static DocumentBuilderFactory newDocumentBuilderFactory() {
     }
 
     /**
-     * Returns a fresh, hardened {@link SAXParserFactory}.
+     * Returns a new, hardened {@link SAXParserFactory}.
      *
      * <p>Beyond the three universal guarantees on {@link XmlFactories}, XInclude resolution is denied by default.
      * When {@link SAXParserFactory#setXIncludeAware(boolean) setXIncludeAware(true)} is called on the returned
@@ -114,7 +114,7 @@ public static SAXParserFactory newSAXParserFactory() {
     }
 
     /**
-     * Returns a fresh, hardened {@link SchemaFactory} for the given schema language.
+     * Returns a new, hardened {@link SchemaFactory} for the given schema language.
      *
      * <p>Beyond the three universal guarantees on {@link XmlFactories}:</p>
      *
@@ -136,7 +136,7 @@ public static SchemaFactory newSchemaFactory(final String schemaLanguage) {
     }
 
     /**
-     * Returns a fresh, hardened {@link TransformerFactory}.
+     * Returns a new, hardened {@link TransformerFactory}.
      *
      * <p>Beyond the three universal guarantees on {@link XmlFactories}: {@code xsl:import}, {@code xsl:include} and {@code document()} URIs are not
      * resolved.</p>
@@ -161,7 +161,7 @@ public static TransformerFactory newTransformerFactory() {
     }
 
     /**
-     * Returns a fresh, hardened {@link XMLInputFactory}.
+     * Returns a new, hardened {@link XMLInputFactory}.
      *
      * <p>The three universal guarantees on {@link XmlFactories} apply; StAX exposes no additional vectors beyond them.</p>
      *
@@ -173,7 +173,7 @@ public static XMLInputFactory newXMLInputFactory() {
     }
 
     /**
-     * Returns a fresh, hardened {@link XPathFactory} for the default XPath object model.
+     * Returns a new, hardened {@link XPathFactory} for the default XPath object model.
      *
      * <p>Beyond the three universal guarantees on {@link XmlFactories}, URI-fetching XPath 3.1+ functions ({@code doc()}, {@code collection()},
      * {@code unparsed-text()}) are not resolved.</p>
diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md
index ac85fef..0a88d1b 100644
--- a/src/site/markdown/index.md
+++ b/src/site/markdown/index.md
@@ -40,7 +40,7 @@ such as standalone Xerces, Woodstox, or Saxon's TrAX, need further configuration
 library author has no control over which implementation is on the classpath at runtime, so the effective security
 posture of their code depends on a deployment decision made elsewhere.
 
-This library provides that baseline. Each `XmlFactories` call returns a fresh factory hardened by an
+This library provides that baseline. Each `XmlFactories` call returns a new factory hardened by an
 implementation-specific recipe, so the returned object behaves the same way security-wise regardless of which JAXP
 implementation resolved. Security becomes a property of the call, not of the classpath, and there is one place to
 update when a new hardening setting becomes available or a default changes.
@@ -57,7 +57,7 @@ Add the library to your build:
 </dependency>
 ```
 
-Every method on `XmlFactories` returns a fresh, hardened factory.
+Every method on `XmlFactories` returns a new, hardened factory.
 Pick the one that matches the API you already use;
 no other configuration is required.
 On hardened factories an external resource reference (DTD, entity, schema, stylesheet) is never fetched:
diff --git a/src/site/markdown/threat_model.md b/src/site/markdown/threat_model.md
index 562c533..1a688c1 100644
--- a/src/site/markdown/threat_model.md
+++ b/src/site/markdown/threat_model.md
@@ -41,7 +41,7 @@ a finding that falls under [What is out of scope](#what-is-out-of-scope) will be
 ### Scope and intended use
 
 This library is a helper for **safely creating JAXP factories**. Each `XmlFactories.newXxxFactory()` method returns a
-fresh, hardened factory whose parsers reject the common XML attacks (external entity / DTD resolution, XXE, SSRF through
+new, hardened factory whose parsers reject the common XML attacks (external entity / DTD resolution, XXE, SSRF through
 external references, and entity-expansion denial of service such as Billion Laughs). The exact guarantee each factory
 makes is documented in the Javadoc:
 
diff --git a/src/test/java/org/apache/commons/xml/XmlFactoriesTest.java b/src/test/java/org/apache/commons/xml/XmlFactoriesTest.java
index 8a016b9..e61d64b 100644
--- a/src/test/java/org/apache/commons/xml/XmlFactoriesTest.java
+++ b/src/test/java/org/apache/commons/xml/XmlFactoriesTest.java
@@ -40,7 +40,7 @@
 /**
  * Public-API smoke tests for {@link XmlFactories}.
  *
- * <p>Attack tests live in the {@code attacks} sub-package; this file only verifies that fresh factories are returned, that they report safe defaults, and that
+ * <p>Attack tests live in the {@code attacks} sub-package; this file only verifies that new factories are returned, that they report safe defaults, and that
  * a benign document still parses successfully.</p>
  */
 class XmlFactoriesTest {


(commons-xml) 04/08: Tests don't print to the console by default.

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-xml.git

commit 8e00317a720c1c5dadbfa1cc2512e605cf5c2700
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Aug 26 07:45:40 2026 -0400

    Tests don't print to the console by default.
---
 src/test/java/org/apache/commons/xml/ShadingFootprintTest.java | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/test/java/org/apache/commons/xml/ShadingFootprintTest.java b/src/test/java/org/apache/commons/xml/ShadingFootprintTest.java
index 53ad525..0aea492 100644
--- a/src/test/java/org/apache/commons/xml/ShadingFootprintTest.java
+++ b/src/test/java/org/apache/commons/xml/ShadingFootprintTest.java
@@ -153,7 +153,9 @@ static void reportFootprint() {
             final long bytes = bytesOf(closure);
             report.append(String.format(Locale.ROOT, "  %-24s %2d classes  %7d bytes  %5.1f%%%n", entry, closure.size(), bytes, 100.0 * bytes / library));
         }
-        System.out.print(report);
+        if (Boolean.getBoolean(ShadingFootprintTest.class.getName() + ".reportFootprint")) {
+            System.out.print(report);
+        }
     }
 
     /**


(commons-xml) 07/08: Fail-fast in constructor on null delegate input.

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-xml.git

commit 641d267d7f403806a89a765be466ccdb36249c74
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Aug 26 08:18:32 2026 -0400

    Fail-fast in constructor on null delegate input.
    
    - As already done in some delegating classes.
    - Javadoc
    - "impl" -> "implementation"
---
 .../commons/xml/HardeningDocumentBuilder.java      |  9 +++++++-
 .../xml/HardeningDocumentBuilderFactory.java       | 10 +++++++-
 .../org/apache/commons/xml/HardeningSAXParser.java | 10 +++++++-
 .../commons/xml/HardeningSAXParserFactory.java     | 10 +++++++-
 .../org/apache/commons/xml/HardeningSchema.java    | 16 +++++++++----
 .../apache/commons/xml/HardeningSchemaFactory.java | 10 +++++++-
 .../org/apache/commons/xml/HardeningTemplates.java | 27 ++++++++++++++++------
 .../commons/xml/HardeningTemplatesHandler.java     | 19 ++++++++++++---
 .../apache/commons/xml/HardeningTransformer.java   | 27 +++++++++++++++-------
 .../commons/xml/HardeningTransformerFactory.java   | 24 ++++++++++++++++---
 .../commons/xml/HardeningTransformerHandler.java   | 15 ++++++++++--
 .../org/apache/commons/xml/HardeningValidator.java |  9 +++++++-
 .../commons/xml/HardeningValidatorHandler.java     | 10 +++++++-
 .../org/apache/commons/xml/HardeningXMLFilter.java |  9 +++++++-
 .../commons/xml/HardeningXMLInputFactory.java      |  9 +++++++-
 .../org/apache/commons/xml/HardeningXMLReader.java |  9 +++++++-
 .../org/apache/commons/xml/HardeningXPath.java     |  6 +++++
 .../commons/xml/HardeningXPathExpression.java      | 10 +++++++-
 .../apache/commons/xml/HardeningXPathFactory.java  |  6 +++++
 .../apache/commons/xml/TransformerHardener.java    |  2 +-
 20 files changed, 208 insertions(+), 39 deletions(-)

diff --git a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java
index 93cbdb8..7c85944 100644
--- a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java
+++ b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java
@@ -18,6 +18,7 @@
 package org.apache.commons.xml;
 
 import java.io.IOException;
+import java.util.Objects;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.validation.Schema;
@@ -42,8 +43,14 @@ final class HardeningDocumentBuilder extends DocumentBuilder {
 
     private final FallbackIgnoreEntityResolver2 floor = new FallbackIgnoreEntityResolver2(null);
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningDocumentBuilder(final DocumentBuilder delegate) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
         delegate.setEntityResolver(floor);
     }
 
diff --git a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java
index 59c066e..19a6ec3 100644
--- a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java
@@ -17,6 +17,8 @@
 
 package org.apache.commons.xml;
 
+import java.util.Objects;
+
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
@@ -35,8 +37,14 @@ final class HardeningDocumentBuilderFactory extends DocumentBuilderFactory {
 
     private final DocumentBuilderFactory delegate;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningDocumentBuilderFactory(final DocumentBuilderFactory delegate) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
     }
 
     @Override
diff --git a/src/main/java/org/apache/commons/xml/HardeningSAXParser.java b/src/main/java/org/apache/commons/xml/HardeningSAXParser.java
index 9fb396a..8abdc8a 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSAXParser.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSAXParser.java
@@ -17,6 +17,8 @@
 
 package org.apache.commons.xml;
 
+import java.util.Objects;
+
 import javax.xml.parsers.SAXParser;
 import javax.xml.validation.Schema;
 
@@ -45,8 +47,14 @@ final class HardeningSAXParser extends SAXParser {
     private XMLReader hardenedReader;
     private Parser hardenedParser;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningSAXParser(final SAXParser delegate) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
     }
 
     @Override
diff --git a/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java b/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java
index f1fff11..0454f15 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java
@@ -17,6 +17,8 @@
 
 package org.apache.commons.xml;
 
+import java.util.Objects;
+
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.parsers.SAXParser;
 import javax.xml.parsers.SAXParserFactory;
@@ -38,8 +40,14 @@ final class HardeningSAXParserFactory extends SAXParserFactory {
 
     private final SAXParserFactory delegate;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningSAXParserFactory(final SAXParserFactory delegate) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
     }
 
     @Override
diff --git a/src/main/java/org/apache/commons/xml/HardeningSchema.java b/src/main/java/org/apache/commons/xml/HardeningSchema.java
index f3294c1..fe2166d 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSchema.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSchema.java
@@ -17,22 +17,30 @@
 
 package org.apache.commons.xml;
 
+import java.util.Objects;
+
 import javax.xml.validation.Schema;
 import javax.xml.validation.Validator;
 import javax.xml.validation.ValidatorHandler;
 
 /**
  * {@link Schema} wrapper that hardens every {@link Validator} and {@link ValidatorHandler} the inner Schema produces: each {@link Validator} is wrapped in
- * {@link HardeningValidator} (which rewrites the Source through {@link SAXParserHardener#hardenSource(javax.xml.transform.Source)} and installs the resolver floor), and
- * each {@link ValidatorHandler} is wrapped in a {@link HardeningValidatorHandler} that keeps the same ignore-all resolver floor so {@code xsi:schemaLocation} is
- * not resolved during SAX-driven validation.
+ * {@link HardeningValidator} (which rewrites the Source through {@link SAXParserHardener#hardenSource(javax.xml.transform.Source)} and installs the resolver
+ * floor), and each {@link ValidatorHandler} is wrapped in a {@link HardeningValidatorHandler} that keeps the same ignore-all resolver floor so
+ * {@code xsi:schemaLocation} is not resolved during SAX-driven validation.
  */
 final class HardeningSchema extends Schema {
 
     private final Schema delegate;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningSchema(final Schema delegate) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
     }
 
     @Override
diff --git a/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java b/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java
index 573b000..c517265 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java
@@ -17,6 +17,8 @@
 
 package org.apache.commons.xml;
 
+import java.util.Objects;
+
 import javax.xml.transform.Source;
 import javax.xml.transform.TransformerConfigurationException;
 import javax.xml.validation.Schema;
@@ -69,8 +71,14 @@ private static Source[] harden(final Source[] schemas) throws SAXException {
 
     private final FallbackIgnoreLSResourceResolver floor = new FallbackIgnoreLSResourceResolver(null);
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningSchemaFactory(final SchemaFactory delegate) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
         // Compile-time block for xs:import/include/redefine; the wrappers carry the rest (per-product resolver, source rewriting, limits via the reader).
         delegate.setResourceResolver(floor);
     }
diff --git a/src/main/java/org/apache/commons/xml/HardeningTemplates.java b/src/main/java/org/apache/commons/xml/HardeningTemplates.java
index 0208c11..7706a49 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTemplates.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTemplates.java
@@ -17,6 +17,7 @@
 
 package org.apache.commons.xml;
 
+import java.util.Objects;
 import java.util.Properties;
 import java.util.function.Supplier;
 
@@ -29,24 +30,37 @@
 /**
  * {@link Templates} wrapper whose only purpose is to return a {@link HardeningTransformer} from {@link Templates#newTransformer()}, with the factory's
  * compile-time {@link URIResolver} pre-installed.
- *
- * <p>Both Apache Xalan 2.7 and stock-JDK XSLTC fail to propagate the factory's URIResolver through {@code Templates.newTransformer()}: the produced runtime
+ * <p>
+ * Both Apache Xalan 2.7 and stock-JDK XSLTC fail to propagate the factory's URIResolver through {@code Templates.newTransformer()}: the produced runtime
  * Transformer has a null URIResolver unless the caller sets one, leaving runtime {@code document()} calls unguarded. Snapshotting the resolver at compile time
  * and restoring it onto the runtime Transformer matches the JAXP-conformant intuition that the factory's resolver is the default for any Transformer the
- * factory ultimately produces.</p>
+ * factory ultimately produces.
+ * </p>
  */
 final class HardeningTemplates implements Templates {
 
     private final Templates delegate;
 
-    /** Compile-time URIResolver snapshot; the underlying impl does not propagate the factory's resolver onto Transformers obtained from Templates. */
+    /**
+     * Compile-time URIResolver snapshot; the underlying implementation does not propagate the factory's resolver onto Transformers obtained from Templates.
+     */
     private final URIResolver uriResolver;
 
-    /** Empty-{@link Source} supplier for the produced Transformer's floor; {@code null} means the default empty DOM. */
+    /**
+     * Empty-{@link Source} supplier for the produced Transformer's floor; {@code null} means the default empty DOM.
+     */
     private final Supplier<Source> emptySource;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate    the delegate to wrap; must not be {@code null}.
+     * @param uriResolver the compile-time URIResolver snapshot to restore onto Transformers produced from the compiled Templates; may be {@code null}.
+     * @param emptySource the empty-{@link Source} supplier for the produced Transformers
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningTemplates(final Templates delegate, final URIResolver uriResolver, final Supplier<Source> emptySource) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
         this.uriResolver = uriResolver;
         this.emptySource = emptySource;
     }
@@ -65,7 +79,6 @@ public Properties getOutputProperties() {
         return delegate.getOutputProperties();
     }
 
-
     @Override
     public Transformer newTransformer() throws TransformerConfigurationException {
         final Transformer transformer = delegate.newTransformer();
diff --git a/src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java b/src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java
index 04124a7..219fcab 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java
@@ -17,6 +17,7 @@
 
 package org.apache.commons.xml;
 
+import java.util.Objects;
 import java.util.function.Supplier;
 
 import javax.xml.transform.Source;
@@ -40,14 +41,26 @@ final class HardeningTemplatesHandler implements TemplatesHandler {
 
     private final TemplatesHandler delegate;
 
-    /** Compile-time URIResolver snapshot, restored onto Transformers produced from the compiled Templates. */
+    /**
+     * Compile-time URIResolver snapshot, restored onto Transformers produced from the compiled Templates.
+     */
     private final URIResolver uriResolver;
 
-    /** Empty-{@link Source} supplier for the produced Templates' floor; {@code null} means the default empty DOM. */
+    /**
+     * Empty-{@link Source} supplier for the produced Templates' floor; {@code null} means the default empty DOM.
+     */
     private final Supplier<Source> emptySource;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @param uriResolver the compile-time URIResolver snapshot to restore onto Transformers produced from the compiled Templates; may be {@code null}.
+     * @param emptySource the empty-{@link Source} supplier for the produced Templates.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningTemplatesHandler(final TemplatesHandler delegate, final URIResolver uriResolver, final Supplier<Source> emptySource) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
         this.uriResolver = uriResolver;
         this.emptySource = emptySource;
     }
diff --git a/src/main/java/org/apache/commons/xml/HardeningTransformer.java b/src/main/java/org/apache/commons/xml/HardeningTransformer.java
index c6b4288..1210f8f 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTransformer.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTransformer.java
@@ -17,6 +17,7 @@
 
 package org.apache.commons.xml;
 
+import java.util.Objects;
 import java.util.Properties;
 import java.util.function.Supplier;
 
@@ -30,24 +31,35 @@
 
 /**
  * {@link Transformer} wrapper that rewrites the Source on every {@link Transformer#transform(Source, Result)} call through
- * {@link SAXParserHardener#hardenSource(Source)} before delegating, and keeps an ignore-all {@link URIResolver} floor so runtime {@code document()} calls a caller does not
- * resolve return empty rather than being fetched.
- *
- * <p>The floor is installed on the delegate transformer at construction, seeded with the factory's compile-time resolver; {@link #setURIResolver(URIResolver)}
+ * {@link SAXParserHardener#hardenSource(Source)} before delegating, and keeps an ignore-all {@link URIResolver} floor so runtime {@code document()} calls a
+ * caller does not resolve return empty rather than being fetched.
+ * <p>
+ * The floor is installed on the delegate transformer at construction, seeded with the factory's compile-time resolver; {@link #setURIResolver(URIResolver)}
  * routes a caller's resolver through it rather than replacing it, so the block cannot be dropped. {@link #reset()} re-establishes the floor, seeded again with
- * the factory's compile-time resolver, matching the just-constructed state.</p>
+ * the factory's compile-time resolver, matching the just-constructed state.
+ * </p>
  */
 final class HardeningTransformer extends Transformer {
 
     private final Transformer delegate;
 
-    /** Compile-time URIResolver snapshot the floor is seeded with, both at construction and again on {@link #reset()}. */
+    /**
+     * Compile-time URIResolver snapshot the floor is seeded with, both at construction and again on {@link #reset()}.
+     */
     private final URIResolver uriResolver;
 
     private final FallbackIgnoreURIResolver floor;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate    the delegate to wrap; must not be {@code null}.
+     * @param uriResolver the compile-time URIResolver snapshot to seed the floor with; may be {@code null}.
+     * @param emptySource the empty-{@link Source} supplier for the produced Transformers
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningTransformer(final Transformer delegate, final URIResolver uriResolver, final Supplier<Source> emptySource) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
         this.uriResolver = uriResolver;
         this.floor = new FallbackIgnoreURIResolver(uriResolver, emptySource);
         delegate.setURIResolver(floor);
@@ -110,7 +122,6 @@ public void setParameter(final String name, final Object value) {
         delegate.setParameter(name, value);
     }
 
-
     @Override
     public void setURIResolver(final URIResolver resolver) {
         floor.setDelegate(resolver);
diff --git a/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java b/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
index 4aa0d37..5dd22c3 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
@@ -18,6 +18,7 @@
 package org.apache.commons.xml;
 
 import java.io.IOException;
+import java.util.Objects;
 import java.util.function.Supplier;
 
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -53,7 +54,8 @@
  * <ol>
  *   <li>{@link HardeningTransformerFactory} rewrites the Source on every entry point that compiles a stylesheet or transforms a one-shot input.</li>
  *   <li>{@link HardeningTemplates} returns a {@link HardeningTransformer} from {@link Templates#newTransformer()} so runtime source parsing is also covered, and
- *       restores the factory's URIResolver onto the produced Transformer (which the underlying impl typically does not propagate through {@code Templates}).</li>
+ *       restores the factory's URIResolver onto the produced Transformer (which the underlying implementation typically does not propagate through
+ *       {@code Templates}).</li>
  *   <li>{@link HardeningTransformer} rewrites the Source on every {@link Transformer#transform(Source, javax.xml.transform.Result)} call.</li>
  * </ol>
  *
@@ -113,17 +115,33 @@ private static Templates unwrap(final Templates templates) {
 
     private final SAXTransformerFactory delegate;
 
-    /** Empty-{@link Source} supplier for the resolver floor, threaded onto every produced Templates/Transformer; {@code null} means the default empty DOM. */
+    /**
+     * Empty-{@link Source} supplier for the resolver floor, threaded onto every produced Templates/Transformer; {@code null} means the default empty DOM.
+     */
     private final Supplier<Source> emptySource;
 
     private final FallbackIgnoreURIResolver floor;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningTransformerFactory(final SAXTransformerFactory delegate) {
         this(delegate, null);
     }
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate    the delegate to wrap; must not be {@code null}.
+     * @param emptySource the empty-{@link Source} supplier for the resolver floor, threaded onto every produced Templates/Transformer; {@code null} means the
+     *                    default empty DOM.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningTransformerFactory(final SAXTransformerFactory delegate, final Supplier<Source> emptySource) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
         this.emptySource = emptySource;
         this.floor = new FallbackIgnoreURIResolver(null, emptySource);
         // Compile-time block for xsl:import/xsl:include and document(); a caller-set resolver is routed through the floor rather than replacing it.
diff --git a/src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java b/src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java
index 1b0f5b1..69000e9 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java
@@ -17,6 +17,7 @@
 
 package org.apache.commons.xml;
 
+import java.util.Objects;
 import java.util.function.Supplier;
 
 import javax.xml.transform.Result;
@@ -42,11 +43,21 @@ final class HardeningTransformerHandler implements TransformerHandler {
 
     private final TransformerHandler delegate;
 
-    /** Wraps the handler's LIVE transformer; constructing it installs the resolver floor that the handler's own transform then runs under. */
+    /**
+     * Wraps the handler's LIVE transformer; constructing it installs the resolver floor that the handler's own transform then runs under.
+     */
     private final HardeningTransformer transformer;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @param uriResolver the compile-time URIResolver snapshot to restore onto the live transformer; may be {@code null}.
+     * @param emptySource the empty-{@link Source} supplier for the produced Transformer's floor; {@code null} means the default empty DOM.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningTransformerHandler(final TransformerHandler delegate, final URIResolver uriResolver, final Supplier<Source> emptySource) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
         this.transformer = new HardeningTransformer(delegate.getTransformer(), uriResolver, emptySource);
     }
 
diff --git a/src/main/java/org/apache/commons/xml/HardeningValidator.java b/src/main/java/org/apache/commons/xml/HardeningValidator.java
index 730230f..96d651b 100644
--- a/src/main/java/org/apache/commons/xml/HardeningValidator.java
+++ b/src/main/java/org/apache/commons/xml/HardeningValidator.java
@@ -18,6 +18,7 @@
 package org.apache.commons.xml;
 
 import java.io.IOException;
+import java.util.Objects;
 
 import javax.xml.transform.Result;
 import javax.xml.transform.Source;
@@ -41,8 +42,14 @@ final class HardeningValidator extends Validator {
 
     private final FallbackIgnoreLSResourceResolver floor = new FallbackIgnoreLSResourceResolver(null);
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningValidator(final Validator delegate) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
         // Block xsi:schemaLocation resolution; neither the JDK nor Xerces reliably propagates the factory's resolver to its Validators. The floor is a
         // non-removable lower bound: a caller opts specific lookups in by setting their own resolver, but cannot drop the ignore-all block.
         delegate.setResourceResolver(floor);
diff --git a/src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java b/src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java
index 2cc80b7..5961b6e 100644
--- a/src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java
+++ b/src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java
@@ -17,6 +17,8 @@
 
 package org.apache.commons.xml;
 
+import java.util.Objects;
+
 import javax.xml.validation.TypeInfoProvider;
 import javax.xml.validation.ValidatorHandler;
 
@@ -41,8 +43,14 @@ final class HardeningValidatorHandler extends ValidatorHandler {
 
     private final FallbackIgnoreLSResourceResolver floor = new FallbackIgnoreLSResourceResolver(null);
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningValidatorHandler(final ValidatorHandler delegate) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
         delegate.setResourceResolver(floor);
     }
 
diff --git a/src/main/java/org/apache/commons/xml/HardeningXMLFilter.java b/src/main/java/org/apache/commons/xml/HardeningXMLFilter.java
index eac2f13..05de4f7 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXMLFilter.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXMLFilter.java
@@ -18,6 +18,7 @@
 package org.apache.commons.xml;
 
 import java.io.IOException;
+import java.util.Objects;
 
 import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerException;
@@ -44,8 +45,14 @@ final class HardeningXMLFilter extends XMLFilterImpl {
 
     private final HardeningTemplates templates;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param templates the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningXMLFilter(final HardeningTemplates templates) {
-        this.templates = templates;
+        this.templates = Objects.requireNonNull(templates, "templates");
     }
 
     @Override
diff --git a/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java b/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
index ecdc2e4..a0635b6 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
@@ -19,6 +19,7 @@
 
 import java.io.InputStream;
 import java.io.Reader;
+import java.util.Objects;
 
 import javax.xml.stream.EventFilter;
 import javax.xml.stream.StreamFilter;
@@ -64,8 +65,14 @@ private static XMLResolver unwrap(final XMLResolver resolver) {
 
     private final XMLInputFactory delegate;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningXMLInputFactory(final XMLInputFactory delegate) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
         delegate.setXMLResolver(new FallbackIgnoreXMLResolver(null));
     }
 
diff --git a/src/main/java/org/apache/commons/xml/HardeningXMLReader.java b/src/main/java/org/apache/commons/xml/HardeningXMLReader.java
index 61d2b35..856aa67 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXMLReader.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXMLReader.java
@@ -18,6 +18,7 @@
 package org.apache.commons.xml;
 
 import java.io.IOException;
+import java.util.Objects;
 
 import org.xml.sax.ContentHandler;
 import org.xml.sax.DTDHandler;
@@ -45,8 +46,14 @@ class HardeningXMLReader implements XMLReader {
 
     private final FallbackIgnoreEntityResolver2 floor;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningXMLReader(final XMLReader delegate) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
         this.floor = new FallbackIgnoreEntityResolver2(null);
         delegate.setEntityResolver(floor);
     }
diff --git a/src/main/java/org/apache/commons/xml/HardeningXPath.java b/src/main/java/org/apache/commons/xml/HardeningXPath.java
index 13cef3a..80eddf6 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXPath.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXPath.java
@@ -73,6 +73,12 @@ static Document parse(final InputSource source) throws XPathExpressionException
 
     private final XPath delegate;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningXPath(final XPath delegate) {
         this.delegate = Objects.requireNonNull(delegate, "delegate");
     }
diff --git a/src/main/java/org/apache/commons/xml/HardeningXPathExpression.java b/src/main/java/org/apache/commons/xml/HardeningXPathExpression.java
index 7a1446f..15de2bd 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXPathExpression.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXPathExpression.java
@@ -17,6 +17,8 @@
 
 package org.apache.commons.xml;
 
+import java.util.Objects;
+
 import javax.xml.namespace.QName;
 import javax.xml.xpath.XPathExpression;
 import javax.xml.xpath.XPathExpressionException;
@@ -34,8 +36,14 @@ final class HardeningXPathExpression implements XPathExpression {
 
     private final XPathExpression delegate;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningXPathExpression(final XPathExpression delegate) {
-        this.delegate = delegate;
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
     }
 
     @Override
diff --git a/src/main/java/org/apache/commons/xml/HardeningXPathFactory.java b/src/main/java/org/apache/commons/xml/HardeningXPathFactory.java
index a697032..6668057 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXPathFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXPathFactory.java
@@ -36,6 +36,12 @@ final class HardeningXPathFactory extends XPathFactory {
 
     private final XPathFactory delegate;
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param delegate the delegate to wrap; must not be {@code null}.
+     * @throws NullPointerException if {@code delegate} is {@code null}.
+     */
     HardeningXPathFactory(final XPathFactory delegate) {
         this.delegate = Objects.requireNonNull(delegate, "delegate");
     }
diff --git a/src/main/java/org/apache/commons/xml/TransformerHardener.java b/src/main/java/org/apache/commons/xml/TransformerHardener.java
index 6dbb623..5b937b5 100644
--- a/src/main/java/org/apache/commons/xml/TransformerHardener.java
+++ b/src/main/java/org/apache/commons/xml/TransformerHardener.java
@@ -51,7 +51,7 @@ static TransformerFactory harden(final TransformerFactory factory) {
         setFeature(factory, XMLConstants.FEATURE_SECURE_PROCESSING, true);
         if (SaxonProvider.isSaxon(factory.getClass())) {
             // Saxon keeps its vendor Configuration for the channels JAXP cannot close,
-            // then goes through the same wrapper as every other impl for the URIResolver floor;
+            // then goes through the same wrapper as every other implementation for the URIResolver floor;
             // EmptySource is the empty-source shape Saxon's consumers expect.
             return new HardeningTransformerFactory((SAXTransformerFactory) SaxonProvider.configure(factory), SaxonProvider.emptySourceSupplier());
         }


(commons-xml) 01/08: Sort members

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-xml.git

commit f6583c76edb116407510971ba27c09abb14956a5
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Aug 26 07:23:51 2026 -0400

    Sort members
---
 .../commons/xml/FallbackIgnoreEntityResolver2.java |  50 +--
 .../xml/FallbackIgnoreLSResourceResolver.java      |  14 +-
 .../commons/xml/FallbackIgnoreURIResolver.java     |   8 +-
 .../commons/xml/FallbackIgnoreXMLResolver.java     |   8 +-
 .../commons/xml/HardeningDocumentBuilder.java      |  42 +--
 .../xml/HardeningDocumentBuilderFactory.java       |  10 +-
 .../org/apache/commons/xml/HardeningException.java |  30 +-
 .../org/apache/commons/xml/HardeningSAXParser.java |  34 +-
 .../commons/xml/HardeningSAXParserFactory.java     |  10 +-
 .../apache/commons/xml/HardeningSchemaFactory.java |  62 ++--
 .../org/apache/commons/xml/HardeningTemplates.java |  14 +-
 .../commons/xml/HardeningTemplatesHandler.java     |  64 ++--
 .../apache/commons/xml/HardeningTransformer.java   |  52 +--
 .../commons/xml/HardeningTransformerFactory.java   | 142 ++++----
 .../commons/xml/HardeningTransformerHandler.java   |  98 +++---
 .../commons/xml/HardeningValidatorHandler.java     |  88 ++---
 .../commons/xml/HardeningXMLInputFactory.java      | 168 +++++-----
 .../org/apache/commons/xml/HardeningXMLReader.java |  20 +-
 .../org/apache/commons/xml/HardeningXPath.java     |  12 +-
 .../apache/commons/xml/HardeningXPathFactory.java  |  12 +-
 .../java/org/apache/commons/xml/SaxonProvider.java |  42 +--
 .../commons/xml/AssociatedStylesheetTest.java      |  16 +-
 .../org/apache/commons/xml/AttackTestSupport.java  |  38 +--
 .../org/apache/commons/xml/DenyUnresolvedTest.java |  10 +-
 .../commons/xml/EntityResolverFloorTest.java       | 372 ++++++++++-----------
 .../org/apache/commons/xml/ResetHardeningTest.java |  28 +-
 .../apache/commons/xml/SaxSurfaceTestSupport.java  |  26 +-
 .../commons/xml/SaxonAlternateFactoryTest.java     |  18 +-
 .../apache/commons/xml/SchemaLocationDomTest.java  |  52 +--
 .../commons/xml/SchemaLocationPropertyTest.java    |  76 ++---
 .../apache/commons/xml/SchemaLocationSaxTest.java  |  56 ++--
 .../apache/commons/xml/ShadingFootprintTest.java   | 120 +++----
 .../apache/commons/xml/TemplatesHandlerTest.java   |  14 +-
 .../apache/commons/xml/TransformerHandlerTest.java |  20 +-
 .../xml/UnsupportedXmlImplementationTest.java      |  30 +-
 .../java/org/apache/commons/xml/XIncludeTest.java  | 194 +++++------
 .../java/org/apache/commons/xml/XMLFilterTest.java |  48 +--
 .../apache/commons/xml/XPathInputSourceTest.java   |  14 +-
 .../org/apache/commons/xml/XmlFactoriesTest.java   |  44 +--
 39 files changed, 1078 insertions(+), 1078 deletions(-)

diff --git a/src/main/java/org/apache/commons/xml/FallbackIgnoreEntityResolver2.java b/src/main/java/org/apache/commons/xml/FallbackIgnoreEntityResolver2.java
index dc04506..0b036b9 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreEntityResolver2.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreEntityResolver2.java
@@ -52,11 +52,6 @@
  */
 class FallbackIgnoreEntityResolver2 extends DefaultHandler2 {
 
-    /**
-     * Caller-supplied resolver consulted first, or {@code null} for a pure ignore-all floor.
-     */
-    private EntityResolver delegate;
-
     /**
      * Resolves {@code systemId} against {@code baseURI}.
      *
@@ -76,16 +71,12 @@ private static String absolutize(final String baseURI, final String systemId) {
         }
     }
 
-    FallbackIgnoreEntityResolver2(final EntityResolver delegate) {
-        this.delegate = delegate;
-    }
-
     /**
-     * Replaces the caller resolver consulted ahead of the floor; lets a single floor instance back successive {@code setEntityResolver} calls.
-     *
-     * @param delegate The caller-supplied resolver, or {@code null} for a pure ignore-all floor.
+     * Caller-supplied resolver consulted first, or {@code null} for a pure ignore-all floor.
      */
-    final void setDelegate(final EntityResolver delegate) {
+    private EntityResolver delegate;
+
+    FallbackIgnoreEntityResolver2(final EntityResolver delegate) {
         this.delegate = delegate;
     }
 
@@ -93,18 +84,6 @@ final EntityResolver getDelegate() {
         return delegate;
     }
 
-    @Override
-    public final InputSource resolveEntity(final String publicId, final String systemId) throws SAXException, IOException {
-        return resolveEntity(null, publicId, null, systemId);
-    }
-
-    @Override
-    public final InputSource resolveEntity(final String name, final String publicId, final String baseURI, final String systemId)
-            throws SAXException, IOException {
-        final InputSource resolved = resolveWithDelegate(name, publicId, baseURI, systemId);
-        return resolved != null ? resolved : onUnresolved(name, publicId, baseURI, systemId);
-    }
-
     /**
      * Outcome when neither the caller delegate nor this resolver provides the entity. Resolves to empty content by default, so the external resource is neither
      * fetched nor leaked and the parse continues with no replacement text. The returned source echoes the requested identifiers (with {@code systemId}
@@ -129,6 +108,18 @@ protected InputSource onUnresolved(final String name, final String publicId, fin
         return empty;
     }
 
+    @Override
+    public final InputSource resolveEntity(final String publicId, final String systemId) throws SAXException, IOException {
+        return resolveEntity(null, publicId, null, systemId);
+    }
+
+    @Override
+    public final InputSource resolveEntity(final String name, final String publicId, final String baseURI, final String systemId)
+            throws SAXException, IOException {
+        final InputSource resolved = resolveWithDelegate(name, publicId, baseURI, systemId);
+        return resolved != null ? resolved : onUnresolved(name, publicId, baseURI, systemId);
+    }
+
     private InputSource resolveWithDelegate(final String name, final String publicId, final String baseURI,
             final String systemId) throws SAXException, IOException {
         if (delegate != null) {
@@ -138,4 +129,13 @@ private InputSource resolveWithDelegate(final String name, final String publicId
         }
         return null;
     }
+
+    /**
+     * Replaces the caller resolver consulted ahead of the floor; lets a single floor instance back successive {@code setEntityResolver} calls.
+     *
+     * @param delegate The caller-supplied resolver, or {@code null} for a pure ignore-all floor.
+     */
+    final void setDelegate(final EntityResolver delegate) {
+        this.delegate = delegate;
+    }
 }
diff --git a/src/main/java/org/apache/commons/xml/FallbackIgnoreLSResourceResolver.java b/src/main/java/org/apache/commons/xml/FallbackIgnoreLSResourceResolver.java
index ab70517..3b98643 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreLSResourceResolver.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreLSResourceResolver.java
@@ -38,12 +38,6 @@ final class FallbackIgnoreLSResourceResolver implements LSResourceResolver {
     /** DOM Level 3 Load/Save implementation used to build the empty input for unresolved lookups. */
     private static final DOMImplementationLS DOM_LS = domImplementationLS();
 
-    private LSResourceResolver delegate;
-
-    FallbackIgnoreLSResourceResolver(final LSResourceResolver delegate) {
-        this.delegate = delegate;
-    }
-
     private static DOMImplementationLS domImplementationLS() {
         try {
             return (DOMImplementationLS) DOMImplementationRegistry.newInstance().getDOMImplementation("LS");
@@ -52,7 +46,9 @@ private static DOMImplementationLS domImplementationLS() {
         }
     }
 
-    void setDelegate(final LSResourceResolver delegate) {
+    private LSResourceResolver delegate;
+
+    FallbackIgnoreLSResourceResolver(final LSResourceResolver delegate) {
         this.delegate = delegate;
     }
 
@@ -80,4 +76,8 @@ public LSInput resolveResource(final String type, final String namespaceURI, fin
         empty.setBaseURI(baseURI);
         return empty;
     }
+
+    void setDelegate(final LSResourceResolver delegate) {
+        this.delegate = delegate;
+    }
 }
diff --git a/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java b/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java
index b903606..a462872 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java
@@ -78,10 +78,6 @@ private static Document newEmptyDocument() {
         this.emptySource = emptySource != null ? emptySource : () -> new DOMSource(EMPTY_DOCUMENT);
     }
 
-    void setDelegate(final URIResolver delegate) {
-        this.delegate = delegate;
-    }
-
     URIResolver getDelegate() {
         return delegate;
     }
@@ -98,4 +94,8 @@ public Source resolve(final String href, final String base) throws TransformerEx
         }
         return emptySource.get();
     }
+
+    void setDelegate(final URIResolver delegate) {
+        this.delegate = delegate;
+    }
 }
diff --git a/src/main/java/org/apache/commons/xml/FallbackIgnoreXMLResolver.java b/src/main/java/org/apache/commons/xml/FallbackIgnoreXMLResolver.java
index 1242be1..94c24c8 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreXMLResolver.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreXMLResolver.java
@@ -45,10 +45,6 @@ final class FallbackIgnoreXMLResolver implements XMLResolver {
         this.delegate = delegate;
     }
 
-    void setDelegate(final XMLResolver delegate) {
-        this.delegate = delegate;
-    }
-
     XMLResolver getDelegate() {
         return delegate;
     }
@@ -64,4 +60,8 @@ public Object resolveEntity(final String publicID, final String systemID, final
         }
         return EMPTY;
     }
+
+    void setDelegate(final XMLResolver delegate) {
+        this.delegate = delegate;
+    }
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java
index 8343921..b9bf44e 100644
--- a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java
+++ b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java
@@ -48,22 +48,15 @@ final class HardeningDocumentBuilder extends DocumentBuilder {
     }
 
     @Override
-    public void setEntityResolver(final EntityResolver resolver) {
-        floor.setDelegate(resolver);
+    public DOMImplementation getDOMImplementation() {
+        return delegate.getDOMImplementation();
     }
 
     @Override
-    public void reset() {
-        delegate.reset();
-        floor.setDelegate(null);
-        delegate.setEntityResolver(floor);
-    }
-
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
-    @Override
-    public Document parse(final InputSource is) throws SAXException, IOException {
-        return delegate.parse(is);
+    public Schema getSchema() {
+        return delegate.getSchema();
     }
+    // </editor-fold>
 
     @Override
     public boolean isNamespaceAware() {
@@ -81,23 +74,30 @@ public boolean isXIncludeAware() {
     }
 
     @Override
-    public void setErrorHandler(final ErrorHandler eh) {
-        delegate.setErrorHandler(eh);
+    public Document newDocument() {
+        return delegate.newDocument();
     }
 
+    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
-    public Document newDocument() {
-        return delegate.newDocument();
+    public Document parse(final InputSource is) throws SAXException, IOException {
+        return delegate.parse(is);
     }
 
     @Override
-    public DOMImplementation getDOMImplementation() {
-        return delegate.getDOMImplementation();
+    public void reset() {
+        delegate.reset();
+        floor.setDelegate(null);
+        delegate.setEntityResolver(floor);
     }
 
     @Override
-    public Schema getSchema() {
-        return delegate.getSchema();
+    public void setEntityResolver(final EntityResolver resolver) {
+        floor.setDelegate(resolver);
+    }
+
+    @Override
+    public void setErrorHandler(final ErrorHandler eh) {
+        delegate.setErrorHandler(eh);
     }
-    // </editor-fold>
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java
index 2ae105f..6a18dbd 100644
--- a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java
@@ -39,11 +39,6 @@ final class HardeningDocumentBuilderFactory extends DocumentBuilderFactory {
         this.delegate = delegate;
     }
 
-    @Override
-    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
-        return new HardeningDocumentBuilder(delegate.newDocumentBuilder());
-    }
-
     // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public Object getAttribute(final String name) {
@@ -95,6 +90,11 @@ public boolean isXIncludeAware() {
         return delegate.isXIncludeAware();
     }
 
+    @Override
+    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
+        return new HardeningDocumentBuilder(delegate.newDocumentBuilder());
+    }
+
     @Override
     public void setAttribute(final String name, final Object value) {
         delegate.setAttribute(name, value);
diff --git a/src/main/java/org/apache/commons/xml/HardeningException.java b/src/main/java/org/apache/commons/xml/HardeningException.java
index bc7c1de..4dd123c 100644
--- a/src/main/java/org/apache/commons/xml/HardeningException.java
+++ b/src/main/java/org/apache/commons/xml/HardeningException.java
@@ -35,8 +35,19 @@ final class HardeningException extends IllegalStateException {
 
     private static final long serialVersionUID = 1L;
 
-    HardeningException(final String message, final Throwable cause) {
-        super(message, cause);
+    /**
+     * Builds the standard "forbidden" message shared by every resolver floor when {@link #throwOnUnresolved()} rejects an unresolved reference.
+     *
+     * @param type      the resource kind, or {@code null} if not applicable.
+     * @param namespace the namespace (or, for Woodstox, the entity name), or {@code null}.
+     * @param publicId  the public identifier, or {@code null} if none.
+     * @param systemId  the system identifier of the denied resource.
+     * @param baseURI   the base URI for relative resolution, or {@code null}.
+     * @return the message naming the denied lookup and the enabling property.
+     */
+    static String forbidden(final String type, final String namespace, final String publicId, final String systemId, final String baseURI) {
+        return String.format("External resource fetch forbidden by %s: type=%s, namespace=%s, publicId=%s, systemId=%s, baseURI=%s",
+                XmlFactories.THROW_ON_UNRESOLVED, type, namespace, publicId, systemId, baseURI);
     }
 
     /**
@@ -63,18 +74,7 @@ static boolean throwOnUnresolved() {
         return Boolean.getBoolean(XmlFactories.THROW_ON_UNRESOLVED);
     }
 
-    /**
-     * Builds the standard "forbidden" message shared by every resolver floor when {@link #throwOnUnresolved()} rejects an unresolved reference.
-     *
-     * @param type      the resource kind, or {@code null} if not applicable.
-     * @param namespace the namespace (or, for Woodstox, the entity name), or {@code null}.
-     * @param publicId  the public identifier, or {@code null} if none.
-     * @param systemId  the system identifier of the denied resource.
-     * @param baseURI   the base URI for relative resolution, or {@code null}.
-     * @return the message naming the denied lookup and the enabling property.
-     */
-    static String forbidden(final String type, final String namespace, final String publicId, final String systemId, final String baseURI) {
-        return String.format("External resource fetch forbidden by %s: type=%s, namespace=%s, publicId=%s, systemId=%s, baseURI=%s",
-                XmlFactories.THROW_ON_UNRESOLVED, type, namespace, publicId, systemId, baseURI);
+    HardeningException(final String message, final Throwable cause) {
+        super(message, cause);
     }
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningSAXParser.java b/src/main/java/org/apache/commons/xml/HardeningSAXParser.java
index 5a79e74..5e176b7 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSAXParser.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSAXParser.java
@@ -49,14 +49,6 @@ final class HardeningSAXParser extends SAXParser {
         this.delegate = delegate;
     }
 
-    @Override
-    public XMLReader getXMLReader() throws SAXException {
-        if (hardenedReader == null) {
-            hardenedReader = SAXParserHardener.hardenReader(delegate.getXMLReader());
-        }
-        return hardenedReader;
-    }
-
     @Override
     @SuppressWarnings("deprecation")
     public Parser getParser() throws SAXException {
@@ -68,15 +60,6 @@ public Parser getParser() throws SAXException {
         return hardenedParser;
     }
 
-    @Override
-    public void reset() {
-        delegate.reset();
-        // The JAXP reset contract reverts the delegate to its just-created state, which strips the post-creation reader hardening.
-        // We reset the cached readers, so hardening can be applied again.
-        hardenedReader = null;
-        hardenedParser = null;
-    }
-
     // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public Object getProperty(final String name) throws SAXNotRecognizedException, SAXNotSupportedException {
@@ -88,6 +71,14 @@ public Schema getSchema() {
         return delegate.getSchema();
     }
 
+    @Override
+    public XMLReader getXMLReader() throws SAXException {
+        if (hardenedReader == null) {
+            hardenedReader = SAXParserHardener.hardenReader(delegate.getXMLReader());
+        }
+        return hardenedReader;
+    }
+
     @Override
     public boolean isNamespaceAware() {
         return delegate.isNamespaceAware();
@@ -103,6 +94,15 @@ public boolean isXIncludeAware() {
         return delegate.isXIncludeAware();
     }
 
+    @Override
+    public void reset() {
+        delegate.reset();
+        // The JAXP reset contract reverts the delegate to its just-created state, which strips the post-creation reader hardening.
+        // We reset the cached readers, so hardening can be applied again.
+        hardenedReader = null;
+        hardenedParser = null;
+    }
+
     @Override
     public void setProperty(final String name, final Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
         delegate.setProperty(name, value);
diff --git a/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java b/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java
index e0ea507..19f79a9 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java
@@ -42,11 +42,6 @@ final class HardeningSAXParserFactory extends SAXParserFactory {
         this.delegate = delegate;
     }
 
-    @Override
-    public SAXParser newSAXParser() throws ParserConfigurationException, SAXException {
-        return new HardeningSAXParser(delegate.newSAXParser());
-    }
-
     // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public boolean getFeature(final String name) throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException {
@@ -73,6 +68,11 @@ public boolean isXIncludeAware() {
         return delegate.isXIncludeAware();
     }
 
+    @Override
+    public SAXParser newSAXParser() throws ParserConfigurationException, SAXException {
+        return new HardeningSAXParser(delegate.newSAXParser());
+    }
+
     @Override
     public void setFeature(final String name, final boolean value) throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException {
         delegate.setFeature(name, value);
diff --git a/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java b/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java
index a21ee58..31cc9c0 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java
@@ -53,37 +53,6 @@
  */
 final class HardeningSchemaFactory extends SchemaFactory {
 
-    private final SchemaFactory delegate;
-
-    private final FallbackIgnoreLSResourceResolver floor = new FallbackIgnoreLSResourceResolver(null);
-
-    HardeningSchemaFactory(final SchemaFactory delegate) {
-        this.delegate = delegate;
-        // Compile-time block for xs:import/include/redefine; the wrappers carry the rest (per-product resolver, source rewriting, limits via the reader).
-        delegate.setResourceResolver(floor);
-    }
-
-    @Override
-    public void setResourceResolver(final LSResourceResolver resourceResolver) {
-        // Route a caller resolver through the floor instead of replacing it, so the ignore-all lower bound cannot be removed.
-        floor.setDelegate(resourceResolver);
-    }
-
-    @Override
-    public LSResourceResolver getResourceResolver() {
-        return floor.getDelegate();
-    }
-
-    @Override
-    public Schema newSchema() throws SAXException {
-        return new HardeningSchema(delegate.newSchema());
-    }
-
-    @Override
-    public Schema newSchema(final Source[] schemas) throws SAXException {
-        return new HardeningSchema(delegate.newSchema(harden(schemas)));
-    }
-
     private static Source[] harden(final Source[] schemas) throws SAXException {
         final Source[] hardened = new Source[schemas.length];
         try {
@@ -96,6 +65,16 @@ private static Source[] harden(final Source[] schemas) throws SAXException {
         return hardened;
     }
 
+    private final SchemaFactory delegate;
+
+    private final FallbackIgnoreLSResourceResolver floor = new FallbackIgnoreLSResourceResolver(null);
+
+    HardeningSchemaFactory(final SchemaFactory delegate) {
+        this.delegate = delegate;
+        // Compile-time block for xs:import/include/redefine; the wrappers carry the rest (per-product resolver, source rewriting, limits via the reader).
+        delegate.setResourceResolver(floor);
+    }
+
     // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public ErrorHandler getErrorHandler() {
@@ -112,11 +91,26 @@ public Object getProperty(final String name) throws SAXNotRecognizedException, S
         return delegate.getProperty(name);
     }
 
+    @Override
+    public LSResourceResolver getResourceResolver() {
+        return floor.getDelegate();
+    }
+
     @Override
     public boolean isSchemaLanguageSupported(final String schemaLanguage) {
         return delegate.isSchemaLanguageSupported(schemaLanguage);
     }
 
+    @Override
+    public Schema newSchema() throws SAXException {
+        return new HardeningSchema(delegate.newSchema());
+    }
+
+    @Override
+    public Schema newSchema(final Source[] schemas) throws SAXException {
+        return new HardeningSchema(delegate.newSchema(harden(schemas)));
+    }
+
     @Override
     public void setErrorHandler(final ErrorHandler errorHandler) {
         delegate.setErrorHandler(errorHandler);
@@ -132,4 +126,10 @@ public void setProperty(final String name, final Object object) throws SAXNotRec
         delegate.setProperty(name, object);
     }
     // </editor-fold>
+
+    @Override
+    public void setResourceResolver(final LSResourceResolver resourceResolver) {
+        // Route a caller resolver through the floor instead of replacing it, so the ignore-all lower bound cannot be removed.
+        floor.setDelegate(resourceResolver);
+    }
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningTemplates.java b/src/main/java/org/apache/commons/xml/HardeningTemplates.java
index f598aa8..af8ceeb 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTemplates.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTemplates.java
@@ -60,6 +60,13 @@ Templates getDelegate() {
         return delegate;
     }
 
+    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
+    @Override
+    public Properties getOutputProperties() {
+        return delegate.getOutputProperties();
+    }
+    // </editor-fold>
+
     @Override
     public Transformer newTransformer() throws TransformerConfigurationException {
         final Transformer transformer = delegate.newTransformer();
@@ -68,11 +75,4 @@ public Transformer newTransformer() throws TransformerConfigurationException {
         }
         return new HardeningTransformer(transformer, uriResolver, emptySource);
     }
-
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
-    @Override
-    public Properties getOutputProperties() {
-        return delegate.getOutputProperties();
-    }
-    // </editor-fold>
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java b/src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java
index dfcddaa..9600ac1 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java
@@ -53,76 +53,76 @@ final class HardeningTemplatesHandler implements TemplatesHandler {
     }
 
     @Override
-    public Templates getTemplates() {
-        // Null before the stylesheet's endDocument (and on a failed compile in some implementations).
-        final Templates templates = delegate.getTemplates();
-        return templates == null ? null : new HardeningTemplates(templates, uriResolver, emptySource);
+    public void characters(final char[] ch, final int start, final int length) throws SAXException {
+        delegate.characters(ch, start, length);
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
-    public void setSystemId(final String systemID) {
-        delegate.setSystemId(systemID);
+    public void endDocument() throws SAXException {
+        delegate.endDocument();
     }
 
     @Override
-    public String getSystemId() {
-        return delegate.getSystemId();
+    public void endElement(final String uri, final String localName, final String qName) throws SAXException {
+        delegate.endElement(uri, localName, qName);
     }
 
     @Override
-    public void setDocumentLocator(final Locator locator) {
-        delegate.setDocumentLocator(locator);
+    public void endPrefixMapping(final String prefix) throws SAXException {
+        delegate.endPrefixMapping(prefix);
     }
 
     @Override
-    public void startDocument() throws SAXException {
-        delegate.startDocument();
+    public String getSystemId() {
+        return delegate.getSystemId();
     }
 
     @Override
-    public void endDocument() throws SAXException {
-        delegate.endDocument();
+    public Templates getTemplates() {
+        // Null before the stylesheet's endDocument (and on a failed compile in some implementations).
+        final Templates templates = delegate.getTemplates();
+        return templates == null ? null : new HardeningTemplates(templates, uriResolver, emptySource);
     }
 
     @Override
-    public void startPrefixMapping(final String prefix, final String uri) throws SAXException {
-        delegate.startPrefixMapping(prefix, uri);
+    public void ignorableWhitespace(final char[] ch, final int start, final int length) throws SAXException {
+        delegate.ignorableWhitespace(ch, start, length);
     }
 
     @Override
-    public void endPrefixMapping(final String prefix) throws SAXException {
-        delegate.endPrefixMapping(prefix);
+    public void processingInstruction(final String target, final String data) throws SAXException {
+        delegate.processingInstruction(target, data);
     }
 
     @Override
-    public void startElement(final String uri, final String localName, final String qName, final Attributes atts) throws SAXException {
-        delegate.startElement(uri, localName, qName, atts);
+    public void setDocumentLocator(final Locator locator) {
+        delegate.setDocumentLocator(locator);
     }
 
+    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
-    public void endElement(final String uri, final String localName, final String qName) throws SAXException {
-        delegate.endElement(uri, localName, qName);
+    public void setSystemId(final String systemID) {
+        delegate.setSystemId(systemID);
     }
 
     @Override
-    public void characters(final char[] ch, final int start, final int length) throws SAXException {
-        delegate.characters(ch, start, length);
+    public void skippedEntity(final String name) throws SAXException {
+        delegate.skippedEntity(name);
     }
+    // </editor-fold>
 
     @Override
-    public void ignorableWhitespace(final char[] ch, final int start, final int length) throws SAXException {
-        delegate.ignorableWhitespace(ch, start, length);
+    public void startDocument() throws SAXException {
+        delegate.startDocument();
     }
 
     @Override
-    public void processingInstruction(final String target, final String data) throws SAXException {
-        delegate.processingInstruction(target, data);
+    public void startElement(final String uri, final String localName, final String qName, final Attributes atts) throws SAXException {
+        delegate.startElement(uri, localName, qName, atts);
     }
 
     @Override
-    public void skippedEntity(final String name) throws SAXException {
-        delegate.skippedEntity(name);
+    public void startPrefixMapping(final String prefix, final String uri) throws SAXException {
+        delegate.startPrefixMapping(prefix, uri);
     }
-    // </editor-fold>
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningTransformer.java b/src/main/java/org/apache/commons/xml/HardeningTransformer.java
index e406138..beda636 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTransformer.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTransformer.java
@@ -53,32 +53,6 @@ final class HardeningTransformer extends Transformer {
         delegate.setURIResolver(floor);
     }
 
-    @Override
-    public void reset() {
-        delegate.reset();
-        floor.setDelegate(uriResolver);
-        delegate.setURIResolver(floor);
-    }
-
-    @Override
-    public void setURIResolver(final URIResolver resolver) {
-        floor.setDelegate(resolver);
-    }
-
-    @Override
-    public URIResolver getURIResolver() {
-        return floor.getDelegate();
-    }
-
-    @Override
-    public void transform(final Source xmlSource, final Result outputTarget) throws TransformerException {
-        try {
-            delegate.transform(SAXParserHardener.hardenSource(xmlSource), outputTarget);
-        } catch (final TransformerConfigurationException e) {
-            throw new TransformerException(e);
-        }
-    }
-
     // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public void clearParameters() {
@@ -105,6 +79,18 @@ public Object getParameter(final String name) {
         return delegate.getParameter(name);
     }
 
+    @Override
+    public URIResolver getURIResolver() {
+        return floor.getDelegate();
+    }
+
+    @Override
+    public void reset() {
+        delegate.reset();
+        floor.setDelegate(uriResolver);
+        delegate.setURIResolver(floor);
+    }
+
     @Override
     public void setErrorListener(final ErrorListener listener) {
         delegate.setErrorListener(listener);
@@ -125,4 +111,18 @@ public void setParameter(final String name, final Object value) {
         delegate.setParameter(name, value);
     }
     // </editor-fold>
+
+    @Override
+    public void setURIResolver(final URIResolver resolver) {
+        floor.setDelegate(resolver);
+    }
+
+    @Override
+    public void transform(final Source xmlSource, final Result outputTarget) throws TransformerException {
+        try {
+            delegate.transform(SAXParserHardener.hardenSource(xmlSource), outputTarget);
+        } catch (final TransformerConfigurationException e) {
+            throw new TransformerException(e);
+        }
+    }
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java b/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
index cc25ddd..84775d4 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
@@ -71,6 +71,46 @@
  */
 final class HardeningTransformerFactory extends SAXTransformerFactory {
 
+    /**
+     * Parses a reader-less source into a DOM through a hardened, namespace-aware {@link javax.xml.parsers.DocumentBuilder} and returns a {@link DOMSource}
+     * carrying its system id, so the consumer walks the tree instead of provisioning its own reader. Any other source is left to
+     * {@link SAXParserHardener#hardenSource(Source)}.
+     *
+     * @param source The source to scan for an associated stylesheet.
+     * @return A {@link DOMSource} for a reader-less source, otherwise the result of {@link SAXParserHardener#hardenSource(Source)}.
+     * @throws TransformerConfigurationException if the source cannot be parsed.
+     */
+    private static Source hardenSourceToDom(final Source source) throws TransformerConfigurationException {
+        if (source instanceof StreamSource || source instanceof SAXSource && ((SAXSource) source).getXMLReader() == null) {
+            final InputSource inputSource = SAXSource.sourceToInputSource(source);
+            if (inputSource != null) {
+                try {
+                    final DocumentBuilderFactory factory = DocumentBuilderHardener.harden(DocumentBuilderFactory.newInstance());
+                    factory.setNamespaceAware(true);
+                    final Document document = factory.newDocumentBuilder().parse(inputSource);
+                    return new DOMSource(document, inputSource.getSystemId());
+                } catch (final ParserConfigurationException | SAXException | IOException e) {
+                    throw new TransformerConfigurationException("Failed to parse the source for associated-stylesheet lookup", e);
+                }
+            }
+        }
+        return SAXParserHardener.hardenSource(source);
+    }
+
+    /**
+     * Whether the delegate is Apache Xalan (either its interpretive or its XSLTC factory), whose {@code getAssociatedStylesheet} ignores a SAXSource reader.
+     *
+     * @param factory The delegate factory.
+     * @return Whether the delegate is an {@code org.apache.xalan.} implementation.
+     */
+    private static boolean isXalan(final SAXTransformerFactory factory) {
+        return factory.getClass().getName().startsWith("org.apache.xalan.");
+    }
+
+    private static Templates unwrap(final Templates templates) {
+        return templates instanceof HardeningTemplates ? ((HardeningTemplates) templates).getDelegate() : templates;
+    }
+
     private final SAXTransformerFactory delegate;
 
     /** Empty-{@link Source} supplier for the resolver floor, threaded onto every produced Templates/Transformer; {@code null} means the default empty DOM. */
@@ -91,57 +131,36 @@ final class HardeningTransformerFactory extends SAXTransformerFactory {
     }
 
     @Override
-    public void setURIResolver(final URIResolver resolver) {
-        floor.setDelegate(resolver);
+    public Source getAssociatedStylesheet(final Source source, final String media, final String title, final String charset)
+            throws TransformerConfigurationException {
+        // Xalan's getAssociatedStylesheet drops a SAXSource's reader and self-provisions its own to scan for xml-stylesheet PIs (XALANJ-2849).
+        final Source hardened = isXalan(delegate) ? hardenSourceToDom(source) : SAXParserHardener.hardenSource(source);
+        return delegate.getAssociatedStylesheet(hardened, media, title, charset);
     }
 
+    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
-    public URIResolver getURIResolver() {
-        return floor.getDelegate();
+    public Object getAttribute(final String name) {
+        return delegate.getAttribute(name);
     }
 
     @Override
-    public Source getAssociatedStylesheet(final Source source, final String media, final String title, final String charset)
-            throws TransformerConfigurationException {
-        // Xalan's getAssociatedStylesheet drops a SAXSource's reader and self-provisions its own to scan for xml-stylesheet PIs (XALANJ-2849).
-        final Source hardened = isXalan(delegate) ? hardenSourceToDom(source) : SAXParserHardener.hardenSource(source);
-        return delegate.getAssociatedStylesheet(hardened, media, title, charset);
+    public ErrorListener getErrorListener() {
+        return delegate.getErrorListener();
     }
 
-    /**
-     * Whether the delegate is Apache Xalan (either its interpretive or its XSLTC factory), whose {@code getAssociatedStylesheet} ignores a SAXSource reader.
-     *
-     * @param factory The delegate factory.
-     * @return Whether the delegate is an {@code org.apache.xalan.} implementation.
-     */
-    private static boolean isXalan(final SAXTransformerFactory factory) {
-        return factory.getClass().getName().startsWith("org.apache.xalan.");
+    @Override
+    public boolean getFeature(final String name) {
+        return delegate.getFeature(name);
     }
 
-    /**
-     * Parses a reader-less source into a DOM through a hardened, namespace-aware {@link javax.xml.parsers.DocumentBuilder} and returns a {@link DOMSource}
-     * carrying its system id, so the consumer walks the tree instead of provisioning its own reader. Any other source is left to
-     * {@link SAXParserHardener#hardenSource(Source)}.
-     *
-     * @param source The source to scan for an associated stylesheet.
-     * @return A {@link DOMSource} for a reader-less source, otherwise the result of {@link SAXParserHardener#hardenSource(Source)}.
-     * @throws TransformerConfigurationException if the source cannot be parsed.
-     */
-    private static Source hardenSourceToDom(final Source source) throws TransformerConfigurationException {
-        if (source instanceof StreamSource || source instanceof SAXSource && ((SAXSource) source).getXMLReader() == null) {
-            final InputSource inputSource = SAXSource.sourceToInputSource(source);
-            if (inputSource != null) {
-                try {
-                    final DocumentBuilderFactory factory = DocumentBuilderHardener.harden(DocumentBuilderFactory.newInstance());
-                    factory.setNamespaceAware(true);
-                    final Document document = factory.newDocumentBuilder().parse(inputSource);
-                    return new DOMSource(document, inputSource.getSystemId());
-                } catch (final ParserConfigurationException | SAXException | IOException e) {
-                    throw new TransformerConfigurationException("Failed to parse the source for associated-stylesheet lookup", e);
-                }
-            }
-        }
-        return SAXParserHardener.hardenSource(source);
+    @Override
+    public URIResolver getURIResolver() {
+        return floor.getDelegate();
+    }
+
+    private TransformerHandler hardenHandler(final TransformerHandler handler) {
+        return handler == null ? null : new HardeningTransformerHandler(handler, getURIResolver(), emptySource);
     }
 
     @Override
@@ -150,6 +169,12 @@ public Templates newTemplates(final Source source) throws TransformerConfigurati
         return templates == null ? null : new HardeningTemplates(templates, getURIResolver(), emptySource);
     }
 
+    @Override
+    public TemplatesHandler newTemplatesHandler() throws TransformerConfigurationException {
+        final TemplatesHandler handler = delegate.newTemplatesHandler();
+        return handler == null ? null : new HardeningTemplatesHandler(handler, getURIResolver(), emptySource);
+    }
+
     @Override
     public Transformer newTransformer() throws TransformerConfigurationException {
         // Identity transformer: still parses runtime sources, so wrap it to harden Transformer.transform(Source, Result).
@@ -179,12 +204,6 @@ public TransformerHandler newTransformerHandler(final Templates templates) throw
         return hardenHandler(delegate.newTransformerHandler(unwrap(templates)));
     }
 
-    @Override
-    public TemplatesHandler newTemplatesHandler() throws TransformerConfigurationException {
-        final TemplatesHandler handler = delegate.newTemplatesHandler();
-        return handler == null ? null : new HardeningTemplatesHandler(handler, getURIResolver(), emptySource);
-    }
-
     @Override
     public XMLFilter newXMLFilter(final Source source) throws TransformerConfigurationException {
         final Templates templates = newTemplates(source);
@@ -197,30 +216,6 @@ public XMLFilter newXMLFilter(final Templates templates) throws TransformerConfi
                 : new HardeningTemplates(templates, getURIResolver(), emptySource));
     }
 
-    private TransformerHandler hardenHandler(final TransformerHandler handler) {
-        return handler == null ? null : new HardeningTransformerHandler(handler, getURIResolver(), emptySource);
-    }
-
-    private static Templates unwrap(final Templates templates) {
-        return templates instanceof HardeningTemplates ? ((HardeningTemplates) templates).getDelegate() : templates;
-    }
-
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
-    @Override
-    public Object getAttribute(final String name) {
-        return delegate.getAttribute(name);
-    }
-
-    @Override
-    public ErrorListener getErrorListener() {
-        return delegate.getErrorListener();
-    }
-
-    @Override
-    public boolean getFeature(final String name) {
-        return delegate.getFeature(name);
-    }
-
     @Override
     public void setAttribute(final String name, final Object value) {
         delegate.setAttribute(name, value);
@@ -236,4 +231,9 @@ public void setFeature(final String name, final boolean value) throws Transforme
         delegate.setFeature(name, value);
     }
     // </editor-fold>
+
+    @Override
+    public void setURIResolver(final URIResolver resolver) {
+        floor.setDelegate(resolver);
+    }
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java b/src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java
index 6281802..6c44417 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java
@@ -51,44 +51,39 @@ final class HardeningTransformerHandler implements TransformerHandler {
     }
 
     @Override
-    public Transformer getTransformer() {
-        return transformer;
-    }
-
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
-    @Override
-    public void setResult(final Result result) {
-        delegate.setResult(result);
+    public void characters(final char[] ch, final int start, final int length) throws SAXException {
+        delegate.characters(ch, start, length);
     }
 
     @Override
-    public void setSystemId(final String systemID) {
-        delegate.setSystemId(systemID);
+    public void comment(final char[] ch, final int start, final int length) throws SAXException {
+        delegate.comment(ch, start, length);
     }
+    // </editor-fold>
 
     @Override
-    public String getSystemId() {
-        return delegate.getSystemId();
+    public void endCDATA() throws SAXException {
+        delegate.endCDATA();
     }
 
     @Override
-    public void setDocumentLocator(final Locator locator) {
-        delegate.setDocumentLocator(locator);
+    public void endDocument() throws SAXException {
+        delegate.endDocument();
     }
 
     @Override
-    public void startDocument() throws SAXException {
-        delegate.startDocument();
+    public void endDTD() throws SAXException {
+        delegate.endDTD();
     }
 
     @Override
-    public void endDocument() throws SAXException {
-        delegate.endDocument();
+    public void endElement(final String uri, final String localName, final String qName) throws SAXException {
+        delegate.endElement(uri, localName, qName);
     }
 
     @Override
-    public void startPrefixMapping(final String prefix, final String uri) throws SAXException {
-        delegate.startPrefixMapping(prefix, uri);
+    public void endEntity(final String name) throws SAXException {
+        delegate.endEntity(name);
     }
 
     @Override
@@ -97,23 +92,23 @@ public void endPrefixMapping(final String prefix) throws SAXException {
     }
 
     @Override
-    public void startElement(final String uri, final String localName, final String qName, final Attributes atts) throws SAXException {
-        delegate.startElement(uri, localName, qName, atts);
+    public String getSystemId() {
+        return delegate.getSystemId();
     }
 
     @Override
-    public void endElement(final String uri, final String localName, final String qName) throws SAXException {
-        delegate.endElement(uri, localName, qName);
+    public Transformer getTransformer() {
+        return transformer;
     }
 
     @Override
-    public void characters(final char[] ch, final int start, final int length) throws SAXException {
-        delegate.characters(ch, start, length);
+    public void ignorableWhitespace(final char[] ch, final int start, final int length) throws SAXException {
+        delegate.ignorableWhitespace(ch, start, length);
     }
 
     @Override
-    public void ignorableWhitespace(final char[] ch, final int start, final int length) throws SAXException {
-        delegate.ignorableWhitespace(ch, start, length);
+    public void notationDecl(final String name, final String publicId, final String systemId) throws SAXException {
+        delegate.notationDecl(name, publicId, systemId);
     }
 
     @Override
@@ -122,53 +117,58 @@ public void processingInstruction(final String target, final String data) throws
     }
 
     @Override
-    public void skippedEntity(final String name) throws SAXException {
-        delegate.skippedEntity(name);
+    public void setDocumentLocator(final Locator locator) {
+        delegate.setDocumentLocator(locator);
     }
 
+    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
-    public void notationDecl(final String name, final String publicId, final String systemId) throws SAXException {
-        delegate.notationDecl(name, publicId, systemId);
+    public void setResult(final Result result) {
+        delegate.setResult(result);
     }
 
     @Override
-    public void unparsedEntityDecl(final String name, final String publicId, final String systemId, final String notationName) throws SAXException {
-        delegate.unparsedEntityDecl(name, publicId, systemId, notationName);
+    public void setSystemId(final String systemID) {
+        delegate.setSystemId(systemID);
     }
 
     @Override
-    public void startDTD(final String name, final String publicId, final String systemId) throws SAXException {
-        delegate.startDTD(name, publicId, systemId);
+    public void skippedEntity(final String name) throws SAXException {
+        delegate.skippedEntity(name);
     }
 
     @Override
-    public void endDTD() throws SAXException {
-        delegate.endDTD();
+    public void startCDATA() throws SAXException {
+        delegate.startCDATA();
     }
 
     @Override
-    public void startEntity(final String name) throws SAXException {
-        delegate.startEntity(name);
+    public void startDocument() throws SAXException {
+        delegate.startDocument();
     }
 
     @Override
-    public void endEntity(final String name) throws SAXException {
-        delegate.endEntity(name);
+    public void startDTD(final String name, final String publicId, final String systemId) throws SAXException {
+        delegate.startDTD(name, publicId, systemId);
     }
 
     @Override
-    public void startCDATA() throws SAXException {
-        delegate.startCDATA();
+    public void startElement(final String uri, final String localName, final String qName, final Attributes atts) throws SAXException {
+        delegate.startElement(uri, localName, qName, atts);
     }
 
     @Override
-    public void endCDATA() throws SAXException {
-        delegate.endCDATA();
+    public void startEntity(final String name) throws SAXException {
+        delegate.startEntity(name);
     }
 
     @Override
-    public void comment(final char[] ch, final int start, final int length) throws SAXException {
-        delegate.comment(ch, start, length);
+    public void startPrefixMapping(final String prefix, final String uri) throws SAXException {
+        delegate.startPrefixMapping(prefix, uri);
+    }
+
+    @Override
+    public void unparsedEntityDecl(final String name, final String publicId, final String systemId, final String notationName) throws SAXException {
+        delegate.unparsedEntityDecl(name, publicId, systemId, notationName);
     }
-    // </editor-fold>
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java b/src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java
index ea25b5b..8cb9c44 100644
--- a/src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java
+++ b/src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java
@@ -47,29 +47,28 @@ final class HardeningValidatorHandler extends ValidatorHandler {
     }
 
     @Override
-    public void setResourceResolver(final LSResourceResolver resourceResolver) {
-        floor.setDelegate(resourceResolver);
+    public void characters(final char[] ch, final int start, final int length) throws SAXException {
+        delegate.characters(ch, start, length);
     }
 
     @Override
-    public LSResourceResolver getResourceResolver() {
-        return floor.getDelegate();
+    public void endDocument() throws SAXException {
+        delegate.endDocument();
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
-    public void setContentHandler(final ContentHandler receiver) {
-        delegate.setContentHandler(receiver);
+    public void endElement(final String uri, final String localName, final String qName) throws SAXException {
+        delegate.endElement(uri, localName, qName);
     }
 
     @Override
-    public ContentHandler getContentHandler() {
-        return delegate.getContentHandler();
+    public void endPrefixMapping(final String prefix) throws SAXException {
+        delegate.endPrefixMapping(prefix);
     }
 
     @Override
-    public void setErrorHandler(final ErrorHandler errorHandler) {
-        delegate.setErrorHandler(errorHandler);
+    public ContentHandler getContentHandler() {
+        return delegate.getContentHandler();
     }
 
     @Override
@@ -78,83 +77,84 @@ public ErrorHandler getErrorHandler() {
     }
 
     @Override
-    public TypeInfoProvider getTypeInfoProvider() {
-        return delegate.getTypeInfoProvider();
+    public boolean getFeature(final String name) throws SAXNotRecognizedException, SAXNotSupportedException {
+        return delegate.getFeature(name);
     }
 
     @Override
-    public boolean getFeature(final String name) throws SAXNotRecognizedException, SAXNotSupportedException {
-        return delegate.getFeature(name);
+    public Object getProperty(final String name) throws SAXNotRecognizedException, SAXNotSupportedException {
+        return delegate.getProperty(name);
     }
 
     @Override
-    public void setFeature(final String name, final boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
-        delegate.setFeature(name, value);
+    public LSResourceResolver getResourceResolver() {
+        return floor.getDelegate();
     }
 
     @Override
-    public Object getProperty(final String name) throws SAXNotRecognizedException, SAXNotSupportedException {
-        return delegate.getProperty(name);
+    public TypeInfoProvider getTypeInfoProvider() {
+        return delegate.getTypeInfoProvider();
     }
 
     @Override
-    public void setProperty(final String name, final Object object) throws SAXNotRecognizedException, SAXNotSupportedException {
-        delegate.setProperty(name, object);
+    public void ignorableWhitespace(final char[] ch, final int start, final int length) throws SAXException {
+        delegate.ignorableWhitespace(ch, start, length);
     }
 
     @Override
-    public void setDocumentLocator(final Locator locator) {
-        delegate.setDocumentLocator(locator);
+    public void processingInstruction(final String target, final String data) throws SAXException {
+        delegate.processingInstruction(target, data);
     }
 
+    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
-    public void startDocument() throws SAXException {
-        delegate.startDocument();
+    public void setContentHandler(final ContentHandler receiver) {
+        delegate.setContentHandler(receiver);
     }
 
     @Override
-    public void endDocument() throws SAXException {
-        delegate.endDocument();
+    public void setDocumentLocator(final Locator locator) {
+        delegate.setDocumentLocator(locator);
     }
 
     @Override
-    public void startPrefixMapping(final String prefix, final String uri) throws SAXException {
-        delegate.startPrefixMapping(prefix, uri);
+    public void setErrorHandler(final ErrorHandler errorHandler) {
+        delegate.setErrorHandler(errorHandler);
     }
 
     @Override
-    public void endPrefixMapping(final String prefix) throws SAXException {
-        delegate.endPrefixMapping(prefix);
+    public void setFeature(final String name, final boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
+        delegate.setFeature(name, value);
     }
 
     @Override
-    public void startElement(final String uri, final String localName, final String qName, final Attributes atts) throws SAXException {
-        delegate.startElement(uri, localName, qName, atts);
+    public void setProperty(final String name, final Object object) throws SAXNotRecognizedException, SAXNotSupportedException {
+        delegate.setProperty(name, object);
     }
 
     @Override
-    public void endElement(final String uri, final String localName, final String qName) throws SAXException {
-        delegate.endElement(uri, localName, qName);
+    public void setResourceResolver(final LSResourceResolver resourceResolver) {
+        floor.setDelegate(resourceResolver);
     }
 
     @Override
-    public void characters(final char[] ch, final int start, final int length) throws SAXException {
-        delegate.characters(ch, start, length);
+    public void skippedEntity(final String name) throws SAXException {
+        delegate.skippedEntity(name);
     }
+    // </editor-fold>
 
     @Override
-    public void ignorableWhitespace(final char[] ch, final int start, final int length) throws SAXException {
-        delegate.ignorableWhitespace(ch, start, length);
+    public void startDocument() throws SAXException {
+        delegate.startDocument();
     }
 
     @Override
-    public void processingInstruction(final String target, final String data) throws SAXException {
-        delegate.processingInstruction(target, data);
+    public void startElement(final String uri, final String localName, final String qName, final Attributes atts) throws SAXException {
+        delegate.startElement(uri, localName, qName, atts);
     }
 
     @Override
-    public void skippedEntity(final String name) throws SAXException {
-        delegate.skippedEntity(name);
+    public void startPrefixMapping(final String prefix, final String uri) throws SAXException {
+        delegate.startPrefixMapping(prefix, uri);
     }
-    // </editor-fold>
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java b/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
index 94cf4b5..a806018 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
@@ -51,6 +51,17 @@
  */
 final class HardeningXMLInputFactory extends XMLInputFactory {
 
+    private static boolean isResolverProperty(final String name) {
+        return XMLInputFactory.RESOLVER.equals(name)
+                || StaxHardener.WSTX_DTD_RESOLVER.equals(name)
+                || StaxHardener.WSTX_ENTITY_RESOLVER.equals(name)
+                || StaxHardener.WSTX_UNDECLARED_ENTITY_RESOLVER.equals(name);
+    }
+
+    private static XMLResolver unwrap(final XMLResolver resolver) {
+        return resolver instanceof FallbackIgnoreXMLResolver ? ((FallbackIgnoreXMLResolver) resolver).getDelegate() : resolver;
+    }
+
     private final XMLInputFactory delegate;
 
     HardeningXMLInputFactory(final XMLInputFactory delegate) {
@@ -59,73 +70,48 @@ final class HardeningXMLInputFactory extends XMLInputFactory {
     }
 
     @Override
-    public void setXMLResolver(final XMLResolver resolver) {
-        setResolverProperty(XMLInputFactory.RESOLVER, resolver);
+    public XMLEventReader createFilteredReader(final XMLEventReader reader, final EventFilter filter) throws XMLStreamException {
+        return delegate.createFilteredReader(reader, filter);
     }
 
     @Override
-    public XMLResolver getXMLResolver() {
-        return unwrap(delegate.getXMLResolver());
+    public XMLStreamReader createFilteredReader(final XMLStreamReader reader, final StreamFilter filter) throws XMLStreamException {
+        return delegate.createFilteredReader(reader, filter);
     }
 
     @Override
-    public void setProperty(final String name, final Object value) {
-        // If a resolver property has a value of the wrong type, pass it to the delegate to generate an appropriate exception.
-        if (isResolverProperty(name) && (value == null || value instanceof XMLResolver)) {
-            setResolverProperty(name, (XMLResolver) value);
-        } else {
-            delegate.setProperty(name, value);
-        }
+    public XMLEventReader createXMLEventReader(final InputStream stream) throws XMLStreamException {
+        return delegate.createXMLEventReader(stream);
     }
 
     @Override
-    public Object getProperty(final String name) {
-        if (isResolverProperty(name)) {
-            return unwrap((XMLResolver) delegate.getProperty(name));
-        }
-        return delegate.getProperty(name);
+    public XMLEventReader createXMLEventReader(final InputStream stream, final String encoding) throws XMLStreamException {
+        return delegate.createXMLEventReader(stream, encoding);
     }
 
-    /**
-     * Routes a caller-set resolver for the property {@code name} behind the floor currently installed on that hook.
-     *
-     * @param name     The resolver-valued property being set.
-     * @param resolver The caller's resolver, or their own {@link FallbackIgnoreXMLResolver} to take control.
-     */
-    private void setResolverProperty(final String name, final XMLResolver resolver) {
-        if (resolver instanceof FallbackIgnoreXMLResolver) {
-            // The caller supplies their own floor: hand it to the delegate as-is.
-            delegate.setProperty(name, resolver);
-        } else {
-            final Object current = delegate.getProperty(name);
-            if (current instanceof FallbackIgnoreXMLResolver) {
-                ((FallbackIgnoreXMLResolver) current).setDelegate(resolver);
-            } else {
-                delegate.setProperty(name, new FallbackIgnoreXMLResolver(resolver));
-            }
-        }
+    @Override
+    public XMLEventReader createXMLEventReader(final Reader reader) throws XMLStreamException {
+        return delegate.createXMLEventReader(reader);
     }
 
-    private static boolean isResolverProperty(final String name) {
-        return XMLInputFactory.RESOLVER.equals(name)
-                || StaxHardener.WSTX_DTD_RESOLVER.equals(name)
-                || StaxHardener.WSTX_ENTITY_RESOLVER.equals(name)
-                || StaxHardener.WSTX_UNDECLARED_ENTITY_RESOLVER.equals(name);
+    @Override
+    public XMLEventReader createXMLEventReader(final Source source) throws XMLStreamException {
+        return delegate.createXMLEventReader(source);
     }
 
-    private static XMLResolver unwrap(final XMLResolver resolver) {
-        return resolver instanceof FallbackIgnoreXMLResolver ? ((FallbackIgnoreXMLResolver) resolver).getDelegate() : resolver;
+    @Override
+    public XMLEventReader createXMLEventReader(final String systemId, final InputStream stream) throws XMLStreamException {
+        return delegate.createXMLEventReader(systemId, stream);
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
-    public XMLStreamReader createXMLStreamReader(final Reader reader) throws XMLStreamException {
-        return delegate.createXMLStreamReader(reader);
+    public XMLEventReader createXMLEventReader(final String systemId, final Reader reader) throws XMLStreamException {
+        return delegate.createXMLEventReader(systemId, reader);
     }
 
     @Override
-    public XMLStreamReader createXMLStreamReader(final Source source) throws XMLStreamException {
-        return delegate.createXMLStreamReader(source);
+    public XMLEventReader createXMLEventReader(final XMLStreamReader reader) throws XMLStreamException {
+        return delegate.createXMLEventReader(reader);
     }
 
     @Override
@@ -138,64 +124,89 @@ public XMLStreamReader createXMLStreamReader(final InputStream stream, final Str
         return delegate.createXMLStreamReader(stream, encoding);
     }
 
+    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
-    public XMLStreamReader createXMLStreamReader(final String systemId, final InputStream stream) throws XMLStreamException {
-        return delegate.createXMLStreamReader(systemId, stream);
+    public XMLStreamReader createXMLStreamReader(final Reader reader) throws XMLStreamException {
+        return delegate.createXMLStreamReader(reader);
     }
 
     @Override
-    public XMLStreamReader createXMLStreamReader(final String systemId, final Reader reader) throws XMLStreamException {
-        return delegate.createXMLStreamReader(systemId, reader);
+    public XMLStreamReader createXMLStreamReader(final Source source) throws XMLStreamException {
+        return delegate.createXMLStreamReader(source);
     }
 
     @Override
-    public XMLEventReader createXMLEventReader(final Reader reader) throws XMLStreamException {
-        return delegate.createXMLEventReader(reader);
+    public XMLStreamReader createXMLStreamReader(final String systemId, final InputStream stream) throws XMLStreamException {
+        return delegate.createXMLStreamReader(systemId, stream);
     }
 
     @Override
-    public XMLEventReader createXMLEventReader(final String systemId, final Reader reader) throws XMLStreamException {
-        return delegate.createXMLEventReader(systemId, reader);
+    public XMLStreamReader createXMLStreamReader(final String systemId, final Reader reader) throws XMLStreamException {
+        return delegate.createXMLStreamReader(systemId, reader);
     }
 
     @Override
-    public XMLEventReader createXMLEventReader(final XMLStreamReader reader) throws XMLStreamException {
-        return delegate.createXMLEventReader(reader);
+    public XMLEventAllocator getEventAllocator() {
+        return delegate.getEventAllocator();
     }
+    // </editor-fold>
 
     @Override
-    public XMLEventReader createXMLEventReader(final Source source) throws XMLStreamException {
-        return delegate.createXMLEventReader(source);
+    public Object getProperty(final String name) {
+        if (isResolverProperty(name)) {
+            return unwrap((XMLResolver) delegate.getProperty(name));
+        }
+        return delegate.getProperty(name);
     }
 
     @Override
-    public XMLEventReader createXMLEventReader(final InputStream stream) throws XMLStreamException {
-        return delegate.createXMLEventReader(stream);
+    public XMLReporter getXMLReporter() {
+        return delegate.getXMLReporter();
     }
 
     @Override
-    public XMLEventReader createXMLEventReader(final InputStream stream, final String encoding) throws XMLStreamException {
-        return delegate.createXMLEventReader(stream, encoding);
+    public XMLResolver getXMLResolver() {
+        return unwrap(delegate.getXMLResolver());
     }
 
     @Override
-    public XMLEventReader createXMLEventReader(final String systemId, final InputStream stream) throws XMLStreamException {
-        return delegate.createXMLEventReader(systemId, stream);
+    public boolean isPropertySupported(final String name) {
+        return delegate.isPropertySupported(name);
     }
 
     @Override
-    public XMLStreamReader createFilteredReader(final XMLStreamReader reader, final StreamFilter filter) throws XMLStreamException {
-        return delegate.createFilteredReader(reader, filter);
+    public void setEventAllocator(final XMLEventAllocator allocator) {
+        delegate.setEventAllocator(allocator);
     }
 
     @Override
-    public XMLEventReader createFilteredReader(final XMLEventReader reader, final EventFilter filter) throws XMLStreamException {
-        return delegate.createFilteredReader(reader, filter);
+    public void setProperty(final String name, final Object value) {
+        // If a resolver property has a value of the wrong type, pass it to the delegate to generate an appropriate exception.
+        if (isResolverProperty(name) && (value == null || value instanceof XMLResolver)) {
+            setResolverProperty(name, (XMLResolver) value);
+        } else {
+            delegate.setProperty(name, value);
+        }
     }
 
-    @Override
-    public XMLReporter getXMLReporter() {
-        return delegate.getXMLReporter();
+    /**
+     * Routes a caller-set resolver for the property {@code name} behind the floor currently installed on that hook.
+     *
+     * @param name     The resolver-valued property being set.
+     * @param resolver The caller's resolver, or their own {@link FallbackIgnoreXMLResolver} to take control.
+     */
+    private void setResolverProperty(final String name, final XMLResolver resolver) {
+        if (resolver instanceof FallbackIgnoreXMLResolver) {
+            // The caller supplies their own floor: hand it to the delegate as-is.
+            delegate.setProperty(name, resolver);
+        } else {
+            final Object current = delegate.getProperty(name);
+            if (current instanceof FallbackIgnoreXMLResolver) {
+                ((FallbackIgnoreXMLResolver) current).setDelegate(resolver);
+            } else {
+                delegate.setProperty(name, new FallbackIgnoreXMLResolver(resolver));
+            }
+        }
     }
 
     @Override
@@ -204,18 +215,7 @@ public void setXMLReporter(final XMLReporter reporter) {
     }
 
     @Override
-    public boolean isPropertySupported(final String name) {
-        return delegate.isPropertySupported(name);
-    }
-
-    @Override
-    public void setEventAllocator(final XMLEventAllocator allocator) {
-        delegate.setEventAllocator(allocator);
-    }
-
-    @Override
-    public XMLEventAllocator getEventAllocator() {
-        return delegate.getEventAllocator();
+    public void setXMLResolver(final XMLResolver resolver) {
+        setResolverProperty(XMLInputFactory.RESOLVER, resolver);
     }
-    // </editor-fold>
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningXMLReader.java b/src/main/java/org/apache/commons/xml/HardeningXMLReader.java
index 7621571..7899ed5 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXMLReader.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXMLReader.java
@@ -51,16 +51,6 @@ class HardeningXMLReader implements XMLReader {
         delegate.setEntityResolver(floor);
     }
 
-    @Override
-    public void setEntityResolver(final EntityResolver resolver) {
-        floor.setDelegate(resolver);
-    }
-
-    @Override
-    public EntityResolver getEntityResolver() {
-        return floor.getDelegate();
-    }
-
     // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public ContentHandler getContentHandler() {
@@ -72,6 +62,11 @@ public DTDHandler getDTDHandler() {
         return delegate.getDTDHandler();
     }
 
+    @Override
+    public EntityResolver getEntityResolver() {
+        return floor.getDelegate();
+    }
+
     @Override
     public ErrorHandler getErrorHandler() {
         return delegate.getErrorHandler();
@@ -107,6 +102,11 @@ public void setDTDHandler(final DTDHandler handler) {
         delegate.setDTDHandler(handler);
     }
 
+    @Override
+    public void setEntityResolver(final EntityResolver resolver) {
+        floor.setDelegate(resolver);
+    }
+
     @Override
     public void setErrorHandler(final ErrorHandler handler) {
         delegate.setErrorHandler(handler);
diff --git a/src/main/java/org/apache/commons/xml/HardeningXPath.java b/src/main/java/org/apache/commons/xml/HardeningXPath.java
index 8db5586..13cef3a 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXPath.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXPath.java
@@ -77,6 +77,12 @@ static Document parse(final InputSource source) throws XPathExpressionException
         this.delegate = Objects.requireNonNull(delegate, "delegate");
     }
 
+    @Override
+    public XPathExpression compile(final String expression) throws XPathExpressionException {
+        final XPathExpression compiled = delegate.compile(expression);
+        return compiled == null ? null : new HardeningXPathExpression(compiled);
+    }
+
     @Override
     public String evaluate(final String expression, final InputSource source) throws XPathExpressionException {
         return delegate.evaluate(expression, parse(source));
@@ -87,12 +93,6 @@ public Object evaluate(final String expression, final InputSource source, final
         return delegate.evaluate(expression, parse(source), returnType);
     }
 
-    @Override
-    public XPathExpression compile(final String expression) throws XPathExpressionException {
-        final XPathExpression compiled = delegate.compile(expression);
-        return compiled == null ? null : new HardeningXPathExpression(compiled);
-    }
-
     @Override
     public String evaluate(final String expression, final Object item) throws XPathExpressionException {
         return delegate.evaluate(expression, item);
diff --git a/src/main/java/org/apache/commons/xml/HardeningXPathFactory.java b/src/main/java/org/apache/commons/xml/HardeningXPathFactory.java
index c6d4cb0..a697032 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXPathFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXPathFactory.java
@@ -40,12 +40,6 @@ final class HardeningXPathFactory extends XPathFactory {
         this.delegate = Objects.requireNonNull(delegate, "delegate");
     }
 
-    @Override
-    public XPath newXPath() {
-        final XPath xpath = delegate.newXPath();
-        return xpath == null ? null : new HardeningXPath(xpath);
-    }
-
     @Override
     public boolean getFeature(final String name) throws XPathFactoryConfigurationException {
         return delegate.getFeature(name);
@@ -56,6 +50,12 @@ public boolean isObjectModelSupported(final String objectModel) {
         return delegate.isObjectModelSupported(objectModel);
     }
 
+    @Override
+    public XPath newXPath() {
+        final XPath xpath = delegate.newXPath();
+        return xpath == null ? null : new HardeningXPath(xpath);
+    }
+
     @Override
     public void setFeature(final String name, final boolean value) throws XPathFactoryConfigurationException {
         delegate.setFeature(name, value);
diff --git a/src/main/java/org/apache/commons/xml/SaxonProvider.java b/src/main/java/org/apache/commons/xml/SaxonProvider.java
index 2b600ba..f2af427 100644
--- a/src/main/java/org/apache/commons/xml/SaxonProvider.java
+++ b/src/main/java/org/apache/commons/xml/SaxonProvider.java
@@ -43,18 +43,6 @@
  */
 final class SaxonProvider {
 
-    /**
-     * Tells whether the factory class is Saxon's, by package prefix, so public subclasses such as {@code net.sf.saxon.BasicTransformerFactory} route to the
-     * same locked-down {@link Configuration} as the factory registered for JAXP lookup.
-     *
-     * @param factoryClass The factory implementation class.
-     * @return Whether the class lives in Saxon's open-source or commercial packages.
-     */
-    static boolean isSaxon(final Class<?> factoryClass) {
-        final String name = factoryClass.getName();
-        return name.startsWith("net.sf.saxon.") || name.startsWith("com.saxonica.");
-    }
-
     /**
      * A Saxon {@link Configuration} carrying the vendor-specific restrictions that the standard JAXP knobs cannot express.
      *
@@ -123,15 +111,6 @@ private static XPathFactory configure(final XPathFactory factory) {
         }
     }
 
-    /**
-     * The empty-{@link Source} shape Saxon's consumers expect, for the {@link FallbackIgnoreURIResolver} floor the TrAX wrapper installs.
-     *
-     * @return a supplier for Saxon's empty {@link Source}.
-     */
-    static Supplier<Source> emptySourceSupplier() {
-        return EmptySource::getInstance;
-    }
-
     static TransformerFactory configure(final TransformerFactory factory) {
         try {
             return SaxonProviderConfigurer.configure(factory);
@@ -156,6 +135,27 @@ static XPathFactory configure(final XPathFactory factory) {
         }
     }
 
+    /**
+     * The empty-{@link Source} shape Saxon's consumers expect, for the {@link FallbackIgnoreURIResolver} floor the TrAX wrapper installs.
+     *
+     * @return a supplier for Saxon's empty {@link Source}.
+     */
+    static Supplier<Source> emptySourceSupplier() {
+        return EmptySource::getInstance;
+    }
+
+    /**
+     * Tells whether the factory class is Saxon's, by package prefix, so public subclasses such as {@code net.sf.saxon.BasicTransformerFactory} route to the
+     * same locked-down {@link Configuration} as the factory registered for JAXP lookup.
+     *
+     * @param factoryClass The factory implementation class.
+     * @return Whether the class lives in Saxon's open-source or commercial packages.
+     */
+    static boolean isSaxon(final Class<?> factoryClass) {
+        final String name = factoryClass.getName();
+        return name.startsWith("net.sf.saxon.") || name.startsWith("com.saxonica.");
+    }
+
     private SaxonProvider() {
     }
 }
diff --git a/src/test/java/org/apache/commons/xml/AssociatedStylesheetTest.java b/src/test/java/org/apache/commons/xml/AssociatedStylesheetTest.java
index c4bb03f..3a553fd 100644
--- a/src/test/java/org/apache/commons/xml/AssociatedStylesheetTest.java
+++ b/src/test/java/org/apache/commons/xml/AssociatedStylesheetTest.java
@@ -41,6 +41,14 @@
 @Tag("trax")
 class AssociatedStylesheetTest {
 
+    /** The PI was found (non-null); where the engine exposes a system id, it points at the declared stylesheet. */
+    private static void assertAssociatedStylesheet(final Source associated) {
+        assertNotNull(associated, "expected the associated stylesheet PI to be found");
+        if (associated.getSystemId() != null) {
+            assertTrue(associated.getSystemId().endsWith("included.xsl"), "unexpected associated stylesheet: " + associated.getSystemId());
+        }
+    }
+
     private static TransformerFactory hardenedFactory() {
         final TransformerFactory factory = XmlFactories.newTransformerFactory();
         factory.setErrorListener(AttackTestSupport.STRICT_REPORTER);
@@ -65,14 +73,6 @@ void hardenedGetAssociatedStylesheetReturnsStylesheet() throws TransformerConfig
         assertAssociatedStylesheet(associated);
     }
 
-    /** The PI was found (non-null); where the engine exposes a system id, it points at the declared stylesheet. */
-    private static void assertAssociatedStylesheet(final Source associated) {
-        assertNotNull(associated, "expected the associated stylesheet PI to be found");
-        if (associated.getSystemId() != null) {
-            assertTrue(associated.getSystemId().endsWith("included.xsl"), "unexpected associated stylesheet: " + associated.getSystemId());
-        }
-    }
-
     @Test
     void unconfiguredGetAssociatedStylesheetFetchesExternalDtd() {
         // Leak/discrimination control: the unconfigured engine attempts to fetch the unreachable external DTD and fails. Android's KXmlParser does not fetch
diff --git a/src/test/java/org/apache/commons/xml/AttackTestSupport.java b/src/test/java/org/apache/commons/xml/AttackTestSupport.java
index b7c4811..a5d87b2 100644
--- a/src/test/java/org/apache/commons/xml/AttackTestSupport.java
+++ b/src/test/java/org/apache/commons/xml/AttackTestSupport.java
@@ -746,11 +746,6 @@ private static String blockedDescription(final String description) {
         return "Hardening did not block " + description + "; parse completed successfully.";
     }
 
-    /** Parses the payload through the supplied reader and returns the accumulated character data, used by the SAX-based {@code DoesNotLeak} helpers. */
-    static String captureCharacters(final XMLReader reader, final String payload) throws Exception {
-        return captureCharacters(reader, inputSource(payload));
-    }
-
     /** Parses the source through the supplied reader, with {@link #STRICT_REPORTER} installed, and returns the accumulated character data. */
     static String captureCharacters(final XMLReader reader, final InputSource source) throws Exception {
         final StringBuilder text = new StringBuilder();
@@ -759,6 +754,11 @@ static String captureCharacters(final XMLReader reader, final InputSource source
         return text.toString();
     }
 
+    /** Parses the payload through the supplied reader and returns the accumulated character data, used by the SAX-based {@code DoesNotLeak} helpers. */
+    static String captureCharacters(final XMLReader reader, final String payload) throws Exception {
+        return captureCharacters(reader, inputSource(payload));
+    }
+
     /** Parses the payload through a {@link XMLEventReader} and returns the accumulated character and CDATA data, used by the StAX-based {@code DoesNotLeak} helper. */
     static String captureStaxEventText(final XMLInputFactory factory, final String payload) throws Exception {
         final StringBuilder text = new StringBuilder();
@@ -883,13 +883,6 @@ private static void liftEntityLimits(final XMLReader reader) {
         }
     }
 
-    /**
-     * Builds a {@link SAXSource} wrapping the payload, without an explicit parser; used by the unconfigured-side TrAX controls.
-     */
-    private static SAXSource permissiveSaxSource(final String xml) {
-        return new SAXSource(permissiveReader(), new InputSource(new StringReader(xml)));
-    }
-
     /**
      * A permissive, namespace-aware {@link XMLReader} with every entity-expansion limit lifted, for the unconfigured-side controls.
      *
@@ -907,6 +900,13 @@ static XMLReader permissiveReader() {
         return IS_ANDROID ? new PermissiveExpatReader(reader) : reader;
     }
 
+    /**
+     * Builds a {@link SAXSource} wrapping the payload, without an explicit parser; used by the unconfigured-side TrAX controls.
+     */
+    private static SAXSource permissiveSaxSource(final String xml) {
+        return new SAXSource(permissiveReader(), new InputSource(new StringReader(xml)));
+    }
+
     private static boolean probeAndroid() {
         try {
             Class.forName("android.os.Build");
@@ -985,20 +985,20 @@ private static Templates strictTemplates(final TransformerFactory factory, final
     }
 
     /**
-     * Builds an identity {@link Transformer} from {@code factory} with {@link #STRICT_REPORTER} installed on both the factory and the resulting transformer.
+     * Builds a {@link Transformer} from {@code templates} with {@link #STRICT_REPORTER} installed as its error listener.
      */
-    private static Transformer strictTransformer(final TransformerFactory factory) throws TransformerConfigurationException {
-        factory.setErrorListener(STRICT_REPORTER);
-        final Transformer transformer = factory.newTransformer();
+    private static Transformer strictTransformer(final Templates templates) throws TransformerConfigurationException {
+        final Transformer transformer = templates.newTransformer();
         transformer.setErrorListener(STRICT_REPORTER);
         return transformer;
     }
 
     /**
-     * Builds a {@link Transformer} from {@code templates} with {@link #STRICT_REPORTER} installed as its error listener.
+     * Builds an identity {@link Transformer} from {@code factory} with {@link #STRICT_REPORTER} installed on both the factory and the resulting transformer.
      */
-    private static Transformer strictTransformer(final Templates templates) throws TransformerConfigurationException {
-        final Transformer transformer = templates.newTransformer();
+    private static Transformer strictTransformer(final TransformerFactory factory) throws TransformerConfigurationException {
+        factory.setErrorListener(STRICT_REPORTER);
+        final Transformer transformer = factory.newTransformer();
         transformer.setErrorListener(STRICT_REPORTER);
         return transformer;
     }
diff --git a/src/test/java/org/apache/commons/xml/DenyUnresolvedTest.java b/src/test/java/org/apache/commons/xml/DenyUnresolvedTest.java
index fb84f99..975ab47 100644
--- a/src/test/java/org/apache/commons/xml/DenyUnresolvedTest.java
+++ b/src/test/java/org/apache/commons/xml/DenyUnresolvedTest.java
@@ -38,16 +38,16 @@ class DenyUnresolvedTest {
 
     private static final String SYSTEM_ID = "http://invalid.example.invalid/external.dtd";
 
-    @BeforeEach
-    void enableThrowOnUnresolved() {
-        System.setProperty(XmlFactories.THROW_ON_UNRESOLVED, "true");
-    }
-
     @AfterEach
     void clearThrowOnUnresolved() {
         System.clearProperty(XmlFactories.THROW_ON_UNRESOLVED);
     }
 
+    @BeforeEach
+    void enableThrowOnUnresolved() {
+        System.setProperty(XmlFactories.THROW_ON_UNRESOLVED, "true");
+    }
+
     @Test
     void floorsThrowOnUnresolved() {
         assertThrows(SAXException.class, () -> new FallbackIgnoreEntityResolver2(null).resolveEntity(null, SYSTEM_ID),
diff --git a/src/test/java/org/apache/commons/xml/EntityResolverFloorTest.java b/src/test/java/org/apache/commons/xml/EntityResolverFloorTest.java
index 529f6ea..e8fe90f 100644
--- a/src/test/java/org/apache/commons/xml/EntityResolverFloorTest.java
+++ b/src/test/java/org/apache/commons/xml/EntityResolverFloorTest.java
@@ -74,12 +74,57 @@ class EntityResolverFloorTest {
     private static final EntityResolver ENTITY_ALLOW_LIST = (publicId, systemId) ->
             ALLOWED.equals(systemId) ? new InputSource(new URL(systemId).openStream()) : null;
 
+    /**
+     * Allow-all resolver: it denies nothing, resolving whatever {@code systemId} it is handed by opening it as a URL. It nonetheless cannot resolve a bare
+     * relative reference such as {@code referenced.xml}, because a plain {@link EntityResolver} (unlike {@link org.xml.sax.ext.EntityResolver2}) is given no
+     * base URI and the SAX2 contract promises it an already-absolutized {@code systemId}. So the resolution fails not from any deny decision but because the
+     * resolver was never handed the whole URL: it succeeds only if the floor absolutizes the XInclude href against the base before consulting the caller.
+     */
+    private static final EntityResolver RESOLVE_ALL = (publicId, systemId) -> {
+        final InputSource source = new InputSource(new URL(systemId).openStream());
+        source.setSystemId(systemId);
+        return source;
+    };
+
+    /** Absolute URL of the host document whose {@code xi:include} references {@code referenced.xml} by a relative href. */
+    private static final String XINCLUDE_HOST = AttackTestSupport.resourceUrl("with-xinclude.xml").toString();
+
+    /** Resolves only {@link #ALLOWED} to its content stream; returns {@code null} for anything else. */
+    private static final XMLResolver STAX_ALLOW_LIST = (publicID, systemID, baseURI, namespace) -> {
+        if (!ALLOWED.equals(systemID)) {
+            return null;
+        }
+        try {
+            return new URL(systemID).openStream();
+        } catch (final java.io.IOException e) {
+            throw new XMLStreamException(e);
+        }
+    };
+
+    /** Absolute location of the imported schema the allow-list resolver permits. */
+    private static final String ALLOWED_SCHEMA = AttackTestSupport.resourceUrl("included.xsd").toString();
+
+    /** Resolves only the {@code included.xsd} import; returns {@code null} for anything else. */
+    private static final LSResourceResolver SCHEMA_ALLOW_LIST = (type, namespaceURI, publicId, systemId, baseURI) ->
+            systemId != null && systemId.endsWith("included.xsd") ? lsInput(ALLOWED_SCHEMA) : null;
+
+    /** Resolves only the {@code included.xsl} import; returns {@code null} for anything else. */
+    private static final URIResolver XSL_ALLOW_LIST = (href, base) ->
+            href != null && href.endsWith("included.xsl") ? AttackTestSupport.resourceSource("included.xsl") : null;
+
     private static String entityPayload(final String entitySystemId) {
         return "<?xml version=\"1.0\"?>\n"
                 + "<!DOCTYPE root [\n  <!ENTITY xxe SYSTEM \"" + entitySystemId + "\">\n]>\n"
                 + "<root>&xxe;</root>";
     }
 
+    private static XMLInputFactory externalEntityStaxFactory() {
+        final XMLInputFactory factory = XmlFactories.newXMLInputFactory();
+        factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true);
+        factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, true);
+        return factory;
+    }
+
     private static DocumentBuilder hardenedBuilder() throws Exception {
         final DocumentBuilder builder = XmlFactories.newDocumentBuilderFactory().newDocumentBuilder();
         builder.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
@@ -92,15 +137,52 @@ private static XMLReader hardenedReader() throws Exception {
         return reader;
     }
 
-    @Test
-    @Tag("dom")
-    void domResolvesAllowListed() throws Exception {
-        Assumptions.assumeTrue(AttackTestSupport.DOM_RESOLVES_INTERNAL_ENTITIES, "platform DOM does not resolve user-defined entities");
-        final DocumentBuilder builder = hardenedBuilder();
-        builder.setEntityResolver(ENTITY_ALLOW_LIST);
-        final Document doc = builder.parse(AttackTestSupport.inputSource(entityPayload(ALLOWED)));
-        assertTrue(doc.getDocumentElement().getTextContent().contains(AttackTestSupport.LEAKED_MARKER),
-                "allow-listed external entity should resolve through the caller's resolver");
+    /**
+     * A hardened {@link TransformerFactory} with a re-throwing error listener. XSLTC and Xalan enforce the block through the
+     * {@link FallbackIgnoreURIResolver} floor; Saxon enforces it through the ignore-all resolver floor on its {@code Configuration}. Either way a caller-set
+     * resolver that returns {@code null} cannot re-open the fetch. The strict listener turns any reported-and-recovered error into a test failure, so an
+     * implementation cannot quietly recover from a floor resolution while the test asserts clean completion.
+     */
+    private static TransformerFactory hardenedTransformerFactory() {
+        final TransformerFactory factory = XmlFactories.newTransformerFactory();
+        factory.setErrorListener(AttackTestSupport.STRICT_REPORTER);
+        return factory;
+    }
+
+    /** An {@link LSInput} naming the resource but carrying no content: a redirect the implementation fetches itself, like an identifier-only {@code InputSource}. */
+    private static LSInput identifierOnlyLsInput(final String systemId) {
+        return assertDoesNotThrow(() -> {
+            final DOMImplementationLS ls = (DOMImplementationLS) DOMImplementationRegistry.newInstance().getDOMImplementation("LS");
+            final LSInput input = ls.createLSInput();
+            input.setSystemId(systemId);
+            return input;
+        }, "Failed to build identifier-only LSInput for " + systemId);
+    }
+
+    private static LSInput lsInput(final String systemId) {
+        return assertDoesNotThrow(() -> {
+            final LSInput input = identifierOnlyLsInput(systemId);
+            input.setByteStream(new URL(systemId).openStream());
+            return input;
+        }, "Failed to build LSInput for " + systemId);
+    }
+
+    private static DocumentBuilder xIncludeAwareBuilder() throws Exception {
+        final DocumentBuilderFactory factory = XmlFactories.newDocumentBuilderFactory();
+        factory.setNamespaceAware(true);
+        AttackTestSupport.assumeDoesNotThrow(() -> factory.setXIncludeAware(true));
+        final DocumentBuilder builder = factory.newDocumentBuilder();
+        builder.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
+        return builder;
+    }
+
+    private static XMLReader xIncludeAwareReader() throws Exception {
+        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
+        factory.setNamespaceAware(true);
+        AttackTestSupport.assumeDoesNotThrow(() -> factory.setXIncludeAware(true));
+        final XMLReader reader = factory.newSAXParser().getXMLReader();
+        reader.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
+        return reader;
     }
 
     @Test
@@ -120,28 +202,24 @@ void domDoesNotLeakUnlisted() throws Exception {
     }
 
     @Test
-    @Tag("sax")
-    void saxReaderResolvesAllowListed() throws Exception {
-        final XMLReader reader = hardenedReader();
-        reader.setEntityResolver(ENTITY_ALLOW_LIST);
-        final String text = AttackTestSupport.captureCharacters(reader, entityPayload(ALLOWED));
-        assertTrue(text.contains(AttackTestSupport.LEAKED_MARKER),
+    @Tag("dom")
+    void domResolvesAllowListed() throws Exception {
+        Assumptions.assumeTrue(AttackTestSupport.DOM_RESOLVES_INTERNAL_ENTITIES, "platform DOM does not resolve user-defined entities");
+        final DocumentBuilder builder = hardenedBuilder();
+        builder.setEntityResolver(ENTITY_ALLOW_LIST);
+        final Document doc = builder.parse(AttackTestSupport.inputSource(entityPayload(ALLOWED)));
+        assertTrue(doc.getDocumentElement().getTextContent().contains(AttackTestSupport.LEAKED_MARKER),
                 "allow-listed external entity should resolve through the caller's resolver");
     }
 
     @Test
-    @Tag("sax")
-    void saxReaderDoesNotLeakUnlisted() throws Exception {
-        final XMLReader reader = hardenedReader();
-        reader.setEntityResolver(ENTITY_ALLOW_LIST);
-        // The caller returns null for the unlisted entity, so the floor resolves it to empty rather than fetching it.
-        final String text;
-        try {
-            text = AttackTestSupport.captureCharacters(reader, entityPayload(UNLISTED));
-        } catch (final SAXException blocked) {
-            return; // Acceptable: rejected at parse rather than resolved to empty.
-        }
-        assertFalse(text.contains(AttackTestSupport.LEAKED_MARKER), "unlisted external entity leaked:\n" + text);
+    @Tag("dom")
+    void domResolvesRelativeXIncludeSibling() throws Exception {
+        final DocumentBuilder builder = xIncludeAwareBuilder();
+        builder.setEntityResolver(RESOLVE_ALL);
+        final Document doc = builder.parse(XINCLUDE_HOST);
+        assertTrue(doc.getDocumentElement().getTextContent().contains(AttackTestSupport.LEAKED_MARKER),
+                "relative XInclude sibling should resolve through the caller's resolver after the floor absolutizes the href");
     }
 
     @Test
@@ -159,26 +237,29 @@ void saxParseWithHandlerDoesNotBypass() throws Exception {
         assertFalse(text.toString().contains(AttackTestSupport.LEAKED_MARKER), "parse(source, handler) leaked the external entity:\n" + text);
     }
 
-    /**
-     * Allow-all resolver: it denies nothing, resolving whatever {@code systemId} it is handed by opening it as a URL. It nonetheless cannot resolve a bare
-     * relative reference such as {@code referenced.xml}, because a plain {@link EntityResolver} (unlike {@link org.xml.sax.ext.EntityResolver2}) is given no
-     * base URI and the SAX2 contract promises it an already-absolutized {@code systemId}. So the resolution fails not from any deny decision but because the
-     * resolver was never handed the whole URL: it succeeds only if the floor absolutizes the XInclude href against the base before consulting the caller.
-     */
-    private static final EntityResolver RESOLVE_ALL = (publicId, systemId) -> {
-        final InputSource source = new InputSource(new URL(systemId).openStream());
-        source.setSystemId(systemId);
-        return source;
-    };
+    @Test
+    @Tag("sax")
+    void saxReaderDoesNotLeakUnlisted() throws Exception {
+        final XMLReader reader = hardenedReader();
+        reader.setEntityResolver(ENTITY_ALLOW_LIST);
+        // The caller returns null for the unlisted entity, so the floor resolves it to empty rather than fetching it.
+        final String text;
+        try {
+            text = AttackTestSupport.captureCharacters(reader, entityPayload(UNLISTED));
+        } catch (final SAXException blocked) {
+            return; // Acceptable: rejected at parse rather than resolved to empty.
+        }
+        assertFalse(text.contains(AttackTestSupport.LEAKED_MARKER), "unlisted external entity leaked:\n" + text);
+    }
 
     @Test
-    @Tag("dom")
-    void domResolvesRelativeXIncludeSibling() throws Exception {
-        final DocumentBuilder builder = xIncludeAwareBuilder();
-        builder.setEntityResolver(RESOLVE_ALL);
-        final Document doc = builder.parse(XINCLUDE_HOST);
-        assertTrue(doc.getDocumentElement().getTextContent().contains(AttackTestSupport.LEAKED_MARKER),
-                "relative XInclude sibling should resolve through the caller's resolver after the floor absolutizes the href");
+    @Tag("sax")
+    void saxReaderResolvesAllowListed() throws Exception {
+        final XMLReader reader = hardenedReader();
+        reader.setEntityResolver(ENTITY_ALLOW_LIST);
+        final String text = AttackTestSupport.captureCharacters(reader, entityPayload(ALLOWED));
+        assertTrue(text.contains(AttackTestSupport.LEAKED_MARKER),
+                "allow-listed external entity should resolve through the caller's resolver");
     }
 
     @Test
@@ -191,64 +272,48 @@ void saxResolvesRelativeXIncludeSibling() throws Exception {
                 "relative XInclude sibling should resolve through the caller's resolver after the floor absolutizes the href");
     }
 
-    /** Absolute URL of the host document whose {@code xi:include} references {@code referenced.xml} by a relative href. */
-    private static final String XINCLUDE_HOST = AttackTestSupport.resourceUrl("with-xinclude.xml").toString();
-
-    private static DocumentBuilder xIncludeAwareBuilder() throws Exception {
-        final DocumentBuilderFactory factory = XmlFactories.newDocumentBuilderFactory();
-        factory.setNamespaceAware(true);
-        AttackTestSupport.assumeDoesNotThrow(() -> factory.setXIncludeAware(true));
-        final DocumentBuilder builder = factory.newDocumentBuilder();
-        builder.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
-        return builder;
-    }
-
-    private static XMLReader xIncludeAwareReader() throws Exception {
-        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
-        factory.setNamespaceAware(true);
-        AttackTestSupport.assumeDoesNotThrow(() -> factory.setXIncludeAware(true));
-        final XMLReader reader = factory.newSAXParser().getXMLReader();
-        reader.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
-        return reader;
+    @Test
+    @Tag("schema")
+    void schemaDeniesUnlisted() {
+        assertParseFails(() -> {
+            final SchemaFactory factory = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+            factory.setResourceResolver((type, namespaceURI, publicId, systemId, baseURI) -> null);
+            factory.newSchema(AttackTestSupport.resourceSource("with-import.xsd"));
+        }, "Schema import", SAXException.class, SecurityException.class);
     }
 
-    /** Resolves only {@link #ALLOWED} to its content stream; returns {@code null} for anything else. */
-    private static final XMLResolver STAX_ALLOW_LIST = (publicID, systemID, baseURI, namespace) -> {
-        if (!ALLOWED.equals(systemID)) {
-            return null;
-        }
-        try {
-            return new URL(systemID).openStream();
-        } catch (final java.io.IOException e) {
-            throw new XMLStreamException(e);
-        }
-    };
-
-    private static XMLInputFactory externalEntityStaxFactory() {
-        final XMLInputFactory factory = XmlFactories.newXMLInputFactory();
-        factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true);
-        factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, true);
-        return factory;
+    @Test
+    @Tag("schema")
+    void schemaFetchesIdentifierOnlyOptIn() {
+        // A non-null return is an opt-in even without content: the implementation fetches the named resource itself, mirroring the entity floor's contract.
+        assertParseSucceeds(() -> {
+            final SchemaFactory factory = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+            factory.setResourceResolver((type, namespaceURI, publicId, systemId, baseURI) ->
+                    systemId != null && systemId.endsWith("included.xsd") ? identifierOnlyLsInput(ALLOWED_SCHEMA) : null);
+            factory.newSchema(AttackTestSupport.resourceSource("with-import.xsd"));
+        }, "Schema import via identifier-only LSInput");
     }
 
     @Test
-    @Tag("stax")
-    void staxResolvesAllowListed() throws Exception {
-        final XMLInputFactory factory = externalEntityStaxFactory();
-        factory.setXMLResolver(STAX_ALLOW_LIST);
-        assertTrue(AttackTestSupport.captureStaxEventText(factory, entityPayload(ALLOWED)).contains(AttackTestSupport.LEAKED_MARKER),
-                "allow-listed external entity should resolve through the caller's resolver");
+    @Tag("schema")
+    void schemaResolvesAllowListed() {
+        // with-import.xsd references an element defined only in the imported included.xsd, so it compiles only if the import is resolved.
+        assertParseSucceeds(() -> {
+            final SchemaFactory factory = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+            factory.setResourceResolver(SCHEMA_ALLOW_LIST);
+            factory.newSchema(AttackTestSupport.resourceSource("with-import.xsd"));
+        }, "Schema import via caller resolver");
     }
 
     @Test
     @Tag("stax")
-    void staxDoesNotLeakUnlisted() throws Exception {
+    void staxCallerCannotRemoveFloor() throws Exception {
+        // A caller resolver that resolves nothing must not re-open external fetches: the floor still resolves the reference to empty rather than fetching it.
         final XMLInputFactory factory = externalEntityStaxFactory();
-        factory.setXMLResolver(STAX_ALLOW_LIST);
-        // The caller returns null for the unlisted entity, so the floor resolves it to empty rather than fetching it.
+        factory.setXMLResolver((publicID, systemID, baseURI, namespace) -> null);
         try {
-            assertFalse(AttackTestSupport.captureStaxEventText(factory, entityPayload(UNLISTED)).contains(AttackTestSupport.LEAKED_MARKER),
-                    "unlisted external entity leaked");
+            assertFalse(AttackTestSupport.captureStaxEventText(factory, entityPayload(ALLOWED)).contains(AttackTestSupport.LEAKED_MARKER),
+                    "floor was bypassed and the entity leaked");
         } catch (final XMLStreamException blocked) {
             // Acceptable: rejected at parse rather than resolved to empty.
         }
@@ -256,13 +321,13 @@ void staxDoesNotLeakUnlisted() throws Exception {
 
     @Test
     @Tag("stax")
-    void staxCallerCannotRemoveFloor() throws Exception {
-        // A caller resolver that resolves nothing must not re-open external fetches: the floor still resolves the reference to empty rather than fetching it.
+    void staxDoesNotLeakUnlisted() throws Exception {
         final XMLInputFactory factory = externalEntityStaxFactory();
-        factory.setXMLResolver((publicID, systemID, baseURI, namespace) -> null);
+        factory.setXMLResolver(STAX_ALLOW_LIST);
+        // The caller returns null for the unlisted entity, so the floor resolves it to empty rather than fetching it.
         try {
-            assertFalse(AttackTestSupport.captureStaxEventText(factory, entityPayload(ALLOWED)).contains(AttackTestSupport.LEAKED_MARKER),
-                    "floor was bypassed and the entity leaked");
+            assertFalse(AttackTestSupport.captureStaxEventText(factory, entityPayload(UNLISTED)).contains(AttackTestSupport.LEAKED_MARKER),
+                    "unlisted external entity leaked");
         } catch (final XMLStreamException blocked) {
             // Acceptable: rejected at parse rather than resolved to empty.
         }
@@ -277,75 +342,13 @@ void staxGetXMLResolverReportsCallerUnwrapped() {
         assertSame(caller, factory.getXMLResolver(), "getXMLResolver should report the caller's resolver, not the floor wrapper");
     }
 
-    /** Absolute location of the imported schema the allow-list resolver permits. */
-    private static final String ALLOWED_SCHEMA = AttackTestSupport.resourceUrl("included.xsd").toString();
-
-    /** Resolves only the {@code included.xsd} import; returns {@code null} for anything else. */
-    private static final LSResourceResolver SCHEMA_ALLOW_LIST = (type, namespaceURI, publicId, systemId, baseURI) ->
-            systemId != null && systemId.endsWith("included.xsd") ? lsInput(ALLOWED_SCHEMA) : null;
-
-    private static LSInput lsInput(final String systemId) {
-        return assertDoesNotThrow(() -> {
-            final LSInput input = identifierOnlyLsInput(systemId);
-            input.setByteStream(new URL(systemId).openStream());
-            return input;
-        }, "Failed to build LSInput for " + systemId);
-    }
-
-    /** An {@link LSInput} naming the resource but carrying no content: a redirect the implementation fetches itself, like an identifier-only {@code InputSource}. */
-    private static LSInput identifierOnlyLsInput(final String systemId) {
-        return assertDoesNotThrow(() -> {
-            final DOMImplementationLS ls = (DOMImplementationLS) DOMImplementationRegistry.newInstance().getDOMImplementation("LS");
-            final LSInput input = ls.createLSInput();
-            input.setSystemId(systemId);
-            return input;
-        }, "Failed to build identifier-only LSInput for " + systemId);
-    }
-
-    @Test
-    @Tag("schema")
-    void schemaResolvesAllowListed() {
-        // with-import.xsd references an element defined only in the imported included.xsd, so it compiles only if the import is resolved.
-        assertParseSucceeds(() -> {
-            final SchemaFactory factory = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-            factory.setResourceResolver(SCHEMA_ALLOW_LIST);
-            factory.newSchema(AttackTestSupport.resourceSource("with-import.xsd"));
-        }, "Schema import via caller resolver");
-    }
-
     @Test
-    @Tag("schema")
-    void schemaDeniesUnlisted() {
-        assertParseFails(() -> {
-            final SchemaFactory factory = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-            factory.setResourceResolver((type, namespaceURI, publicId, systemId, baseURI) -> null);
-            factory.newSchema(AttackTestSupport.resourceSource("with-import.xsd"));
-        }, "Schema import", SAXException.class, SecurityException.class);
-    }
-
-    @Test
-    @Tag("schema")
-    void schemaFetchesIdentifierOnlyOptIn() {
-        // A non-null return is an opt-in even without content: the implementation fetches the named resource itself, mirroring the entity floor's contract.
-        assertParseSucceeds(() -> {
-            final SchemaFactory factory = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-            factory.setResourceResolver((type, namespaceURI, publicId, systemId, baseURI) ->
-                    systemId != null && systemId.endsWith("included.xsd") ? identifierOnlyLsInput(ALLOWED_SCHEMA) : null);
-            factory.newSchema(AttackTestSupport.resourceSource("with-import.xsd"));
-        }, "Schema import via identifier-only LSInput");
-    }
-
-    /** Resolves only the {@code included.xsl} import; returns {@code null} for anything else. */
-    private static final URIResolver XSL_ALLOW_LIST = (href, base) ->
-            href != null && href.endsWith("included.xsl") ? AttackTestSupport.resourceSource("included.xsl") : null;
-
-    @Test
-    @Tag("trax")
-    void transformerResolvesAllowListed() {
-        // with-import.xsl imports included.xsl, so it compiles only if the import is resolved.
-        final TransformerFactory factory = hardenedTransformerFactory();
-        factory.setURIResolver(XSL_ALLOW_LIST);
-        assertParseSucceeds(() -> factory.newTemplates(AttackTestSupport.resourceSource("with-import.xsl")), "Stylesheet import via caller resolver");
+    @Tag("stax")
+    void staxResolvesAllowListed() throws Exception {
+        final XMLInputFactory factory = externalEntityStaxFactory();
+        factory.setXMLResolver(STAX_ALLOW_LIST);
+        assertTrue(AttackTestSupport.captureStaxEventText(factory, entityPayload(ALLOWED)).contains(AttackTestSupport.LEAKED_MARKER),
+                "allow-listed external entity should resolve through the caller's resolver");
     }
 
     @Test
@@ -361,6 +364,20 @@ void transformerDoesNotLeakUnlisted() {
         assertFalse(sink.toString().contains(AttackTestSupport.LEAKED_MARKER), "unlisted stylesheet import leaked");
     }
 
+    @Test
+    @Tag("trax")
+    void transformerParsesOptedInDocumentHardened() {
+        // Same contract on the runtime document() channel, which reaches a different internal reader than the compile-time import.
+        final TransformerFactory factory = hardenedTransformerFactory();
+        factory.setURIResolver((href, base) ->
+                href != null && href.endsWith("referenced.xml") ? AttackTestSupport.resourceSource("referenced-with-entity.xml") : null);
+        // Same undeclared-entity outcome as the import above: skipped, never expanded.
+        final StringWriter sink = new StringWriter();
+        assertDoesNotThrow(() -> factory.newTemplates(AttackTestSupport.resourceSource("with-document.xsl")).newTransformer()
+                .transform(AttackTestSupport.streamSource("<root/>"), new StreamResult(sink)));
+        assertFalse(sink.toString().contains(AttackTestSupport.LEAKED_MARKER), "opted-in document() resource leaked its external entity");
+    }
+
     @Test
     @Tag("trax")
     void transformerParsesOptedInImportHardened() {
@@ -378,27 +395,10 @@ void transformerParsesOptedInImportHardened() {
 
     @Test
     @Tag("trax")
-    void transformerParsesOptedInDocumentHardened() {
-        // Same contract on the runtime document() channel, which reaches a different internal reader than the compile-time import.
+    void transformerResolvesAllowListed() {
+        // with-import.xsl imports included.xsl, so it compiles only if the import is resolved.
         final TransformerFactory factory = hardenedTransformerFactory();
-        factory.setURIResolver((href, base) ->
-                href != null && href.endsWith("referenced.xml") ? AttackTestSupport.resourceSource("referenced-with-entity.xml") : null);
-        // Same undeclared-entity outcome as the import above: skipped, never expanded.
-        final StringWriter sink = new StringWriter();
-        assertDoesNotThrow(() -> factory.newTemplates(AttackTestSupport.resourceSource("with-document.xsl")).newTransformer()
-                .transform(AttackTestSupport.streamSource("<root/>"), new StreamResult(sink)));
-        assertFalse(sink.toString().contains(AttackTestSupport.LEAKED_MARKER), "opted-in document() resource leaked its external entity");
-    }
-
-    /**
-     * A hardened {@link TransformerFactory} with a re-throwing error listener. XSLTC and Xalan enforce the block through the
-     * {@link FallbackIgnoreURIResolver} floor; Saxon enforces it through the ignore-all resolver floor on its {@code Configuration}. Either way a caller-set
-     * resolver that returns {@code null} cannot re-open the fetch. The strict listener turns any reported-and-recovered error into a test failure, so an
-     * implementation cannot quietly recover from a floor resolution while the test asserts clean completion.
-     */
-    private static TransformerFactory hardenedTransformerFactory() {
-        final TransformerFactory factory = XmlFactories.newTransformerFactory();
-        factory.setErrorListener(AttackTestSupport.STRICT_REPORTER);
-        return factory;
+        factory.setURIResolver(XSL_ALLOW_LIST);
+        assertParseSucceeds(() -> factory.newTemplates(AttackTestSupport.resourceSource("with-import.xsl")), "Stylesheet import via caller resolver");
     }
 }
diff --git a/src/test/java/org/apache/commons/xml/ResetHardeningTest.java b/src/test/java/org/apache/commons/xml/ResetHardeningTest.java
index 93c0de9..687df35 100644
--- a/src/test/java/org/apache/commons/xml/ResetHardeningTest.java
+++ b/src/test/java/org/apache/commons/xml/ResetHardeningTest.java
@@ -86,20 +86,6 @@ void saxParserResetKeepsEntityResolverFloor() throws Exception {
         assertFalse(text.contains(AttackTestSupport.LEAKED_MARKER), "external entity leaked after reset:\n" + text);
     }
 
-    @Test
-    @Tag("schema")
-    void validatorResetKeepsResourceResolverFloor() throws Exception {
-        // A Schema built without sources validates against the instance's xsi:schemaLocation hints, so the resolver floor is the only barrier between the
-        // validator and the external schema fetch.
-        final Validator validator = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema().newValidator();
-        AttackTestSupport.assumeDoesNotThrow(validator::reset);
-        validator.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
-        // schema-location-instance.xml hints at schema-location.xsd, which declares its root: a validator whose floor was stripped fetches it and validates
-        // cleanly, while the floor resolves the hint to empty content, which fails the validation.
-        AttackTestSupport.assertParseFails(() -> validator.validate(AttackTestSupport.resourceSource("schema-location-instance.xml")),
-                "Validator after reset", SAXException.class, SecurityException.class);
-    }
-
     @Test
     @Tag("trax")
     void transformerResetKeepsUriResolverFloor() throws Exception {
@@ -115,4 +101,18 @@ void transformerResetKeepsUriResolverFloor() throws Exception {
         }
         assertFalse(sink.toString().contains(AttackTestSupport.LEAKED_MARKER), "document() leaked after reset:\n" + sink);
     }
+
+    @Test
+    @Tag("schema")
+    void validatorResetKeepsResourceResolverFloor() throws Exception {
+        // A Schema built without sources validates against the instance's xsi:schemaLocation hints, so the resolver floor is the only barrier between the
+        // validator and the external schema fetch.
+        final Validator validator = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema().newValidator();
+        AttackTestSupport.assumeDoesNotThrow(validator::reset);
+        validator.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
+        // schema-location-instance.xml hints at schema-location.xsd, which declares its root: a validator whose floor was stripped fetches it and validates
+        // cleanly, while the floor resolves the hint to empty content, which fails the validation.
+        AttackTestSupport.assertParseFails(() -> validator.validate(AttackTestSupport.resourceSource("schema-location-instance.xml")),
+                "Validator after reset", SAXException.class, SecurityException.class);
+    }
 }
diff --git a/src/test/java/org/apache/commons/xml/SaxSurfaceTestSupport.java b/src/test/java/org/apache/commons/xml/SaxSurfaceTestSupport.java
index 76909d4..3a52ec9 100644
--- a/src/test/java/org/apache/commons/xml/SaxSurfaceTestSupport.java
+++ b/src/test/java/org/apache/commons/xml/SaxSurfaceTestSupport.java
@@ -37,16 +37,21 @@
  */
 final class SaxSurfaceTestSupport {
 
+    /** Feeds the input's SAX events into the handler through a hardened, namespace-aware reader. */
+    static void feed(final ContentHandler handler, final InputSource input) throws Exception {
+        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
+        factory.setNamespaceAware(true);
+        final XMLReader reader = factory.newSAXParser().getXMLReader();
+        reader.setContentHandler(handler);
+        reader.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
+        reader.parse(input);
+    }
+
     /** The hardened factory, as its runtime {@link SAXTransformerFactory} type. */
     static SAXTransformerFactory hardenedFactory() {
         return (SAXTransformerFactory) XmlFactories.newTransformerFactory();
     }
 
-    /** A benign {@code <root/>} input document. */
-    static InputSource rootInput() {
-        return new InputSource(new StringReader("<root/>"));
-    }
-
     /** Opens a fixture under {@code leaked/} as an {@link InputSource} preserving its system id, so relative hrefs resolve normally. */
     static InputSource resourceInput(final String name) throws IOException {
         final URL url = AttackTestSupport.resourceUrl(name);
@@ -55,14 +60,9 @@ static InputSource resourceInput(final String name) throws IOException {
         return input;
     }
 
-    /** Feeds the input's SAX events into the handler through a hardened, namespace-aware reader. */
-    static void feed(final ContentHandler handler, final InputSource input) throws Exception {
-        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
-        factory.setNamespaceAware(true);
-        final XMLReader reader = factory.newSAXParser().getXMLReader();
-        reader.setContentHandler(handler);
-        reader.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
-        reader.parse(input);
+    /** A benign {@code <root/>} input document. */
+    static InputSource rootInput() {
+        return new InputSource(new StringReader("<root/>"));
     }
 
     private SaxSurfaceTestSupport() {
diff --git a/src/test/java/org/apache/commons/xml/SaxonAlternateFactoryTest.java b/src/test/java/org/apache/commons/xml/SaxonAlternateFactoryTest.java
index 36e15a1..ea21886 100644
--- a/src/test/java/org/apache/commons/xml/SaxonAlternateFactoryTest.java
+++ b/src/test/java/org/apache/commons/xml/SaxonAlternateFactoryTest.java
@@ -45,15 +45,6 @@ class SaxonAlternateFactoryTest {
 
     private static final String BASIC_FACTORY_CLASS = "net.sf.saxon.BasicTransformerFactory";
 
-    /** Instantiates {@code BasicTransformerFactory} reflectively, so this test compiles and loads without Saxon on the classpath. */
-    private static TransformerFactory basicSaxonFactory() {
-        try {
-            return (TransformerFactory) Class.forName(BASIC_FACTORY_CLASS).getDeclaredConstructor().newInstance();
-        } catch (final ReflectiveOperationException e) {
-            throw new AssertionError("Cannot instantiate " + BASIC_FACTORY_CLASS, e);
-        }
-    }
-
     private static void assumeSaxonPresent() {
         boolean present;
         try {
@@ -65,6 +56,15 @@ private static void assumeSaxonPresent() {
         Assumptions.assumeTrue(present, "Saxon is not on the classpath");
     }
 
+    /** Instantiates {@code BasicTransformerFactory} reflectively, so this test compiles and loads without Saxon on the classpath. */
+    private static TransformerFactory basicSaxonFactory() {
+        try {
+            return (TransformerFactory) Class.forName(BASIC_FACTORY_CLASS).getDeclaredConstructor().newInstance();
+        } catch (final ReflectiveOperationException e) {
+            throw new AssertionError("Cannot instantiate " + BASIC_FACTORY_CLASS, e);
+        }
+    }
+
     /** A {@code collection()} over the test fixtures whose {@code referenced.xml} carries {@link AttackTestSupport#LEAKED_MARKER}. */
     private static String collectionStylesheet() {
         final String collection = AttackTestSupport.resourceUrl("referenced.xml").toString().replaceFirst("referenced\\.xml$", "?select=referenced.xml");
diff --git a/src/test/java/org/apache/commons/xml/SchemaLocationDomTest.java b/src/test/java/org/apache/commons/xml/SchemaLocationDomTest.java
index 869af34..7cd4c3b 100644
--- a/src/test/java/org/apache/commons/xml/SchemaLocationDomTest.java
+++ b/src/test/java/org/apache/commons/xml/SchemaLocationDomTest.java
@@ -55,32 +55,6 @@ class SchemaLocationDomTest {
 
     private static final String INSTANCE = "schema-location-instance.xml";
 
-    @Test
-    void hardenedDoesNotFetchExternalSchema() {
-        assumeTrue(supportsSchemaLanguage(), "parser does not support JAXP 1.2 schema-language XSD validation");
-        final DocumentBuilderFactory factory = enableXsdValidation(XmlFactories.newDocumentBuilderFactory());
-        // The schemaLocation reference resolves to empty rather than being fetched. Either the empty schema fails the validating parse (acceptable), or the
-        // parse completes but the schema's default leak attribute is never inlined. Either way the marker must not reach the DOM.
-        try {
-            final Document document = parse(factory);
-            assertNotEquals(LEAKED_MARKER, document.getDocumentElement().getAttribute("leak"),
-                    "Hardened parse must not inline the external schema's default attribute.");
-        } catch (final Exception blocked) {
-            // Acceptable: the empty schema was rejected at parse time, so nothing was fetched or inlined.
-        }
-    }
-
-    @Test
-    void unconfiguredFetchesExternalSchema() throws Exception {
-        assumeTrue(supportsSchemaLanguage(), "parser does not support JAXP 1.2 schema-language XSD validation");
-        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
-        // Positive control: without hardening the external schema is fetched and its default attribute is inlined into the DOM.
-        final Document document = parse(enableXsdValidation(factory));
-        assertEquals(LEAKED_MARKER, document.getDocumentElement().getAttribute("leak"),
-                "Permissive parse should have fetched the external schema and inlined its default attribute.");
-    }
-
     private static DocumentBuilderFactory enableXsdValidation(final DocumentBuilderFactory factory) {
         factory.setNamespaceAware(true);
         factory.setValidating(true);
@@ -109,4 +83,30 @@ private static boolean supportsSchemaLanguage() {
             return false;
         }
     }
+
+    @Test
+    void hardenedDoesNotFetchExternalSchema() {
+        assumeTrue(supportsSchemaLanguage(), "parser does not support JAXP 1.2 schema-language XSD validation");
+        final DocumentBuilderFactory factory = enableXsdValidation(XmlFactories.newDocumentBuilderFactory());
+        // The schemaLocation reference resolves to empty rather than being fetched. Either the empty schema fails the validating parse (acceptable), or the
+        // parse completes but the schema's default leak attribute is never inlined. Either way the marker must not reach the DOM.
+        try {
+            final Document document = parse(factory);
+            assertNotEquals(LEAKED_MARKER, document.getDocumentElement().getAttribute("leak"),
+                    "Hardened parse must not inline the external schema's default attribute.");
+        } catch (final Exception blocked) {
+            // Acceptable: the empty schema was rejected at parse time, so nothing was fetched or inlined.
+        }
+    }
+
+    @Test
+    void unconfiguredFetchesExternalSchema() throws Exception {
+        assumeTrue(supportsSchemaLanguage(), "parser does not support JAXP 1.2 schema-language XSD validation");
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
+        // Positive control: without hardening the external schema is fetched and its default attribute is inlined into the DOM.
+        final Document document = parse(enableXsdValidation(factory));
+        assertEquals(LEAKED_MARKER, document.getDocumentElement().getAttribute("leak"),
+                "Permissive parse should have fetched the external schema and inlined its default attribute.");
+    }
 }
diff --git a/src/test/java/org/apache/commons/xml/SchemaLocationPropertyTest.java b/src/test/java/org/apache/commons/xml/SchemaLocationPropertyTest.java
index 8fc03db..127c920 100644
--- a/src/test/java/org/apache/commons/xml/SchemaLocationPropertyTest.java
+++ b/src/test/java/org/apache/commons/xml/SchemaLocationPropertyTest.java
@@ -68,14 +68,6 @@ class SchemaLocationPropertyTest {
     /** Instance whose root, {@code l:leaked}, is declared by {@code included.xsd} in the {@value #LEAKED_NS} namespace. */
     private static final String NAMESPACED_INSTANCE = "<l:leaked xmlns:l=\"" + LEAKED_NS + "\">x</l:leaked>";
 
-    private static String noNamespaceLocation() {
-        return resourceUrl("no-namespace.xsd").toString();
-    }
-
-    private static String namespacedLocation() {
-        return LEAKED_NS + " " + resourceUrl("included.xsd");
-    }
-
     /**
      * Runs the parser setup, skipping the test (rather than failing it) on parsers that do not accept these
      * schema-validation features/properties, such as Android's KXmlParser and Expat.
@@ -99,18 +91,6 @@ private static DocumentBuilder hardenedValidatingDom(final String property, fina
         });
     }
 
-    private static DocumentBuilder permissiveValidatingDom(final String property, final String value) {
-        return configureOrSkip(() -> {
-            final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-            factory.setNamespaceAware(true);
-            factory.setValidating(true);
-            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
-            factory.setAttribute(SCHEMA_LANGUAGE, XMLConstants.W3C_XML_SCHEMA_NS_URI);
-            factory.setAttribute(property, value);
-            return strictDocumentBuilder(factory);
-        });
-    }
-
     private static XMLReader hardenedValidatingSax(final String property, final String value) {
         return configureOrSkip(() -> {
             final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
@@ -123,6 +103,26 @@ private static XMLReader hardenedValidatingSax(final String property, final Stri
         });
     }
 
+    private static String namespacedLocation() {
+        return LEAKED_NS + " " + resourceUrl("included.xsd");
+    }
+
+    private static String noNamespaceLocation() {
+        return resourceUrl("no-namespace.xsd").toString();
+    }
+
+    private static DocumentBuilder permissiveValidatingDom(final String property, final String value) {
+        return configureOrSkip(() -> {
+            final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+            factory.setNamespaceAware(true);
+            factory.setValidating(true);
+            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
+            factory.setAttribute(SCHEMA_LANGUAGE, XMLConstants.W3C_XML_SCHEMA_NS_URI);
+            factory.setAttribute(property, value);
+            return strictDocumentBuilder(factory);
+        });
+    }
+
     private static XMLReader permissiveValidatingSax(final String property, final String value) {
         return configureOrSkip(() -> {
             final SAXParserFactory factory = SAXParserFactory.newInstance();
@@ -143,42 +143,42 @@ void hardenedDomRefusesNoNamespaceSchemaLocation() {
         assertParseFails(() -> builder.parse(inputSource(NO_NS_INSTANCE)), "DOM external-noNamespaceSchemaLocation", SAXException.class);
     }
 
-    @Test
-    void permissiveDomFetchesNoNamespaceSchemaLocation() {
-        final DocumentBuilder builder = permissiveValidatingDom(EXTERNAL_NO_NS, noNamespaceLocation());
-        assertParseSucceeds(() -> builder.parse(inputSource(NO_NS_INSTANCE)), "DOM external-noNamespaceSchemaLocation (permissive)");
-    }
-
     @Test
     void hardenedDomRefusesSchemaLocation() {
         final DocumentBuilder builder = hardenedValidatingDom(EXTERNAL_SCHEMA_LOCATION, namespacedLocation());
         assertParseFails(() -> builder.parse(inputSource(NAMESPACED_INSTANCE)), "DOM external-schemaLocation", SAXException.class);
     }
 
-    @Test
-    void permissiveDomFetchesSchemaLocation() {
-        final DocumentBuilder builder = permissiveValidatingDom(EXTERNAL_SCHEMA_LOCATION, namespacedLocation());
-        assertParseSucceeds(() -> builder.parse(inputSource(NAMESPACED_INSTANCE)), "DOM external-schemaLocation (permissive)");
-    }
-
     @Test
     void hardenedSaxRefusesNoNamespaceSchemaLocation() {
         final XMLReader reader = hardenedValidatingSax(EXTERNAL_NO_NS, noNamespaceLocation());
         assertParseFails(() -> reader.parse(inputSource(NO_NS_INSTANCE)), "SAX external-noNamespaceSchemaLocation", SAXException.class);
     }
 
-    @Test
-    void permissiveSaxFetchesNoNamespaceSchemaLocation() {
-        final XMLReader reader = permissiveValidatingSax(EXTERNAL_NO_NS, noNamespaceLocation());
-        assertParseSucceeds(() -> reader.parse(inputSource(NO_NS_INSTANCE)), "SAX external-noNamespaceSchemaLocation (permissive)");
-    }
-
     @Test
     void hardenedSaxRefusesSchemaLocation() {
         final XMLReader reader = hardenedValidatingSax(EXTERNAL_SCHEMA_LOCATION, namespacedLocation());
         assertParseFails(() -> reader.parse(inputSource(NAMESPACED_INSTANCE)), "SAX external-schemaLocation", SAXException.class);
     }
 
+    @Test
+    void permissiveDomFetchesNoNamespaceSchemaLocation() {
+        final DocumentBuilder builder = permissiveValidatingDom(EXTERNAL_NO_NS, noNamespaceLocation());
+        assertParseSucceeds(() -> builder.parse(inputSource(NO_NS_INSTANCE)), "DOM external-noNamespaceSchemaLocation (permissive)");
+    }
+
+    @Test
+    void permissiveDomFetchesSchemaLocation() {
+        final DocumentBuilder builder = permissiveValidatingDom(EXTERNAL_SCHEMA_LOCATION, namespacedLocation());
+        assertParseSucceeds(() -> builder.parse(inputSource(NAMESPACED_INSTANCE)), "DOM external-schemaLocation (permissive)");
+    }
+
+    @Test
+    void permissiveSaxFetchesNoNamespaceSchemaLocation() {
+        final XMLReader reader = permissiveValidatingSax(EXTERNAL_NO_NS, noNamespaceLocation());
+        assertParseSucceeds(() -> reader.parse(inputSource(NO_NS_INSTANCE)), "SAX external-noNamespaceSchemaLocation (permissive)");
+    }
+
     @Test
     void permissiveSaxFetchesSchemaLocation() {
         final XMLReader reader = permissiveValidatingSax(EXTERNAL_SCHEMA_LOCATION, namespacedLocation());
diff --git a/src/test/java/org/apache/commons/xml/SchemaLocationSaxTest.java b/src/test/java/org/apache/commons/xml/SchemaLocationSaxTest.java
index b7317e1..7d30e35 100644
--- a/src/test/java/org/apache/commons/xml/SchemaLocationSaxTest.java
+++ b/src/test/java/org/apache/commons/xml/SchemaLocationSaxTest.java
@@ -67,34 +67,6 @@ public void startElement(final String uri, final String localName, final String
 
     private static final String INSTANCE = "schema-location-instance.xml";
 
-    @Test
-    void hardenedDoesNotFetchExternalSchema() throws Exception {
-        assumeTrue(supportsSchemaLanguage(), "parser does not support JAXP 1.2 schema-language XSD validation");
-        final SAXParser parser = newValidatingParser(XmlFactories.newSAXParserFactory());
-        // The schemaLocation reference resolves to empty rather than being fetched. Either the empty schema fails the validating parse (acceptable), or the
-        // parse completes but the schema's default leak attribute is never augmented onto the element. Either way the marker must not be observed.
-        final LeakCapturingHandler handler = new LeakCapturingHandler();
-        try {
-            parse(parser, handler);
-        } catch (final Exception blocked) {
-            // Acceptable: the empty schema was rejected at parse time, so nothing was fetched or augmented.
-        }
-        assertNull(handler.leak, "Hardened parse must not augment the external schema's default attribute onto the element.");
-    }
-
-    @Test
-    void unconfiguredFetchesExternalSchema() throws Exception {
-        assumeTrue(supportsSchemaLanguage(), "parser does not support JAXP 1.2 schema-language XSD validation");
-        final SAXParserFactory factory = SAXParserFactory.newInstance();
-        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
-        final SAXParser parser = newValidatingParser(factory);
-        // Positive control: without hardening the external schema is fetched and its default attribute is augmented onto the root element.
-        final LeakCapturingHandler handler = new LeakCapturingHandler();
-        parse(parser, handler);
-        assertEquals(LEAKED_MARKER, handler.leak,
-                "Permissive parse should have fetched the external schema and augmented its default attribute onto the element.");
-    }
-
     private static SAXParser newValidatingParser(final SAXParserFactory factory) throws Exception {
         factory.setNamespaceAware(true);
         factory.setValidating(true);
@@ -124,4 +96,32 @@ private static boolean supportsSchemaLanguage() {
             return false;
         }
     }
+
+    @Test
+    void hardenedDoesNotFetchExternalSchema() throws Exception {
+        assumeTrue(supportsSchemaLanguage(), "parser does not support JAXP 1.2 schema-language XSD validation");
+        final SAXParser parser = newValidatingParser(XmlFactories.newSAXParserFactory());
+        // The schemaLocation reference resolves to empty rather than being fetched. Either the empty schema fails the validating parse (acceptable), or the
+        // parse completes but the schema's default leak attribute is never augmented onto the element. Either way the marker must not be observed.
+        final LeakCapturingHandler handler = new LeakCapturingHandler();
+        try {
+            parse(parser, handler);
+        } catch (final Exception blocked) {
+            // Acceptable: the empty schema was rejected at parse time, so nothing was fetched or augmented.
+        }
+        assertNull(handler.leak, "Hardened parse must not augment the external schema's default attribute onto the element.");
+    }
+
+    @Test
+    void unconfiguredFetchesExternalSchema() throws Exception {
+        assumeTrue(supportsSchemaLanguage(), "parser does not support JAXP 1.2 schema-language XSD validation");
+        final SAXParserFactory factory = SAXParserFactory.newInstance();
+        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
+        final SAXParser parser = newValidatingParser(factory);
+        // Positive control: without hardening the external schema is fetched and its default attribute is augmented onto the root element.
+        final LeakCapturingHandler handler = new LeakCapturingHandler();
+        parse(parser, handler);
+        assertEquals(LEAKED_MARKER, handler.leak,
+                "Permissive parse should have fetched the external schema and augmented its default attribute onto the element.");
+    }
 }
diff --git a/src/test/java/org/apache/commons/xml/ShadingFootprintTest.java b/src/test/java/org/apache/commons/xml/ShadingFootprintTest.java
index 4972cba..53ad525 100644
--- a/src/test/java/org/apache/commons/xml/ShadingFootprintTest.java
+++ b/src/test/java/org/apache/commons/xml/ShadingFootprintTest.java
@@ -100,6 +100,39 @@ class ShadingFootprintTest {
     private static Clazzpath clazzpath;
     private static Path classesDir;
 
+    /**
+     * Sums the uncompressed {@code .class} file sizes of a closure's classes, as they would land in a shaded jar.
+     */
+    private static long bytesOf(final Set<String> simpleNames) {
+        long total = 0;
+        for (final String name : simpleNames) {
+            try {
+                total += Files.size(classesDir.resolve("org/apache/commons/xml/" + name + ".class"));
+            } catch (final IOException e) {
+                throw new UncheckedIOException(e);
+            }
+        }
+        return total;
+    }
+
+    /**
+     * Transitive class closure of {@code PKG + simpleName}, restricted to this library's own package and reported by simple name.
+     */
+    private static Set<String> closureOf(final String simpleName) {
+        final Clazz entry = clazzpath.getClazz(PKG + simpleName);
+        if (entry == null) {
+            throw new IllegalStateException("Not on the compiled classpath: " + PKG + simpleName);
+        }
+        final Set<String> names = new TreeSet<>();
+        names.add(strip(entry.getName()));
+        for (final Clazz dependency : entry.getTransitiveDependencies()) {
+            if (dependency.getName().startsWith(PKG)) {
+                names.add(strip(dependency.getName()));
+            }
+        }
+        return names;
+    }
+
     @BeforeAll
     static void indexCompiledClasses() throws Exception {
         classesDir = Paths.get(HardeningException.class.getProtectionDomain().getCodeSource().getLocation().toURI());
@@ -123,29 +156,36 @@ static void reportFootprint() {
         System.out.print(report);
     }
 
-    @Test
-    void documentBuilderHardenerFootprint() {
-        assertEquals(DOCUMENT_BUILDER_HARDENER, closureOf("DocumentBuilderHardener"));
+    /**
+     * {@link #SAX_PARSER_HARDENER} plus the extra names; used where an entry point's closure is the SAX path plus its own classes.
+     */
+    private static Set<String> saxParsersHardenerPlus(final String... more) {
+        final Set<String> union = new TreeSet<>(SAX_PARSER_HARDENER);
+        union.addAll(Arrays.asList(more));
+        return union;
     }
 
-    @Test
-    void saxParserHardenerFootprint() {
-        assertEquals(SAX_PARSER_HARDENER, closureOf("SAXParserHardener"));
+    private static Set<String> set(final String... names) {
+        return new TreeSet<>(Arrays.asList(names));
+    }
+
+    private static String strip(final String qualifiedName) {
+        return qualifiedName.substring(PKG.length());
     }
 
     @Test
-    void staxHardenerFootprint() {
-        assertEquals(STAX_HARDENER, closureOf("StaxHardener"));
+    void documentBuilderHardenerFootprint() {
+        assertEquals(DOCUMENT_BUILDER_HARDENER, closureOf("DocumentBuilderHardener"));
     }
 
     @Test
-    void transformerHardenerFootprint() {
-        assertEquals(TRANSFORMER_HARDENER, closureOf("TransformerHardener"));
+    void onlyXmlFactoriesPullsTheWholeLibrary() {
+        assertEquals(LIBRARY_CLASS_COUNT, closureOf("XmlFactories").size(), "XmlFactories closure size drifted");
     }
 
     @Test
-    void xPathHardenerFootprint() {
-        assertEquals(XPATH_HARDENER, closureOf("XPathHardener"));
+    void saxParserHardenerFootprint() {
+        assertEquals(SAX_PARSER_HARDENER, closureOf("SAXParserHardener"));
     }
 
     @Test
@@ -154,57 +194,17 @@ void schemaHardenerFootprint() {
     }
 
     @Test
-    void onlyXmlFactoriesPullsTheWholeLibrary() {
-        assertEquals(LIBRARY_CLASS_COUNT, closureOf("XmlFactories").size(), "XmlFactories closure size drifted");
-    }
-
-    /**
-     * Transitive class closure of {@code PKG + simpleName}, restricted to this library's own package and reported by simple name.
-     */
-    private static Set<String> closureOf(final String simpleName) {
-        final Clazz entry = clazzpath.getClazz(PKG + simpleName);
-        if (entry == null) {
-            throw new IllegalStateException("Not on the compiled classpath: " + PKG + simpleName);
-        }
-        final Set<String> names = new TreeSet<>();
-        names.add(strip(entry.getName()));
-        for (final Clazz dependency : entry.getTransitiveDependencies()) {
-            if (dependency.getName().startsWith(PKG)) {
-                names.add(strip(dependency.getName()));
-            }
-        }
-        return names;
-    }
-
-    /**
-     * Sums the uncompressed {@code .class} file sizes of a closure's classes, as they would land in a shaded jar.
-     */
-    private static long bytesOf(final Set<String> simpleNames) {
-        long total = 0;
-        for (final String name : simpleNames) {
-            try {
-                total += Files.size(classesDir.resolve("org/apache/commons/xml/" + name + ".class"));
-            } catch (final IOException e) {
-                throw new UncheckedIOException(e);
-            }
-        }
-        return total;
-    }
-
-    private static String strip(final String qualifiedName) {
-        return qualifiedName.substring(PKG.length());
+    void staxHardenerFootprint() {
+        assertEquals(STAX_HARDENER, closureOf("StaxHardener"));
     }
 
-    private static Set<String> set(final String... names) {
-        return new TreeSet<>(Arrays.asList(names));
+    @Test
+    void transformerHardenerFootprint() {
+        assertEquals(TRANSFORMER_HARDENER, closureOf("TransformerHardener"));
     }
 
-    /**
-     * {@link #SAX_PARSER_HARDENER} plus the extra names; used where an entry point's closure is the SAX path plus its own classes.
-     */
-    private static Set<String> saxParsersHardenerPlus(final String... more) {
-        final Set<String> union = new TreeSet<>(SAX_PARSER_HARDENER);
-        union.addAll(Arrays.asList(more));
-        return union;
+    @Test
+    void xPathHardenerFootprint() {
+        assertEquals(XPATH_HARDENER, closureOf("XPathHardener"));
     }
 }
diff --git a/src/test/java/org/apache/commons/xml/TemplatesHandlerTest.java b/src/test/java/org/apache/commons/xml/TemplatesHandlerTest.java
index 348aa80..360ff6c 100644
--- a/src/test/java/org/apache/commons/xml/TemplatesHandlerTest.java
+++ b/src/test/java/org/apache/commons/xml/TemplatesHandlerTest.java
@@ -53,13 +53,6 @@ private static String compileAndTransform(final TemplatesHandler handler, final
         return sink.toString();
     }
 
-    @Test
-    void hardenedTemplatesHandlerDoesNotLeakInclude() throws Exception {
-        final TemplatesHandler handler = SaxSurfaceTestSupport.hardenedFactory().newTemplatesHandler();
-        assertFalse(compileAndTransform(handler, "with-include.xsl").contains(AttackTestSupport.LEAKED_MARKER),
-                "xsl:include through TemplatesHandler leaked");
-    }
-
     @Test
     void hardenedTemplatesHandlerDoesNotLeakDocument() throws Exception {
         // The f004 product path: the Templates from getTemplates() must produce floored Transformers for runtime document().
@@ -68,6 +61,13 @@ void hardenedTemplatesHandlerDoesNotLeakDocument() throws Exception {
                 "document() through TemplatesHandler.getTemplates() leaked");
     }
 
+    @Test
+    void hardenedTemplatesHandlerDoesNotLeakInclude() throws Exception {
+        final TemplatesHandler handler = SaxSurfaceTestSupport.hardenedFactory().newTemplatesHandler();
+        assertFalse(compileAndTransform(handler, "with-include.xsl").contains(AttackTestSupport.LEAKED_MARKER),
+                "xsl:include through TemplatesHandler leaked");
+    }
+
     @Test
     void unconfiguredTemplatesHandlerLeaksDocument() throws Exception {
         final TemplatesHandler handler = ((SAXTransformerFactory) TransformerFactory.newInstance()).newTemplatesHandler();
diff --git a/src/test/java/org/apache/commons/xml/TransformerHandlerTest.java b/src/test/java/org/apache/commons/xml/TransformerHandlerTest.java
index 767b4f4..89060cd 100644
--- a/src/test/java/org/apache/commons/xml/TransformerHandlerTest.java
+++ b/src/test/java/org/apache/commons/xml/TransformerHandlerTest.java
@@ -48,6 +48,16 @@ private static String transformViaHandler(final TransformerHandler handler) thro
         return sink.toString();
     }
 
+    @Test
+    void hardenedGetTransformerDoesNotLeakDocument() throws Exception {
+        // The f004 bypass: pull the inner Transformer out of the handler and transform directly; the floor must ride along.
+        final SAXTransformerFactory factory = SaxSurfaceTestSupport.hardenedFactory();
+        final TransformerHandler handler = factory.newTransformerHandler(AttackTestSupport.resourceSource("with-document.xsl"));
+        final StringWriter sink = new StringWriter();
+        handler.getTransformer().transform(AttackTestSupport.streamSource("<root/>"), new StreamResult(sink));
+        assertFalse(sink.toString().contains(AttackTestSupport.LEAKED_MARKER), "document() through getTransformer() leaked");
+    }
+
     @Test
     void hardenedTransformerHandlerDoesNotLeakDocument() throws Exception {
         final SAXTransformerFactory factory = SaxSurfaceTestSupport.hardenedFactory();
@@ -65,16 +75,6 @@ void hardenedTransformerHandlerFromTemplatesDoesNotLeakDocument() throws Excepti
                 "document() through TransformerHandler(Templates) leaked");
     }
 
-    @Test
-    void hardenedGetTransformerDoesNotLeakDocument() throws Exception {
-        // The f004 bypass: pull the inner Transformer out of the handler and transform directly; the floor must ride along.
-        final SAXTransformerFactory factory = SaxSurfaceTestSupport.hardenedFactory();
-        final TransformerHandler handler = factory.newTransformerHandler(AttackTestSupport.resourceSource("with-document.xsl"));
-        final StringWriter sink = new StringWriter();
-        handler.getTransformer().transform(AttackTestSupport.streamSource("<root/>"), new StreamResult(sink));
-        assertFalse(sink.toString().contains(AttackTestSupport.LEAKED_MARKER), "document() through getTransformer() leaked");
-    }
-
     @Test
     void unconfiguredTransformerHandlerLeaksDocument() throws Exception {
         final SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();
diff --git a/src/test/java/org/apache/commons/xml/UnsupportedXmlImplementationTest.java b/src/test/java/org/apache/commons/xml/UnsupportedXmlImplementationTest.java
index 138eb00..7d9d01e 100644
--- a/src/test/java/org/apache/commons/xml/UnsupportedXmlImplementationTest.java
+++ b/src/test/java/org/apache/commons/xml/UnsupportedXmlImplementationTest.java
@@ -43,28 +43,28 @@ class UnsupportedXmlImplementationTest {
     public static final class FakeDocumentBuilderFactory extends DocumentBuilderFactory {
 
         @Override
-        public DocumentBuilder newDocumentBuilder() {
-            throw new UnsupportedOperationException();
+        public Object getAttribute(final String name) {
+            return null;
         }
 
         @Override
-        public void setAttribute(final String name, final Object value) {
-            // no-op
+        public boolean getFeature(final String name) {
+            return false;
         }
 
         @Override
-        public Object getAttribute(final String name) {
-            return null;
+        public DocumentBuilder newDocumentBuilder() {
+            throw new UnsupportedOperationException();
         }
 
         @Override
-        public void setFeature(final String name, final boolean value) throws ParserConfigurationException {
-            throw new ParserConfigurationException("feature not recognized: " + name);
+        public void setAttribute(final String name, final Object value) {
+            // no-op
         }
 
         @Override
-        public boolean getFeature(final String name) {
-            return false;
+        public void setFeature(final String name, final boolean value) throws ParserConfigurationException {
+            throw new ParserConfigurationException("feature not recognized: " + name);
         }
     }
 
@@ -73,6 +73,11 @@ public boolean getFeature(final String name) {
      */
     public static final class FakeSAXParserFactory extends SAXParserFactory {
 
+        @Override
+        public boolean getFeature(final String name) throws SAXNotSupportedException {
+            throw new SAXNotSupportedException("feature not recognized: " + name);
+        }
+
         @Override
         public SAXParser newSAXParser() {
             throw new UnsupportedOperationException();
@@ -82,11 +87,6 @@ public SAXParser newSAXParser() {
         public void setFeature(final String name, final boolean value) throws SAXNotRecognizedException {
             throw new SAXNotRecognizedException("feature not recognized: " + name);
         }
-
-        @Override
-        public boolean getFeature(final String name) throws SAXNotSupportedException {
-            throw new SAXNotSupportedException("feature not recognized: " + name);
-        }
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/xml/XIncludeTest.java b/src/test/java/org/apache/commons/xml/XIncludeTest.java
index 84a3933..0d4d4a0 100644
--- a/src/test/java/org/apache/commons/xml/XIncludeTest.java
+++ b/src/test/java/org/apache/commons/xml/XIncludeTest.java
@@ -53,15 +53,6 @@
  */
 class XIncludeTest {
 
-    /** Absolute URL of the XML fixture pulled in by {@code parse="xml"} includes; carries {@link AttackTestSupport#LEAKED_MARKER}. */
-    private static final String REFERENCED_XML = resourceUrl("referenced.xml").toString();
-
-    /** Absolute URL of the text fixture pulled in by {@code parse="text"} includes; carries {@link AttackTestSupport#LEAKED_MARKER}. */
-    private static final String REFERENCED_TEXT = resourceUrl("referenced.txt").toString();
-
-    /** Content the allow-list resolver returns for an allowed include; its presence proves the caller's resolver was consulted. */
-    private static final String RESOLVED_MARKER = "XINCLUDE-RESOLVED-905bbbce-16ee-4a0c-b165-d1f8c663934c";
-
     /**
      * Allow-lists the two fixture URLs, returning the appropriate in-memory content for each: {@link #RESOLVED_MARKER}
      * wrapped as XML for {@link #REFERENCED_XML}, and as plain text for {@link #REFERENCED_TEXT}. Anything else returns
@@ -85,17 +76,18 @@ public InputSource resolveEntity(final String publicId, final String systemId) {
         }
     }
 
+    /** Absolute URL of the XML fixture pulled in by {@code parse="xml"} includes; carries {@link AttackTestSupport#LEAKED_MARKER}. */
+    private static final String REFERENCED_XML = resourceUrl("referenced.xml").toString();
+
+    /** Absolute URL of the text fixture pulled in by {@code parse="text"} includes; carries {@link AttackTestSupport#LEAKED_MARKER}. */
+    private static final String REFERENCED_TEXT = resourceUrl("referenced.txt").toString();
+
+    /** Content the allow-list resolver returns for an allowed include; its presence proves the caller's resolver was consulted. */
+    private static final String RESOLVED_MARKER = "XINCLUDE-RESOLVED-905bbbce-16ee-4a0c-b165-d1f8c663934c";
+
     /** Resolver that resolves nothing, so the hardening's ignore-all floor must empty every lookup and never leak. */
     private static final EntityResolver NO_OP_RESOLVER = (publicId, systemId) -> null;
 
-    /** XML wrapper for xi:include in the given {@code parse} mode referencing {@code href}. */
-    private static String xiIncludeXml(final String href, final String parseMode) {
-        return "<?xml version=\"1.0\"?>\n"
-                + "<root xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n"
-                + "  <xi:include href=\"" + href + "\" parse=\"" + parseMode + "\"/>\n"
-                + "</root>";
-    }
-
     /**
      * Enables XInclude on the factory under test, skipping the test when the platform refuses.
      *
@@ -122,60 +114,68 @@ private static void assumeXIncludeAware(final SAXParserFactory factory) {
         }
     }
 
+    /** XML wrapper for xi:include in the given {@code parse} mode referencing {@code href}. */
+    private static String xiIncludeXml(final String href, final String parseMode) {
+        return "<?xml version=\"1.0\"?>\n"
+                + "<root xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n"
+                + "  <xi:include href=\"" + href + "\" parse=\"" + parseMode + "\"/>\n"
+                + "</root>";
+    }
+
     //region Baseline: unhardened JAXP resolves the include
 
     @Test
     @Tag("dom")
-    void baselineDomLeaksParseXml() throws Exception {
-        final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, "xml"));
+    void baselineDomLeaksParseText() throws Exception {
+        final InputSource input = inputSource(xiIncludeXml(REFERENCED_TEXT, "text"));
 
         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final Document doc = factory.newDocumentBuilder().parse(input);
         final String text = doc.getDocumentElement().getTextContent();
-        assertEquals(LEAKED_MARKER, text.trim(),
-                "Baseline DOM parse=xml should leak marker; got: " + text);
+        assertTrue(text != null && text.contains(LEAKED_MARKER),
+                "Baseline DOM parse=text should leak marker; got: " + text);
     }
 
     @Test
     @Tag("dom")
-    void baselineDomLeaksParseText() throws Exception {
-        final InputSource input = inputSource(xiIncludeXml(REFERENCED_TEXT, "text"));
+    void baselineDomLeaksParseXml() throws Exception {
+        final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, "xml"));
 
         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final Document doc = factory.newDocumentBuilder().parse(input);
         final String text = doc.getDocumentElement().getTextContent();
-        assertTrue(text != null && text.contains(LEAKED_MARKER),
-                "Baseline DOM parse=text should leak marker; got: " + text);
+        assertEquals(LEAKED_MARKER, text.trim(),
+                "Baseline DOM parse=xml should leak marker; got: " + text);
     }
 
     @Test
     @Tag("sax")
-    void baselineSaxLeaksParseXml() throws Exception {
-        final String input = xiIncludeXml(REFERENCED_XML, "xml");
+    void baselineSaxLeaksParseText() throws Exception {
+        final String input = xiIncludeXml(REFERENCED_TEXT, "text");
 
         final SAXParserFactory factory = SAXParserFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final String captured = captureCharacters(factory.newSAXParser().getXMLReader(), input);
-        assertEquals(LEAKED_MARKER, captured.trim(),
-                "Baseline SAX parse=xml should leak marker; got: " + captured);
+        assertTrue(captured.contains(LEAKED_MARKER),
+                "Baseline SAX parse=text should leak marker; got: " + captured);
     }
 
     @Test
     @Tag("sax")
-    void baselineSaxLeaksParseText() throws Exception {
-        final String input = xiIncludeXml(REFERENCED_TEXT, "text");
+    void baselineSaxLeaksParseXml() throws Exception {
+        final String input = xiIncludeXml(REFERENCED_XML, "xml");
 
         final SAXParserFactory factory = SAXParserFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final String captured = captureCharacters(factory.newSAXParser().getXMLReader(), input);
-        assertTrue(captured.contains(LEAKED_MARKER),
-                "Baseline SAX parse=text should leak marker; got: " + captured);
+        assertEquals(LEAKED_MARKER, captured.trim(),
+                "Baseline SAX parse=xml should leak marker; got: " + captured);
     }
 
     //endregion
@@ -184,57 +184,59 @@ void baselineSaxLeaksParseText() throws Exception {
 
     @Test
     @Tag("dom")
-    void hardenedDomBlocksParseXml() throws Exception {
-        final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, "xml"));
+    void hardenedDomBlocksParseText() throws Exception {
+        final InputSource input = inputSource(xiIncludeXml(REFERENCED_TEXT, "text"));
 
         final DocumentBuilderFactory factory = XmlFactories.newDocumentBuilderFactory();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
-        assertThrows(SAXException.class, () -> {
-            final DocumentBuilder builder = factory.newDocumentBuilder();
-            builder.parse(input);
-        }, "Hardened DOM parse=xml should throw");
+        final Document doc = factory.newDocumentBuilder().parse(input);
+        final String text = doc.getDocumentElement().getTextContent();
+        assertFalse(text.contains(LEAKED_MARKER),
+                "Hardened DOM parse=text must resolve the include to empty, not leak; got: " + text);
     }
 
     @Test
     @Tag("dom")
-    void hardenedDomBlocksParseText() throws Exception {
-        final InputSource input = inputSource(xiIncludeXml(REFERENCED_TEXT, "text"));
+    void hardenedDomBlocksParseXml() throws Exception {
+        final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, "xml"));
 
         final DocumentBuilderFactory factory = XmlFactories.newDocumentBuilderFactory();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
-        final Document doc = factory.newDocumentBuilder().parse(input);
-        final String text = doc.getDocumentElement().getTextContent();
-        assertFalse(text.contains(LEAKED_MARKER),
-                "Hardened DOM parse=text must resolve the include to empty, not leak; got: " + text);
+        assertThrows(SAXException.class, () -> {
+            final DocumentBuilder builder = factory.newDocumentBuilder();
+            builder.parse(input);
+        }, "Hardened DOM parse=xml should throw");
     }
 
     @Test
-    @Tag("sax")
-    void hardenedSaxBlocksParseXml() throws Exception {
+    @Tag("dom")
+    void hardenedDomNullResolverDoesNotLeak() throws Exception {
         final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, "xml"));
 
-        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
+        final DocumentBuilderFactory factory = XmlFactories.newDocumentBuilderFactory();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
-        assertThrows(SAXException.class, () -> {
-            final XMLReader reader = factory.newSAXParser().getXMLReader();
-            reader.parse(input);
-        }, "Hardened SAX parse=xml should throw");
+        final DocumentBuilder builder = factory.newDocumentBuilder();
+        builder.setEntityResolver(NO_OP_RESOLVER);
+        assertThrows(SAXException.class, () -> builder.parse(input),
+                "a resolver that returns null must not leak: the ignore-all floor blocks the real href");
     }
 
     @Test
-    @Tag("sax")
-    void hardenedSaxBlocksParseText() throws Exception {
-        final String input = xiIncludeXml(REFERENCED_TEXT, "text");
+    @Tag("dom")
+    void hardenedDomWithAllowListResolvesParseText() throws Exception {
+        final InputSource input = inputSource(xiIncludeXml(REFERENCED_TEXT, "text"));
 
-        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
+        final DocumentBuilderFactory factory = XmlFactories.newDocumentBuilderFactory();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
-        final String captured = captureCharacters(factory.newSAXParser().getXMLReader(), input);
-        assertFalse(captured.contains(LEAKED_MARKER),
-                "Hardened SAX parse=text must resolve the include to empty, not leak; got: " + captured);
+        final DocumentBuilder builder = factory.newDocumentBuilder();
+        builder.setEntityResolver(new AllowListResolver());
+        final Document doc = builder.parse(input);
+        assertEquals(RESOLVED_MARKER, doc.getDocumentElement().getTextContent().trim(),
+                "DOM parse=text with allow-list should resolve to the resolver's content");
     }
 
     //endregion
@@ -257,47 +259,44 @@ void hardenedDomWithAllowListResolvesParseXml() throws Exception {
     }
 
     @Test
-    @Tag("dom")
-    void hardenedDomWithAllowListResolvesParseText() throws Exception {
-        final InputSource input = inputSource(xiIncludeXml(REFERENCED_TEXT, "text"));
+    @Tag("sax")
+    void hardenedSaxBlocksParseText() throws Exception {
+        final String input = xiIncludeXml(REFERENCED_TEXT, "text");
 
-        final DocumentBuilderFactory factory = XmlFactories.newDocumentBuilderFactory();
+        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
-        final DocumentBuilder builder = factory.newDocumentBuilder();
-        builder.setEntityResolver(new AllowListResolver());
-        final Document doc = builder.parse(input);
-        assertEquals(RESOLVED_MARKER, doc.getDocumentElement().getTextContent().trim(),
-                "DOM parse=text with allow-list should resolve to the resolver's content");
+        final String captured = captureCharacters(factory.newSAXParser().getXMLReader(), input);
+        assertFalse(captured.contains(LEAKED_MARKER),
+                "Hardened SAX parse=text must resolve the include to empty, not leak; got: " + captured);
     }
 
     @Test
-    @Tag("dom")
-    void hardenedDomNullResolverDoesNotLeak() throws Exception {
+    @Tag("sax")
+    void hardenedSaxBlocksParseXml() throws Exception {
         final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, "xml"));
 
-        final DocumentBuilderFactory factory = XmlFactories.newDocumentBuilderFactory();
+        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
-        final DocumentBuilder builder = factory.newDocumentBuilder();
-        builder.setEntityResolver(NO_OP_RESOLVER);
-        assertThrows(SAXException.class, () -> builder.parse(input),
-                "a resolver that returns null must not leak: the ignore-all floor blocks the real href");
+        assertThrows(SAXException.class, () -> {
+            final XMLReader reader = factory.newSAXParser().getXMLReader();
+            reader.parse(input);
+        }, "Hardened SAX parse=xml should throw");
     }
 
     @Test
     @Tag("sax")
-    void hardenedSaxWithAllowListResolvesParseXml() throws Exception {
-        final String input = xiIncludeXml(REFERENCED_XML, "xml");
+    void hardenedSaxNullResolverDoesNotLeak() throws Exception {
+        final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, "xml"));
 
         final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final XMLReader reader = factory.newSAXParser().getXMLReader();
-        reader.setEntityResolver(new AllowListResolver());
-        final String captured = captureCharacters(reader, input);
-        assertEquals(RESOLVED_MARKER, captured.trim(),
-                "SAX parse=xml with allow-list should resolve to the resolver's content");
+        reader.setEntityResolver(NO_OP_RESOLVER);
+        assertThrows(SAXException.class, () -> reader.parse(input),
+                "a resolver that returns null must not leak: the ignore-all floor blocks the real href");
     }
 
     @Test
@@ -317,16 +316,17 @@ void hardenedSaxWithAllowListResolvesParseText() throws Exception {
 
     @Test
     @Tag("sax")
-    void hardenedSaxNullResolverDoesNotLeak() throws Exception {
-        final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, "xml"));
+    void hardenedSaxWithAllowListResolvesParseXml() throws Exception {
+        final String input = xiIncludeXml(REFERENCED_XML, "xml");
 
         final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final XMLReader reader = factory.newSAXParser().getXMLReader();
-        reader.setEntityResolver(NO_OP_RESOLVER);
-        assertThrows(SAXException.class, () -> reader.parse(input),
-                "a resolver that returns null must not leak: the ignore-all floor blocks the real href");
+        reader.setEntityResolver(new AllowListResolver());
+        final String captured = captureCharacters(reader, input);
+        assertEquals(RESOLVED_MARKER, captured.trim(),
+                "SAX parse=xml with allow-list should resolve to the resolver's content");
     }
 
     //endregion
@@ -335,16 +335,17 @@ void hardenedSaxNullResolverDoesNotLeak() throws Exception {
 
     @Test
     @Tag("sax")
-    void hardenReaderBlocksParseXml() throws Exception {
-        final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, "xml"));
+    void hardenReaderAllowListResolvesParseXml() throws Exception {
+        final String input = xiIncludeXml(REFERENCED_XML, "xml");
 
-        // Reader from an unhardened factory that already has XInclude enabled
         final SAXParserFactory unhardenedFactory = SAXParserFactory.newInstance();
         unhardenedFactory.setNamespaceAware(true);
         assumeXIncludeAware(unhardenedFactory);
         final XMLReader reader = SAXParserHardener.hardenReader(unhardenedFactory.newSAXParser().getXMLReader());
-        assertThrows(SAXException.class, () -> reader.parse(input),
-                "hardenReader should block XInclude parse=xml on reader with XInclude already enabled");
+        reader.setEntityResolver(new AllowListResolver());
+        final String captured = captureCharacters(reader, input);
+        assertEquals(RESOLVED_MARKER, captured.trim(),
+                "hardenReader + allow-list should resolve to the resolver's content on a reader with XInclude already enabled");
     }
 
     @Test
@@ -363,17 +364,16 @@ void hardenReaderBlocksParseText() throws Exception {
 
     @Test
     @Tag("sax")
-    void hardenReaderAllowListResolvesParseXml() throws Exception {
-        final String input = xiIncludeXml(REFERENCED_XML, "xml");
+    void hardenReaderBlocksParseXml() throws Exception {
+        final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, "xml"));
 
+        // Reader from an unhardened factory that already has XInclude enabled
         final SAXParserFactory unhardenedFactory = SAXParserFactory.newInstance();
         unhardenedFactory.setNamespaceAware(true);
         assumeXIncludeAware(unhardenedFactory);
         final XMLReader reader = SAXParserHardener.hardenReader(unhardenedFactory.newSAXParser().getXMLReader());
-        reader.setEntityResolver(new AllowListResolver());
-        final String captured = captureCharacters(reader, input);
-        assertEquals(RESOLVED_MARKER, captured.trim(),
-                "hardenReader + allow-list should resolve to the resolver's content on a reader with XInclude already enabled");
+        assertThrows(SAXException.class, () -> reader.parse(input),
+                "hardenReader should block XInclude parse=xml on reader with XInclude already enabled");
     }
 
     //endregion
diff --git a/src/test/java/org/apache/commons/xml/XMLFilterTest.java b/src/test/java/org/apache/commons/xml/XMLFilterTest.java
index 5b91a2d..0a33878 100644
--- a/src/test/java/org/apache/commons/xml/XMLFilterTest.java
+++ b/src/test/java/org/apache/commons/xml/XMLFilterTest.java
@@ -63,12 +63,30 @@ private static String filterAndCapture(final XMLFilter filter, final String inpu
         return text.toString();
     }
 
+    /**
+     * On Android, hand the unconfigured filter a permissive parent so Xalan's {@code TrAXFilter} uses it instead of self-provisioning an Expat reader on which
+     * it enables {@code namespace-prefixes}, a feature Android's libexpat accepts but fails on mid-parse. The parent stays permissive (no floor), so the leak
+     * these controls assert still occurs.
+     */
+    private static void setPermissiveParentOnAndroid(final XMLFilter filter) {
+        if (AttackTestSupport.IS_ANDROID) {
+            filter.setParent(AttackTestSupport.permissiveReader());
+        }
+    }
+
     @Test
     void hardenedFilterDoesNotLeakDocument() throws Exception {
         final XMLFilter filter = SaxSurfaceTestSupport.hardenedFactory().newXMLFilter(AttackTestSupport.resourceSource("with-document.xsl"));
         assertFalse(filterAndCapture(filter, "<root/>").contains(AttackTestSupport.LEAKED_MARKER), "document() through XMLFilter leaked");
     }
 
+    @Test
+    void hardenedFilterDoesNotLeakExternalEntity() throws Exception {
+        // The f003 vector: with no caller-set parent, the input must be parsed by a hardened reader, not a self-provisioned permissive one.
+        final XMLFilter filter = SaxSurfaceTestSupport.hardenedFactory().newXMLFilter(AttackTestSupport.streamSource(IDENTITY_XSLT));
+        assertFalse(filterAndCapture(filter, entityPayload()).contains(AttackTestSupport.LEAKED_MARKER), "external entity through XMLFilter leaked");
+    }
+
     @Test
     void hardenedFilterFromTemplatesDoesNotLeakDocument() throws Exception {
         final SAXTransformerFactory factory = SaxSurfaceTestSupport.hardenedFactory();
@@ -79,10 +97,12 @@ void hardenedFilterFromTemplatesDoesNotLeakDocument() throws Exception {
     }
 
     @Test
-    void hardenedFilterDoesNotLeakExternalEntity() throws Exception {
-        // The f003 vector: with no caller-set parent, the input must be parsed by a hardened reader, not a self-provisioned permissive one.
-        final XMLFilter filter = SaxSurfaceTestSupport.hardenedFactory().newXMLFilter(AttackTestSupport.streamSource(IDENTITY_XSLT));
-        assertFalse(filterAndCapture(filter, entityPayload()).contains(AttackTestSupport.LEAKED_MARKER), "external entity through XMLFilter leaked");
+    void unconfiguredFilterLeaksDocument() throws Exception {
+        final SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();
+        final XMLFilter filter = factory.newXMLFilter(AttackTestSupport.resourceSource("with-document.xsl"));
+        setPermissiveParentOnAndroid(filter);
+        assertTrue(filterAndCapture(filter, "<root/>").contains(AttackTestSupport.LEAKED_MARKER),
+                "unconfigured XMLFilter should resolve document()");
     }
 
     @Test
@@ -96,24 +116,4 @@ void unconfiguredFilterLeaksExternalEntity() throws Exception {
         assertTrue(filterAndCapture(filter, entityPayload()).contains(AttackTestSupport.LEAKED_MARKER),
                 "unconfigured XMLFilter should resolve the input's external entity");
     }
-
-    @Test
-    void unconfiguredFilterLeaksDocument() throws Exception {
-        final SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();
-        final XMLFilter filter = factory.newXMLFilter(AttackTestSupport.resourceSource("with-document.xsl"));
-        setPermissiveParentOnAndroid(filter);
-        assertTrue(filterAndCapture(filter, "<root/>").contains(AttackTestSupport.LEAKED_MARKER),
-                "unconfigured XMLFilter should resolve document()");
-    }
-
-    /**
-     * On Android, hand the unconfigured filter a permissive parent so Xalan's {@code TrAXFilter} uses it instead of self-provisioning an Expat reader on which
-     * it enables {@code namespace-prefixes}, a feature Android's libexpat accepts but fails on mid-parse. The parent stays permissive (no floor), so the leak
-     * these controls assert still occurs.
-     */
-    private static void setPermissiveParentOnAndroid(final XMLFilter filter) {
-        if (AttackTestSupport.IS_ANDROID) {
-            filter.setParent(AttackTestSupport.permissiveReader());
-        }
-    }
 }
diff --git a/src/test/java/org/apache/commons/xml/XPathInputSourceTest.java b/src/test/java/org/apache/commons/xml/XPathInputSourceTest.java
index 2c18720..a16bc6a 100644
--- a/src/test/java/org/apache/commons/xml/XPathInputSourceTest.java
+++ b/src/test/java/org/apache/commons/xml/XPathInputSourceTest.java
@@ -57,13 +57,6 @@ void hardenedXPathEvaluateDoesNotLeak() throws Exception {
         assertFalse(result.contains(AttackTestSupport.LEAKED_MARKER), "external entity leaked into the XPath result: " + result);
     }
 
-    @Test
-    void hardenedXPathExpressionEvaluateDoesNotLeak() throws Exception {
-        // Same declared-entity outcome as above on the compiled-expression entry point.
-        final String result = XmlFactories.newXPathFactory().newXPath().compile(EXPRESSION).evaluate(AttackTestSupport.inputSource(entityPayload()));
-        assertFalse(result.contains(AttackTestSupport.LEAKED_MARKER), "external entity leaked into the compiled XPath result: " + result);
-    }
-
     @Test
     void hardenedXPathEvaluatesPlainDocument() throws Exception {
         // Positive control: the hardened pre-parse still evaluates an entity-free document end to end.
@@ -72,6 +65,13 @@ void hardenedXPathEvaluatesPlainDocument() throws Exception {
         assertEquals("plain text", result, "hardened XPath should evaluate a plain document");
     }
 
+    @Test
+    void hardenedXPathExpressionEvaluateDoesNotLeak() throws Exception {
+        // Same declared-entity outcome as above on the compiled-expression entry point.
+        final String result = XmlFactories.newXPathFactory().newXPath().compile(EXPRESSION).evaluate(AttackTestSupport.inputSource(entityPayload()));
+        assertFalse(result.contains(AttackTestSupport.LEAKED_MARKER), "external entity leaked into the compiled XPath result: " + result);
+    }
+
     @Test
     void unconfiguredXPathEvaluateLeaks() throws Exception {
         // Leak control: the unconfigured engine's internal parser resolves the entity, which is exactly what the wrapper exists to prevent.
diff --git a/src/test/java/org/apache/commons/xml/XmlFactoriesTest.java b/src/test/java/org/apache/commons/xml/XmlFactoriesTest.java
index e7d133a..8a016b9 100644
--- a/src/test/java/org/apache/commons/xml/XmlFactoriesTest.java
+++ b/src/test/java/org/apache/commons/xml/XmlFactoriesTest.java
@@ -49,12 +49,10 @@ class XmlFactoriesTest {
             "<?xml version=\"1.0\"?>\n<root><child>hello</child></root>\n";
 
     @Test
-    void newDocumentBuilderFactoryReturnsFreshInstance() {
-        final DocumentBuilderFactory a = XmlFactories.newDocumentBuilderFactory();
-        final DocumentBuilderFactory b = XmlFactories.newDocumentBuilderFactory();
-        assertNotNull(a);
-        assertNotNull(b);
-        assertNotSame(a, b);
+    void benignDocumentParses() throws Exception {
+        final Document doc = XmlFactories.newDocumentBuilderFactory().newDocumentBuilder().parse(new InputSource(new StringReader(BENIGN_XML)));
+        assertNotNull(doc);
+        assertNotNull(doc.getDocumentElement());
     }
 
     @Test
@@ -72,10 +70,12 @@ void newDocumentBuilderFactoryEnablesSecureProcessing() throws Exception {
     }
 
     @Test
-    void benignDocumentParses() throws Exception {
-        final Document doc = XmlFactories.newDocumentBuilderFactory().newDocumentBuilder().parse(new InputSource(new StringReader(BENIGN_XML)));
-        assertNotNull(doc);
-        assertNotNull(doc.getDocumentElement());
+    void newDocumentBuilderFactoryReturnsFreshInstance() {
+        final DocumentBuilderFactory a = XmlFactories.newDocumentBuilderFactory();
+        final DocumentBuilderFactory b = XmlFactories.newDocumentBuilderFactory();
+        assertNotNull(a);
+        assertNotNull(b);
+        assertNotSame(a, b);
     }
 
     @Test
@@ -88,12 +88,11 @@ void newSAXParserFactoryReturnsFreshInstance() {
     }
 
     @Test
-    void newXMLInputFactoryReturnsFreshInstance() {
-        final XMLInputFactory a = XmlFactories.newXMLInputFactory();
-        final XMLInputFactory b = XmlFactories.newXMLInputFactory();
+    void newSchemaFactoryReturnsFreshInstance() throws Exception {
+        final SchemaFactory a = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        final SchemaFactory b = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
         assertNotSame(a, b);
-        assertEquals(Boolean.TRUE, a.getProperty(XMLInputFactory.SUPPORT_DTD));
-        assertEquals(Boolean.FALSE, a.getProperty(XMLInputFactory.IS_VALIDATING));
+        assertTrue(a.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
     }
 
     @Test
@@ -104,17 +103,18 @@ void newTransformerFactoryReturnsFreshInstance() {
     }
 
     @Test
-    void newXPathFactoryReturnsFreshInstance() throws Exception {
-        final XPathFactory a = XmlFactories.newXPathFactory();
-        final XPathFactory b = XmlFactories.newXPathFactory();
+    void newXMLInputFactoryReturnsFreshInstance() {
+        final XMLInputFactory a = XmlFactories.newXMLInputFactory();
+        final XMLInputFactory b = XmlFactories.newXMLInputFactory();
         assertNotSame(a, b);
-        assertTrue(a.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
+        assertEquals(Boolean.TRUE, a.getProperty(XMLInputFactory.SUPPORT_DTD));
+        assertEquals(Boolean.FALSE, a.getProperty(XMLInputFactory.IS_VALIDATING));
     }
 
     @Test
-    void newSchemaFactoryReturnsFreshInstance() throws Exception {
-        final SchemaFactory a = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-        final SchemaFactory b = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+    void newXPathFactoryReturnsFreshInstance() throws Exception {
+        final XPathFactory a = XmlFactories.newXPathFactory();
+        final XPathFactory b = XmlFactories.newXPathFactory();
         assertNotSame(a, b);
         assertTrue(a.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
     }


(commons-xml) 03/08: Remove noisy editor comments

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-xml.git

commit 972d94de80cefb35b692a6689c9baef5b1e9f6a2
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Aug 26 07:33:51 2026 -0400

    Remove noisy editor comments
---
 src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java     | 3 +--
 .../java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java   | 3 +--
 src/main/java/org/apache/commons/xml/HardeningSAXParser.java           | 3 +--
 src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java    | 3 +--
 src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java       | 3 +--
 src/main/java/org/apache/commons/xml/HardeningTemplates.java           | 3 +--
 src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java    | 3 +--
 src/main/java/org/apache/commons/xml/HardeningTransformer.java         | 3 +--
 src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java  | 3 +--
 src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java  | 3 +--
 src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java    | 3 +--
 src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java     | 3 +--
 src/main/java/org/apache/commons/xml/HardeningXMLReader.java           | 3 +--
 13 files changed, 13 insertions(+), 26 deletions(-)

diff --git a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java
index b9bf44e..93cbdb8 100644
--- a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java
+++ b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilder.java
@@ -56,7 +56,7 @@ public DOMImplementation getDOMImplementation() {
     public Schema getSchema() {
         return delegate.getSchema();
     }
-    // </editor-fold>
+
 
     @Override
     public boolean isNamespaceAware() {
@@ -78,7 +78,6 @@ public Document newDocument() {
         return delegate.newDocument();
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public Document parse(final InputSource is) throws SAXException, IOException {
         return delegate.parse(is);
diff --git a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java
index 6a18dbd..59c066e 100644
--- a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java
@@ -39,7 +39,6 @@ final class HardeningDocumentBuilderFactory extends DocumentBuilderFactory {
         this.delegate = delegate;
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public Object getAttribute(final String name) {
         return delegate.getAttribute(name);
@@ -144,5 +143,5 @@ public void setValidating(final boolean validating) {
     public void setXIncludeAware(final boolean state) {
         delegate.setXIncludeAware(state);
     }
-    // </editor-fold>
+
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningSAXParser.java b/src/main/java/org/apache/commons/xml/HardeningSAXParser.java
index e37f3d6..9fb396a 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSAXParser.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSAXParser.java
@@ -60,7 +60,6 @@ public Parser getParser() throws SAXException {
         return hardenedParser;
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public Object getProperty(final String name) throws SAXNotRecognizedException, SAXNotSupportedException {
         return delegate.getProperty(name);
@@ -107,5 +106,5 @@ public void reset() {
     public void setProperty(final String name, final Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
         delegate.setProperty(name, value);
     }
-    // </editor-fold>
+
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java b/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java
index 19f79a9..f1fff11 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java
@@ -42,7 +42,6 @@ final class HardeningSAXParserFactory extends SAXParserFactory {
         this.delegate = delegate;
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public boolean getFeature(final String name) throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException {
         return delegate.getFeature(name);
@@ -97,5 +96,5 @@ public void setValidating(final boolean validating) {
     public void setXIncludeAware(final boolean state) {
         delegate.setXIncludeAware(state);
     }
-    // </editor-fold>
+
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java b/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java
index 31cc9c0..573b000 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java
@@ -75,7 +75,6 @@ private static Source[] harden(final Source[] schemas) throws SAXException {
         delegate.setResourceResolver(floor);
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public ErrorHandler getErrorHandler() {
         return delegate.getErrorHandler();
@@ -125,7 +124,7 @@ public void setFeature(final String name, final boolean value) throws SAXNotReco
     public void setProperty(final String name, final Object object) throws SAXNotRecognizedException, SAXNotSupportedException {
         delegate.setProperty(name, object);
     }
-    // </editor-fold>
+
 
     @Override
     public void setResourceResolver(final LSResourceResolver resourceResolver) {
diff --git a/src/main/java/org/apache/commons/xml/HardeningTemplates.java b/src/main/java/org/apache/commons/xml/HardeningTemplates.java
index af8ceeb..0208c11 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTemplates.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTemplates.java
@@ -60,12 +60,11 @@ Templates getDelegate() {
         return delegate;
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public Properties getOutputProperties() {
         return delegate.getOutputProperties();
     }
-    // </editor-fold>
+
 
     @Override
     public Transformer newTransformer() throws TransformerConfigurationException {
diff --git a/src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java b/src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java
index 9600ac1..04124a7 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTemplatesHandler.java
@@ -99,7 +99,6 @@ public void setDocumentLocator(final Locator locator) {
         delegate.setDocumentLocator(locator);
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public void setSystemId(final String systemID) {
         delegate.setSystemId(systemID);
@@ -109,7 +108,7 @@ public void setSystemId(final String systemID) {
     public void skippedEntity(final String name) throws SAXException {
         delegate.skippedEntity(name);
     }
-    // </editor-fold>
+
 
     @Override
     public void startDocument() throws SAXException {
diff --git a/src/main/java/org/apache/commons/xml/HardeningTransformer.java b/src/main/java/org/apache/commons/xml/HardeningTransformer.java
index beda636..c6b4288 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTransformer.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTransformer.java
@@ -53,7 +53,6 @@ final class HardeningTransformer extends Transformer {
         delegate.setURIResolver(floor);
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public void clearParameters() {
         delegate.clearParameters();
@@ -110,7 +109,7 @@ public void setOutputProperty(final String name, final String value) {
     public void setParameter(final String name, final Object value) {
         delegate.setParameter(name, value);
     }
-    // </editor-fold>
+
 
     @Override
     public void setURIResolver(final URIResolver resolver) {
diff --git a/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java b/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
index 5b8ef0f..4aa0d37 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
@@ -138,7 +138,6 @@ public Source getAssociatedStylesheet(final Source source, final String media, f
         return delegate.getAssociatedStylesheet(hardened, media, title, charset);
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public Object getAttribute(final String name) {
         return delegate.getAttribute(name);
@@ -230,7 +229,7 @@ public void setErrorListener(final ErrorListener listener) {
     public void setFeature(final String name, final boolean value) throws TransformerConfigurationException {
         delegate.setFeature(name, value);
     }
-    // </editor-fold>
+
 
     @Override
     public void setURIResolver(final URIResolver resolver) {
diff --git a/src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java b/src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java
index 6c44417..1b0f5b1 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTransformerHandler.java
@@ -59,7 +59,7 @@ public void characters(final char[] ch, final int start, final int length) throw
     public void comment(final char[] ch, final int start, final int length) throws SAXException {
         delegate.comment(ch, start, length);
     }
-    // </editor-fold>
+
 
     @Override
     public void endCDATA() throws SAXException {
@@ -121,7 +121,6 @@ public void setDocumentLocator(final Locator locator) {
         delegate.setDocumentLocator(locator);
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public void setResult(final Result result) {
         delegate.setResult(result);
diff --git a/src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java b/src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java
index 8cb9c44..2cc80b7 100644
--- a/src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java
+++ b/src/main/java/org/apache/commons/xml/HardeningValidatorHandler.java
@@ -106,7 +106,6 @@ public void processingInstruction(final String target, final String data) throws
         delegate.processingInstruction(target, data);
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public void setContentHandler(final ContentHandler receiver) {
         delegate.setContentHandler(receiver);
@@ -141,7 +140,7 @@ public void setResourceResolver(final LSResourceResolver resourceResolver) {
     public void skippedEntity(final String name) throws SAXException {
         delegate.skippedEntity(name);
     }
-    // </editor-fold>
+
 
     @Override
     public void startDocument() throws SAXException {
diff --git a/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java b/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
index 05ffbd7..ecdc2e4 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
@@ -124,7 +124,6 @@ public XMLStreamReader createXMLStreamReader(final InputStream stream, final Str
         return delegate.createXMLStreamReader(stream, encoding);
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public XMLStreamReader createXMLStreamReader(final Reader reader) throws XMLStreamException {
         return delegate.createXMLStreamReader(reader);
@@ -149,7 +148,7 @@ public XMLStreamReader createXMLStreamReader(final String systemId, final Reader
     public XMLEventAllocator getEventAllocator() {
         return delegate.getEventAllocator();
     }
-    // </editor-fold>
+
 
     @Override
     public Object getProperty(final String name) {
diff --git a/src/main/java/org/apache/commons/xml/HardeningXMLReader.java b/src/main/java/org/apache/commons/xml/HardeningXMLReader.java
index 7899ed5..61d2b35 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXMLReader.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXMLReader.java
@@ -51,7 +51,6 @@ class HardeningXMLReader implements XMLReader {
         delegate.setEntityResolver(floor);
     }
 
-    // <editor-fold defaultstate="collapsed" desc="Trivial delegation">
     @Override
     public ContentHandler getContentHandler() {
         return delegate.getContentHandler();
@@ -121,5 +120,5 @@ public void setFeature(final String name, final boolean value) throws SAXNotReco
     public void setProperty(final String name, final Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
         delegate.setProperty(name, value);
     }
-    // </editor-fold>
+
 }


(commons-xml) 08/08: Merge branch 'main' of https://github.com/apache/commons-xml into main

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-xml.git

commit 14ec66209a30c08e9728da8ba59ff65c4de6833f
Merge: 641d267 51879f6
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Aug 26 08:21:26 2026 -0400

    Merge branch 'main' of https://github.com/apache/commons-xml into main

 src/changes/changes.xml | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)


(commons-xml) 06/08: Javadoc

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-xml.git

commit 80c5a0a41725a8aa0f7f59227e6e3a616c22f107
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Aug 26 07:59:54 2026 -0400

    Javadoc
---
 .../xml/FallbackIgnoreLSResourceResolver.java        |  5 +++++
 .../commons/xml/FallbackIgnoreURIResolver.java       |  5 +++++
 .../commons/xml/FallbackIgnoreXMLResolver.java       | 20 +++++++++++++-------
 3 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/src/main/java/org/apache/commons/xml/FallbackIgnoreLSResourceResolver.java b/src/main/java/org/apache/commons/xml/FallbackIgnoreLSResourceResolver.java
index 3c0d764..3d99d64 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreLSResourceResolver.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreLSResourceResolver.java
@@ -88,6 +88,11 @@ public LSInput resolveResource(final String type, final String namespaceURI, fin
         return empty;
     }
 
+    /**
+     * Sets the delegate to consult first, replacing any previous delegate. A {@code null} value removes the delegate and leaves a pure ignore-all floor.
+     *
+     * @param delegate The delegate to consult first, or {@code null} for a pure ignore-all floor.
+     */
     void setDelegate(final LSResourceResolver delegate) {
         this.delegate = delegate;
     }
diff --git a/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java b/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java
index edcd78f..21a4305 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreURIResolver.java
@@ -112,6 +112,11 @@ public Source resolve(final String href, final String base) throws TransformerEx
         return emptySource.get();
     }
 
+    /**
+     * Sets the delegate to consult first, replacing any previous delegate. A {@code null} value removes the delegate and leaves a pure ignore-all floor.
+     *
+     * @param delegate The delegate to consult first, or {@code null} for a pure ignore-all floor.
+     */
     void setDelegate(final URIResolver delegate) {
         this.delegate = delegate;
     }
diff --git a/src/main/java/org/apache/commons/xml/FallbackIgnoreXMLResolver.java b/src/main/java/org/apache/commons/xml/FallbackIgnoreXMLResolver.java
index b055a2a..12dd350 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreXMLResolver.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreXMLResolver.java
@@ -25,17 +25,18 @@
 
 /**
  * {@link XMLResolver} floor: consults an optional caller-supplied resolver and ignores (resolves to empty) whatever the caller does not resolve.
- *
- * <p>The StAX counterpart of {@link FallbackIgnoreEntityResolver2}, installed on each entity-resolution hook. The hardened {@link javax.xml.stream.XMLInputFactory}
- * wrapper routes a caller-set resolver through {@link #setDelegate} rather than letting it replace the floor. A caller opts a specific entity in by returning
- * a non-{@code null} result; anything left unresolved resolves to an empty input, so the external resource is neither fetched nor leaked and the parse
- * continues with no replacement content.</p>
+ * <p>
+ * The StAX counterpart of {@link FallbackIgnoreEntityResolver2}, installed on each entity-resolution hook. The hardened
+ * {@link javax.xml.stream.XMLInputFactory} wrapper routes a caller-set resolver through {@link #setDelegate} rather than letting it replace the floor. A caller
+ * opts a specific entity in by returning a non-{@code null} result; anything left unresolved resolves to an empty input, so the external resource is neither
+ * fetched nor leaked and the parse continues with no replacement content.
+ * </p>
  */
 final class FallbackIgnoreXMLResolver implements XMLResolver {
 
     /**
-     * Empty {@link ByteArrayInputStream} shared across every call. {@code read()} on a zero-length array always returns {@code -1}, so reusing the instance
-     * is safe even though the type is technically stateful.
+     * Empty {@link ByteArrayInputStream} shared across every call. {@code read()} on a zero-length array always returns {@code -1}, so reusing the instance is
+     * safe even though the type is technically stateful.
      */
     private static final InputStream EMPTY = new ByteArrayInputStream(new byte[0]);
 
@@ -71,6 +72,11 @@ public Object resolveEntity(final String publicID, final String systemID, final
         return EMPTY;
     }
 
+    /**
+     * Sets the delegate to consult first, replacing any previous delegate, may be {@code null}.
+     *
+     * @param delegate The delegate to consult first, replacing any previous delegate.
+     */
     void setDelegate(final XMLResolver delegate) {
         this.delegate = delegate;
     }