Index: extra/IndexedSequence.java =================================================================== --- extra/IndexedSequence.java (revision 0) +++ extra/IndexedSequence.java (revision 0) @@ -0,0 +1,112 @@ +package net.sf.saxon.extra; + +import net.sf.saxon.trans.XPathException; +import net.sf.saxon.expr.*; +import net.sf.saxon.om.*; +import net.sf.saxon.value.*; +import net.sf.saxon.functions.Evaluate; +import net.sf.saxon.trace.Location; + +import java.util.HashMap; +import java.util.ArrayList; + +/** An IndexedSequence stores a sequence in a format allowing quick lookup + * via keys. + * An index is generated on construction time and lookups can be done using + * the find function. + * + * @author Freek van Walderveen + */ + +public class IndexedSequence { + + private HashMap index; + + /** + * Construct an IndexedSequence over a given node set using an expression + * providing the key for each node by evaluating it over all nodes. + * + * @param context the XPath dynamic evaluation context + * @param nsv the input sequence + * @param pexpression the expression whose maximum is to be computed + */ + public IndexedSequence(XPathContext context, + SequenceIterator nsv, + Evaluate.PreparedExpression pexpression) + throws XPathException { + index = new HashMap(); //TODO initial size + + if (nsv == null) { + return; + } + if (pexpression == null) { + return; + } + + // Build index for this sequence and value + XPathContext c = context.newMinorContext(); + c.setOriginatingConstructType(Location.SAXON_HIGHER_ORDER_EXTENSION_FUNCTION); + c.setCurrentIterator(nsv); + while (true) { + Item next = nsv.next(); + if (next == null) { + break; + } + String stringVal = pexpression.expression.evaluateItem(c).getStringValue(); + if (index.containsKey(stringVal)) { + // remove current item and add ArrayList with all items + Object old = index.get(stringVal); + ArrayList list; + if (old instanceof ArrayList) { + list = (ArrayList)old; + } else { + list = new ArrayList(); + list.add(old); + } + list.add(nsv.current()); + index.put(stringVal, list); + } else { + index.put(stringVal, nsv.current()); + } + /* else { + XPathException e = new XPathException("expression in saxon:find():" + v.getStringValue()); + e.setXPathContext(context); + throw e; + }*/ + } + } + + /** + * Find all nodes that have the given key as their index key. The output + * order is not defined. + * @param k key to be searched for + * @return an iterator over a list of nodes for the given key + */ + public SequenceIterator find(Value k) throws XPathException { + if (index.containsKey(k.getStringValue())) { + Object object = index.get(k.getStringValue()); + if (object instanceof Item) { + return SingletonIterator.makeIterator((Item)object); + } else if (object instanceof ArrayList) { + return new ListIterator((ArrayList)object); + } + } + return EmptyIterator.getInstance(); + } + +} + + +// +// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License"); +// you may not use this file except in compliance with the License. You may obtain a copy of the +// License at http://www.mozilla.org/MPL/ +// +// Software distributed under the License is distributed on an "AS IS" basis, +// WITHOUT WARRANTY OF ANY KIND, either express or implied. +// See the License for the specific language governing rights and limitations under the License. +// +// The Original Code is: all this file. +// +// The Initial Developer of the Original Code is Freek van Walderveen +// Index: functions/Extensions.java =================================================================== --- functions/Extensions.java (revision 233) +++ functions/Extensions.java (working copy) @@ -21,6 +21,7 @@ import net.sf.saxon.type.BuiltInAtomicType; import net.sf.saxon.type.Type; import net.sf.saxon.value.*; +import net.sf.saxon.extra.IndexedSequence; import javax.xml.transform.*; import javax.xml.transform.stream.StreamResult; @@ -360,7 +361,35 @@ // } // return min; // } - + + /** + * Create an index for a node set to allow fast lookups using the find + * function. The expression is evaluated for each node in the set to + * determine the key for indexing. + * + * @param context XPath dynamic evaluation context + * @param nsv input sequence + * @param pexpression expression of which the evaluation is to be indexed over + * @return an index for the sequence with the expression as key + */ + public static IndexedSequence index(XPathContext context, + SequenceIterator nsv, + Evaluate.PreparedExpression pexpression) throws XPathException { + return new IndexedSequence(context, nsv, pexpression); + } + + /** + * Find all values having k as their key. + * + * @param seqIndex the index in which to search + * @param k the key for which a value is to be found + * @return an iterator over the items in the index (in unspecified order) for which the key is equal to k + */ + public static SequenceIterator find(IndexedSequence seqIndex, + Value k) throws XPathException { + return seqIndex.find(k); + } + /** * Get the node with maximum numeric value of the string-value of each of a set of nodes *