
net.sf.saxon.functions.CollectionFn Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Saxon-HE Show documentation
Show all versions of Saxon-HE Show documentation
The XSLT and XQuery Processor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2015 Saxonica Limited.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.functions;
import net.sf.saxon.Controller;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.parser.RoleDiagnostic;
import net.sf.saxon.lib.CollectionFinder;
import net.sf.saxon.lib.FeatureKeys;
import net.sf.saxon.lib.Resource;
import net.sf.saxon.lib.ResourceCollection;
import net.sf.saxon.om.*;
import net.sf.saxon.pattern.AnyNodeTest;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.Type;
import net.sf.saxon.value.ObjectValue;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
/**
* Implement the fn:collection() function. This is responsible for calling the
* registered {@link CollectionFinder}. For the effect of the default
* system-supplied CollectionURIResolver, see {@link net.sf.saxon.resource.StandardCollectionFinder}
*/
public class CollectionFn extends SystemFunction implements Callable {
/**
* URI representing a collection that is always empty, regardless of any collection URI resolver
*/
public static String EMPTY_COLLECTION_URI = "http://saxon.sf.net/collection/empty";
/**
* An empty collection
*/
public final static ResourceCollection EMPTY_COLLECTION = new EmptyCollection(EMPTY_COLLECTION_URI);
/**
* Implementation of an empty collection
*/
private static class EmptyCollection implements ResourceCollection {
private String collectionUri;
public EmptyCollection(String cUri) {
collectionUri = cUri;
}
public String getCollectionURI() {
return collectionUri;
}
public Iterator getResourceURIs(XPathContext context) throws XPathException {
return new ArrayList().iterator();
}
public Iterator getResources(XPathContext context) throws XPathException {
return new ArrayList().iterator();
}
public boolean isStable(XPathContext context) {
return true;
}
}
public int getSpecialProperties(Expression[] arguments) {
// See redmine bug 1652. We cannot assume that the nodes will be in document order because we can't assume
// they will all be "new" documents. We can't even assume that they will be distinct.
return (super.getSpecialProperties(arguments) & ~StaticProperty.NON_CREATIVE) | StaticProperty.PEER_NODESET;
}
private String getAbsoluteCollectionURI(String href, XPathContext context) throws XPathException {
String absoluteURI;
if (href == null) {
absoluteURI = context.getConfiguration().getDefaultCollection();
} else {
try {
URI uri = new URI(href);
if (uri.isAbsolute()) {
absoluteURI = uri.toString();
} else {
String base = getRetainedStaticContext().getStaticBaseUriString();
if (base != null) {
absoluteURI = new URI(base).resolve(href).toString();
} else {
throw new XPathException("Relative collection URI cannot be resolved: no base URI available");
}
}
} catch (URISyntaxException e) {
throw new XPathException(e.getMessage(), "FODC0004");
}
}
return absoluteURI;
}
/**
* Get an iterator of the Resources in a ResourceCollection, returned in the form of external objects wrapping
* a Resource object
* @param collection the resource collection
* @param context the XPath dynamic context
* @return a SequenceIterator delivering ObjectValue<Resource> items
* @throws XPathException
*/
private SequenceIterator getSequenceIterator(final ResourceCollection collection, final XPathContext context) throws XPathException {
final Iterator extends Resource> sources = collection.getResources(context);
return new SequenceIterator() {
public Item next() {
if (sources.hasNext()) {
return new ObjectValue(sources.next());
} else {
return null;
}
}
public void close() {
// no action;
}
public int getProperties() {
return 0;
}
public SequenceIterator getAnother() throws XPathException {
return getSequenceIterator(collection, context);
}
};
}
/**
* Dynamic call on collection() function
*
* @param context the dynamic evaluation context
* @param arguments the values of the arguments, supplied as Sequences.
* @return the sequence of nodes forming the collection
* @throws XPathException
*/
public Sequence call(final XPathContext context, Sequence[] arguments) throws XPathException {
String href;
if (getArity() == 0) {
// No arguments supplied: this gets the default collection
href = context.getController().getDefaultCollection();
} else {
Item arg = arguments[0].head();
if (arg == null) {
href = context.getController().getDefaultCollection();
} else {
href = arg.getStringValue();
}
}
if (href == null) {
throw new XPathException("No default collection has been defined", "FODC0002");
}
String absoluteURI = getAbsoluteCollectionURI(href, context);
// See if the collection has been cached
GroundedValue cachedCollection = (GroundedValue)context.getController().getUserData("saxon:collections", absoluteURI);
if (cachedCollection != null) {
return cachedCollection;
}
// Call the user-supplied CollectionFinder to get the ResourceCollection
String absoluteURI1 = getAbsoluteCollectionURI(absoluteURI, context);
CollectionFinder collectionFinder = context.getController().getCollectionFinder();
ResourceCollection collection = collectionFinder.findCollection(context, absoluteURI1);
if (collection == null) {
collection = new EmptyCollection(EMPTY_COLLECTION_URI);
}
// Get an iterator over the resources in the collection
SequenceIterator sourceSeq = getSequenceIterator(collection, context);
// Get an iterator over the items representing the resources
SequenceIterator result = context.getConfiguration()
.getMultithreadedItemMappingIterator(sourceSeq,
new ItemMappingFunction, Item>() {
public Item mapItem(ObjectValue item1) throws XPathException {
return item1.getObject().getItem(context);
}
});
// For releases earlier than 3.0, wrap result in an item checker to check the results are nodes
if (getRetainedStaticContext().getXPathVersion() <= 30) {
RoleDiagnostic role = new RoleDiagnostic(RoleDiagnostic.FUNCTION, "collection", 0);
ItemTypeCheckingFunction function = new ItemTypeCheckingFunction- (
AnyNodeTest.getInstance(), role, null, context.getConfiguration());
result = new ItemMappingIterator(result, function, true);
}
// If the collection is stable, cache the result
if (collection.isStable(context) || context.getConfiguration().getBooleanProperty(FeatureKeys.STABLE_COLLECTION_URI)) {
Controller controller = context.getController();
DocumentPool docPool = controller.getDocumentPool();
cachedCollection = SequenceTool.toGroundedValue(result);
SequenceIterator iter = cachedCollection.iterate();
Item item;
while ((item = iter.next()) != null) {
if (item instanceof NodeInfo && ((NodeInfo)item).getNodeKind() == Type.DOCUMENT) {
String docUri = ((NodeInfo)item).getSystemId();
DocumentURI docKey = new DocumentURI(docUri);
TreeInfo info = item instanceof TreeInfo ? (TreeInfo)item : new GenericTreeInfo(controller.getConfiguration(), (NodeInfo)item);
docPool.add(info, docKey);
}
}
context.getController().setUserData("saxon:collections", absoluteURI, cachedCollection);
return cachedCollection;
}
return new LazySequence(result);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy