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));