The enclosed patch -- would you prefer a github pull request? -- makes
xslt:transform() aware of XML catalog files, as other XML parsing
already is. The same CATFILE preference is used (via the query
context).

I refactored CatalogWrapper slightly so it could be reused. I also took
away the line that sets verbosity to 0, as without it you can control
verbosity via a system property, or using the CatalogManager.properties
file.

I tested this with xml-commons-resolver-1.2/resolver.jar and with the
built-in resolver and both seem to work.

I have not added tests. In addition, it'd be worth adding something to
the documentation, especially about the xml.catalog.verbosity property
(just verbosity in the .properties file).

Possible breaking change: i also removed the line that sets
prefer=public. I spent ages trying to get catalogs working before i
dicovered this, as i was using a system identifier!  The code could
check to see if the corresponding system property is set (users can't
override the API with system properties, frustratingly), but since
catalogs already say prefer=public or prefer=system in them, and it'd
have needed to have been the same to work, i don't think this change
breaks anythign in practice. It may make some catalogs start to work
that had not been working, so maybe it's worth a line in the release
notes.

Liam


-- 
Liam Quin, https://www.delightfulcomputing.com/
Available for XML/Document/Information Architecture/XSLT/
XSL/XQuery/Web/Text Processing/A11Y training, work & consulting.
Web slave for vintage clipart http://www.fromoldbooks.org/
diff --git a/basex-core/src/main/java/org/basex/build/xml/CatalogWrapper.java b/basex-core/src/main/java/org/basex/build/xml/CatalogWrapper.java
index c4c9f41fa..1303fca92 100644
--- a/basex-core/src/main/java/org/basex/build/xml/CatalogWrapper.java
+++ b/basex-core/src/main/java/org/basex/build/xml/CatalogWrapper.java
@@ -38,6 +38,41 @@ public final class CatalogWrapper {
     return CM != null;
   }
 
+  /**
+   * Returns the resolver, which could be of any class that implements the
+   * CatalogManager interface.
+   */
+  public static Object getCM() {
+    return get(CRP, CM);
+  }
+
+  public static void setDefaults(final String path) {
+    if(CM == null) return;
+
+    // IgnoreMissingProperties - default is to print a warning if properties
+    // are unset; this is not usually what we want, but can be overridden
+    // for debugging. Not all resolvers produce errors even if this is false,
+    // so better to set it to true.
+    invoke(method(CMP, "setIgnoreMissingProperties", boolean.class), CM, true);
+
+    // CatalogFiles - semicolon-separated list of files
+    invoke(method(CMP, "setCatalogFiles", String.class), CM, path);
+
+    // StaticCatalog:
+    // If this manager uses static catalogs, the same static catalog will
+    // always be returned.   Otherwise a new catalog will be returned.
+    // We would probably get better performance by using true here. You get a
+    // new catalogmanager instance if you change PATH.
+    invoke(method(CMP, "setUseStaticCatalog", boolean.class), CM, false);
+
+    // You can also set Verbosity, but that's best left for the properties file,
+    // CatalogManager.propertie,  or system property, to help debugging.
+    // The higher the number, the more messages.
+    // NOTE messages go to output, not err stream!
+    // invoke(method(CMP, "setVerbosity", int.class), CM, 0);
+
+  }
+
   /**
    * Decorates the {@link XMLReader} with the catalog resolver if it is found in the classpath.
    * Does nothing otherwise.
@@ -46,11 +81,9 @@ public final class CatalogWrapper {
    */
   static void set(final XMLReader reader, final String path) {
     if(CM == null) return;
-    invoke(method(CMP, "setIgnoreMissingProperties", boolean.class), CM, true);
-    invoke(method(CMP, "setCatalogFiles", String.class), CM, path);
-    invoke(method(CMP, "setPreferPublic", boolean.class), CM, true);
-    invoke(method(CMP, "setUseStaticCatalog", boolean.class), CM, false);
-    invoke(method(CMP, "setVerbosity", int.class), CM, 0);
+
+    setDefaults(path);
+
     reader.setEntityResolver((EntityResolver) get(CRP, CM));
   }
 }
diff --git a/basex-core/src/main/java/org/basex/query/func/xslt/XsltTransform.java b/basex-core/src/main/java/org/basex/query/func/xslt/XsltTransform.java
index a0750b74c..3c34bced7 100644
--- a/basex-core/src/main/java/org/basex/query/func/xslt/XsltTransform.java
+++ b/basex-core/src/main/java/org/basex/query/func/xslt/XsltTransform.java
@@ -9,6 +9,7 @@ import java.util.*;
 import javax.xml.transform.*;
 import javax.xml.transform.stream.*;
 
+import org.basex.core.*;
 import org.basex.io.*;
 import org.basex.io.out.*;
 import org.basex.io.serial.*;
@@ -17,6 +18,7 @@ import org.basex.query.value.item.*;
 import org.basex.query.value.node.*;
 import org.basex.util.*;
 import org.basex.util.options.*;
+import org.basex.build.xml.*; // for CatalogWrapper
 
 /**
  * Function implementation.
@@ -56,7 +58,7 @@ public class XsltTransform extends XsltFn {
     final ArrayOutput ao = new ArrayOutput();
     try {
       System.setErr(new PrintStream(ao));
-      return transform(in, xsl, opts.free(), xopts);
+      return transform(in, xsl, opts.free(), xopts, qc);
     } catch(final TransformerException ex) {
       Util.debug(ex);
       throw XSLT_ERROR_X.get(info, trim(utf8(ao.finish(), Prop.ENCODING)));
@@ -87,23 +89,33 @@ public class XsltTransform extends XsltFn {
     throw STRNOD_X_X.get(info, item.type, item);
   }
 
+
   /**
    * Uses Java's XSLT implementation to perform an XSL transformation.
    * @param in input
    * @param xsl style sheet
    * @param par parameters
    * @param xopts XSLT options
+   * @param qc query context
    * @return transformed result
    * @throws TransformerException transformer exception
    */
   private static byte[] transform(final IO in, final IO xsl, final HashMap<String, String> par,
-      final XsltOptions xopts) throws TransformerException {
+      final XsltOptions xopts, final QueryContext qc) throws TransformerException {
 
     // retrieve new or cached transformer
     final Transformer tr = transformer(xsl.streamSource(), xopts.get(XsltOptions.CACHE));
     // bind parameters
     par.forEach(tr::setParameter);
 
+    // set URI resolver
+    final String path = qc.context.options.get(MainOptions.CATFILE);
+    if (!(path == null) && !path.isEmpty()) {
+      CatalogWrapper.setDefaults(path);
+
+      tr.setURIResolver((URIResolver) CatalogWrapper.getCM());
+    }
+
     // do transformation and return result
     final ArrayOutput ao = new ArrayOutput();
     tr.transform(in.streamSource(), new StreamResult(ao));

Reply via email to