net.sf.saxon.expr.accum.AccumulatorManager Maven / Gradle / Ivy
Show all versions of Saxon-HE Show documentation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2022 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.expr.accum;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.expr.XPathContextMajor;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.om.TreeInfo;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.tiny.TinyTree;
import net.sf.saxon.tree.wrapper.VirtualCopy;
import net.sf.saxon.tree.wrapper.VirtualTreeInfo;
import java.util.*;
/**
* Manager for accumulator functions (XSLT 3.0).
*
* In principle there is a dataset containing accumulator values for every accumulator/document pair.
* In addition, if the same stylesheet is run concurrently in multiple transformations, and a document
* is shared between these transformations, the data values for the accumulator can differ between the
* two transformations, because the accumulator rules can depend on values of stylesheet parameters
* or on context variables such as current-dateTime.
*
* It is important that the accumulator data for a tree does not cause the tree to be locked in
* memory for the duration of a transformation. We therefore keep a weak reference to the tree. For
* each tree, until it is garbage collected, there is a map from accumulator names to accumulator data.
*/
public class AccumulatorManager {
private final transient WeakHashMap> accumulatorDataIndex =
new WeakHashMap>();
private final transient WeakHashMap> applicableAccumulators =
new WeakHashMap>();
public AccumulatorManager() {
}
/**
* By default, all accumulators are applicable to any given tree. If this method is called,
* a specific set of accumulators are registered as applicable. This set may be empty.
* @param tree the document tree in question
* @param accumulators the set of accumulators that are appicable
*/
public void setApplicableAccumulators(TreeInfo tree, Set extends Accumulator> accumulators) {
applicableAccumulators.put(tree, accumulators);
}
/**
* Get the set of accumulators applicable to a particular tree
*
* @param tree the tree in question
* @return the set of applicable accumulators; a value of null indicates the universal set (all accumulators
* are applicable
*/
public Set extends Accumulator> getApplicableAccumulators(TreeInfo tree) {
return applicableAccumulators.get(tree);
}
/**
* Ask whether a particular accumulator is applicable to a particular tree
* @param tree the tree in question
* @param accumulator the accumulator in question
* @return true if the accumulator is applicable to this tree, otherwise false
*/
public boolean isApplicable(TreeInfo tree, Accumulator accumulator) {
Set extends Accumulator> accSet = applicableAccumulators.get(tree);
return accSet == null || accSet.contains(accumulator);
}
private static final AccumulatorData MARKER = new AccumulatorData(null);
/**
* Get the data relating to a particular accumulator for a particular unstreamed document tree
*
* @param doc the source document tree in question
* @param acc the required accumulator
* @param context the XPath dynamic evaluation context
* @return a data structure holding the evaluated values of the accumulator for this source document
* @throws XPathException if any dynamic error occurs
*/
public synchronized IAccumulatorData getAccumulatorData(TreeInfo doc, Accumulator acc, XPathContext context)
throws XPathException {
Map map = accumulatorDataIndex.get(doc);
if (map != null) {
IAccumulatorData data = map.get(acc);
if (data != null) {
if (data == MARKER) {
throw new XPathException("Accumulator " + acc.getAccumulatorName().getDisplayName() +
" requires access to its own value", "XTDE3400");
}
return data;
}
} else {
map = new HashMap();
accumulatorDataIndex.put(doc, map);
}
map.put(acc, MARKER);
if (doc instanceof VirtualTreeInfo && ((VirtualTreeInfo)doc).isCopyAccumulators()) {
NodeInfo original = ((VirtualCopy) doc.getRootNode()).getOriginalNode();
IAccumulatorData originalData = getAccumulatorData(original.getTreeInfo(), acc, context);
VirtualAccumulatorData vad = new VirtualAccumulatorData(originalData);
map.put(acc, vad);
return vad;
} else if (doc instanceof TinyTree && ((TinyTree)doc).getCopiedFrom() != null) {
IAccumulatorData original = getAccumulatorData(((TinyTree) doc).getCopiedFrom().getTreeInfo(), acc, context);
return new PathMappedAccumulatorData(original, ((TinyTree) doc).getCopiedFrom());
} else {
AccumulatorData d = new AccumulatorData(acc);
XPathContextMajor c2 = context.newCleanContext();
c2.setCurrentComponent(acc.getDeclaringComponent());
try {
d.buildIndex(doc.getRootNode(), c2);
map.put(acc, d);
return d;
} catch (XPathException err) {
IAccumulatorData failed = new FailedAccumulatorData(acc, err);
map.put(acc, failed);
return failed;
}
}
}
/**
* Add the accumulator data for a particular accumulator, if it does not already exist
* @param doc the document/tree whose accumulator data is being added
* @param acc the accumulator in question
* @param accData the data holding the values of the accumulator for the nodes in this tree
*/
public synchronized void addAccumulatorData(TreeInfo doc, Accumulator acc, IAccumulatorData accData) {
Map map = accumulatorDataIndex.get(doc);
if (map != null) {
IAccumulatorData data = map.get(acc);
if (data != null) {
return;
}
} else {
map = new HashMap();
accumulatorDataIndex.put(doc, map);
}
map.put(acc, accData);
}
}