Project

Profile

Help

how to call xslt functions from a XPath query? » XsltHelperV94.java

Xavier Coble, 2015-06-29 20:56

 
package cw.validation.streaming.impl.saxon;

import static org.slf4j.LoggerFactory.getLogger;

import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import java.util.List;

import javax.xml.transform.stream.StreamSource;

import net.sf.saxon.Controller;
import net.sf.saxon.expr.instruct.UserFunction;
import net.sf.saxon.expr.instruct.UserFunctionParameter;
import net.sf.saxon.functions.ExecutableFunctionLibrary;
import net.sf.saxon.functions.FunctionLibrary;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.om.ValueRepresentation;
import net.sf.saxon.pattern.AnyNodeTest;
import net.sf.saxon.pattern.NodeKindTest;
import net.sf.saxon.s9api.ExtensionFunction;
import net.sf.saxon.s9api.ItemType;
import net.sf.saxon.s9api.ItemTypeFactory;
import net.sf.saxon.s9api.OccurrenceIndicator;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.SequenceType;
import net.sf.saxon.s9api.XdmAtomicValue;
import net.sf.saxon.s9api.XdmEmptySequence;
import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.s9api.XdmValue;
import net.sf.saxon.s9api.XsltCompiler;
import net.sf.saxon.s9api.XsltExecutable;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.iter.ArrayIterator;
import net.sf.saxon.type.AnyItemType;
import net.sf.saxon.type.BuiltInAtomicType;
import net.sf.saxon.value.Cardinality;
import net.sf.saxon.value.EmptySequence;
import net.sf.saxon.value.SequenceExtent;

import org.slf4j.Logger;

import com.google.common.collect.Lists;
import com.google.common.io.ByteSource;
import com.google.common.io.Closer;
import com.google.common.io.Resources;

/**
* Class to help with the analysis of an XSLT instance
*/
class XsltHelperV94 {
private static final Logger LOG = getLogger(XsltHelper.class);
private final Processor proc;
private XsltExecutable stylesheet;
private ItemTypeFactory itemFac;

public XsltHelperV94(Processor proc) {
this.proc = proc;
}

public void compile(URL xsltUrl) throws SaxonApiException, IOException {
ByteSource source = Resources.asByteSource(xsltUrl);
Closer closer = Closer.create();
XsltCompiler comp = proc.newXsltCompiler();
try {
stylesheet = comp.compile(new StreamSource(closer.register(source.openBufferedStream())));
} finally {
closer.close();
}
}

/**
* @return a list of ExtensionFunctions contained within the stylesheet
*/
public List<ExtensionFunction> getExtensionFunctions() {
List<ExtensionFunction> ret = Lists.newArrayList();
if (itemFac == null) {
itemFac = new ItemTypeFactory(proc);
}
for (FunctionLibrary fl : stylesheet.getUnderlyingCompiledStylesheet().getFunctionLibrary().getLibraryList()) {
// holds custom xslt functions
if (fl instanceof ExecutableFunctionLibrary) {
Iterator<UserFunction> functions = ((ExecutableFunctionLibrary) fl).iterateFunctions();
while (functions.hasNext()) {
final UserFunction function = functions.next();
final SequenceType resultType =
valueToSaxon9Api(function.getResultType(proc.getUnderlyingConfiguration().getTypeHierarchy()));
final QName functionName =
new QName(function.getFunctionName().getURI(), function.getFunctionName().getLocalPart());
LOG.info("processing: " + functionName);
final SequenceType[] argTypes = valuesToSaxon9Api(function.getParameterDefinitions());

ret.add(new ExtensionFunction() {
@Override
public QName getName() {
return functionName;
}

@Override
public SequenceType[] getArgumentTypes() {
return argTypes;
}

@Override
public SequenceType getResultType() {
return resultType;
}

@Override
public XdmValue call(XdmValue[] args) throws SaxonApiException {
try {
@SuppressWarnings("rawtypes")
ValueRepresentation ret =
function.call(valuesToSaxon9Api(args), new Controller(proc.getUnderlyingConfiguration()));
if (ret instanceof EmptySequence) {
return XdmEmptySequence.getInstance();
}
if (resultType.getItemType().getUnderlyingItemType().isAtomicType()) {
if (ret instanceof SequenceExtent) {
List<XdmItem> seqRet = Lists.newArrayList();
@SuppressWarnings("rawtypes")
ArrayIterator it = ((SequenceExtent) ret).iterate();
while (it.hasNext()) {
@SuppressWarnings("rawtypes")
Item cur = it.next();
seqRet.add(new XdmAtomicValue(cur.getStringValue(), resultType.getItemType()));
}
return new XdmValue(seqRet);
}
return new XdmAtomicValue(ret.getStringValue(), resultType.getItemType());
}
throw new SaxonApiException("Currently, only atomic return types are handled.");
} catch (XPathException e) {
throw new SaxonApiException(e);
}
}
});
}
}
}
return ret;
}

private SequenceType valueToSaxon9Api(net.sf.saxon.value.SequenceType sequenceType) {
String o = Cardinality.getOccurrenceIndicator(sequenceType.getCardinality());
OccurrenceIndicator dec;
if (o.equals("")) {
dec = OccurrenceIndicator.ONE;
} else if (o.equals("+")) {
dec = OccurrenceIndicator.ONE_OR_MORE;
} else if (o.equals("*")) {
dec = OccurrenceIndicator.ZERO_OR_MORE;
} else if (o.equals("?")) {
dec = OccurrenceIndicator.ZERO_OR_ONE;
} else if (o.equals("*")) {
dec = OccurrenceIndicator.ZERO_OR_MORE;
} else {
throw new RuntimeException("Cardinality [" + o + "] is unknown");
}
return SequenceType.makeSequenceType(valueToSaxon9Api(sequenceType.getPrimaryType()), dec);
}

private SequenceType[] valuesToSaxon9Api(UserFunctionParameter[] params) {
SequenceType[] ret = new net.sf.saxon.s9api.SequenceType[params.length];
for (int i = 0; i < params.length; i++) {
ret[i] = valueToSaxon9Api(params[i].getRequiredType());
}
return ret;
}

private ItemType valueToSaxon9Api(net.sf.saxon.type.ItemType type) {
if (type.isAtomicType()) {
if (type instanceof BuiltInAtomicType) {
StructuredQName fg = ((BuiltInAtomicType) type).getQualifiedName();
try {
return itemFac.getAtomicType(new QName(fg.getURI(), fg.getLocalPart()));
} catch (SaxonApiException e) {
throw new RuntimeException(e);
}
}
} else {
if (type instanceof AnyItemType) {
return ItemType.ANY_ITEM;
} else if (type instanceof AnyNodeTest) {
return ItemType.ANY_NODE;
} else if (type instanceof NodeKindTest) {
return ItemType.ANY_NODE;
}
}
throw new RuntimeException("unknown type [" + type + "]");
}

@SuppressWarnings("rawtypes")
private static ValueRepresentation[] valuesToSaxon9Api(XdmValue[] args) {
ValueRepresentation[] ret = new ValueRepresentation[args.length];
for (int i = 0; i < args.length; i++) {
ret[i] = args[i].getUnderlyingValue();
}
return ret;
}
}
(1-1/4)