0

When I run my xsl stylesheet, I need to know if some files are present. Because xslt has no native way to test if a file exists (ASFAIK), I want to do this with javascript. Usually I'm working with the saxon 9 HE, but some colleagues told me that javascript functions were not supported with the HE edition. Is there a way to execute java script functions with saxon 9 HE?

I've tried this example: How to include javaScript file in xslt , but oxygen gave me this error: "Cannot find a matching 1-argument function named".

Ole
  • 161
  • 1
  • 3
  • 12

2 Answers2

3

You can use the XPath functions doc-available https://www.w3.org/TR/xpath-functions/#func-doc-available and unparsed-text-available https://www.w3.org/TR/xpath-functions/#func-unparsed-text-available to check whether an XML document or an non-XML text document is present.

There is no support for using Javascript with Saxon 9 (and a simple ECMAScript/Javascript engine usually does not include any file IO functionality anyway) but there is a feature called integrated extension functions http://saxonica.com/html/documentation/extensibility/integratedfunctions/ you can use in Saxon 9 (all editions) to call Java code. In Saxon 9 PE and EE you can additionally use reflexive extension functions to call Java code directly from the XSLT code.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Thanks for this comprehensive and exact answer. Your solution works well for text-based files, but I have to test if some *.mp4 files exists, so unparsed-text-available function won't work for me. I already got working soultions for saxon PE and it's java support with e.g.: TRUE – Ole Apr 03 '18 at 19:22
  • As I mentioned before, in production I have only a saxon HE processor. Next, I will try your idea with the integrated extension function calls! I also found an interesting, but yet not finished, saxon module solving my problem: http://expath.org/spec/file – Ole Apr 03 '18 at 19:27
  • Saxon PE and EE support that EXPath module http://saxonica.com/html/documentation/functions/expath-file/exists.html but that doesn't help with the HE version. – Martin Honnen Apr 03 '18 at 19:33
3

Thanks again Martin, your really helped me out! I wrote my own custom xslt function with the integrated extension functions feature. The function calls a java method, which tests if a file is present in a given directory and returns either true or false. For those who need an working example of the "integrated extension functions" feature, or even want to test if a file exists with the saxon-9-HE, I will share my simple solution.

Java class which defines the xslt function name, arguments and return type and harbours the java method to call, when the xslt function is invoked:

package de.mypackage.xsltfunctions;

import java.io.File;

import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.BooleanValue;
import net.sf.saxon.value.SequenceType;
import net.sf.saxon.value.StringValue;

public class FileExists extends ExtensionFunctionDefinition {
  @Override
  public StructuredQName getFunctionQName() {
    return new StructuredQName("file", "http://mydomain.de/xslt/filesystem", "file-exists");
  }

  @Override
  public SequenceType[] getArgumentTypes() {
    return new SequenceType[] { SequenceType.SINGLE_STRING, SequenceType.SINGLE_STRING };
  }

  @Override
  public SequenceType getResultType(final SequenceType[] suppliedArgumentTypes) {
    return SequenceType.SINGLE_BOOLEAN;
  }

  @Override
  public ExtensionFunctionCall makeCallExpression() {
    return new ExtensionFunctionCall() {

      @Override
      public Sequence call(final XPathContext context, final Sequence[] arguments)
          throws XPathException {

        String searchDir = ((StringValue) arguments[0]).getStringValue();
        String fileName = ((StringValue) arguments[1]).getStringValue();

        if (!new File(searchDir).isDirectory()) {
          throw new XPathException(
              "First argument \"" + searchDir + "\" is not a directory or cannot be found!");
        }

        return BooleanValue.get(new File(searchDir + fileName).exists());
      }
    };
  }

}

Code snipped which registers the custom xslt-function for the saxon processor:

import java.io.StringWriter;
import de.mypackage.xsltfunctions.FileExists;
import net.sf.saxon.TransformerFactoryImpl;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

StringWriter xmlResultResource = new StringWriter();      
System.setProperty("javax.xml.transform.TransformerFactory","net.sf.saxon.TransformerFactoryImpl");

TransformerFactory factory = TransformerFactory.newInstance();
TransformerFactoryImpl tFactoryImpl = (TransformerFactoryImpl) factory;
net.sf.saxon.Configuration saxonConfig = tFactoryImpl.getConfiguration();
saxonConfig.registerExtensionFunction(new FileExists());
Transformer transformer = factory.newTransformer(new StreamSource(getXslFile()));

transformer.transform(new StreamSource(xmlFileInput), new StreamResult(xmlResultResource));

String result = xmlResultResource.getBuffer().toString();
O'Neil
  • 3,790
  • 4
  • 16
  • 30
Ole
  • 161
  • 1
  • 3
  • 12
  • Here is also the xslt function call: `` and also define the namespace: `` – Ole Apr 05 '18 at 11:50
  • One minor suggestion: because the function depends on external state, it's safer to implement the hasSideEffects() method to return true. Otherwise, for example, the function might be called at compile time, which could give the wrong answer if the file is created/deleted between stylesheet compile time and stylesheet execution time. – Michael Kay Apr 11 '18 at 22:08