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