/**
* Copyright (C) 2010 Orbeon, Inc.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either version
* 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
*/
package org.orbeon.oxf.xforms.function.xxforms;
import org.orbeon.oxf.xforms.function.XFormsFunction;
import org.orbeon.saxon.expr.Expression;
import org.orbeon.saxon.expr.PathMap;
import org.orbeon.saxon.expr.XPathContext;
import org.orbeon.saxon.om.Item;
import org.orbeon.saxon.om.NodeInfo;
import org.orbeon.saxon.om.SequenceIterator;
import org.orbeon.saxon.sort.AtomicComparer;
import org.orbeon.saxon.sort.SortKeyDefinition;
import org.orbeon.saxon.sort.SortKeyEvaluator;
import org.orbeon.saxon.sort.SortedIterator;
import org.orbeon.saxon.trans.XPathException;
import org.orbeon.saxon.value.Value;
/**
* exf:sort() function
*/
public class XXFormsSort extends XFormsFunction {
@Override
public SequenceIterator iterate(XPathContext xpathContext) throws XPathException {
final Expression sequenceToSortExpression = argument[0];
final Expression sortKeyExpression = argument[1];
return sort(xpathContext, null, sequenceToSortExpression, sortKeyExpression);
}
protected SequenceIterator sort(XPathContext xpathContext, final XPathContext keyXPathContext, Expression sequenceToSortExpression, final Expression sortKeyExpression) throws XPathException {
final SortKeyEvaluator sortKeyEvaluator = new SortKeyEvaluator() {
public Item evaluateSortKey(int n, XPathContext context) throws XPathException {
// The context may be provided from "outside" (e.g. because the expression was compiled at runtime).
// In that case, use it, and make sure it has the correct current iterator.
final XPathContext sortKeyContext; {
if (keyXPathContext != null) {
sortKeyContext = keyXPathContext;
sortKeyContext.setCurrentIterator(context.getCurrentIterator());
} else {
sortKeyContext = context;
}
}
Item c = sortKeyExpression.evaluateItem(sortKeyContext);
if (c instanceof NodeInfo) {
final Value v = ((NodeInfo)c).atomize();
if (v.getLength() == 0) {
c = null;
} else if (v.getLength() == 1) {
c = v.itemAt(0);
} else {
throw new XPathException("error in xxf:sort() - a node has a typed value of length > 1");
}
}
return c;
}
};
final SortKeyDefinition sortKeyDefinition = getSortKeyDefinition(sortKeyExpression);
final AtomicComparer comparer = sortKeyDefinition.makeComparator(xpathContext);
final AtomicComparer[] comparers = { comparer };
return new SortedIterator(xpathContext, sequenceToSortExpression.iterate(xpathContext), sortKeyEvaluator, comparers);
}
private SortKeyDefinition getSortKeyDefinition(Expression sortKeyExpression) {
final Expression datatypeExpression = (argument.length > 2) ? argument[2] : null;
final Expression orderExpression = (argument.length > 3) ? argument[3] : null;
final Expression caseOrderExpression = (argument.length > 4) ? argument[4] : null;
// Expression langExpression = argument[5];// new in XSLT 2.0
// Expression collationExpression = argument[6];// new in XSLT 2.0
// Expression stableExpression = argument[7];// new in XSLT 2.0
final SortKeyDefinition sortKeyDefinition = new SortKeyDefinition();
sortKeyDefinition.setSortKey(sortKeyExpression);
if (datatypeExpression != null)
sortKeyDefinition.setDataTypeExpression(datatypeExpression);
if (orderExpression != null)
sortKeyDefinition.setOrder(orderExpression);
if (caseOrderExpression != null)
sortKeyDefinition.setCaseOrder(caseOrderExpression);
// sortKey.setLanguage(langExpression);
// sortKey.setCollationName(collationExpression);
// sortKey.setStable(stableExpression);
return sortKeyDefinition;
}
@Override
public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
final PathMap.PathMapNodeSet target = argument[0].addToPathMap(pathMap, pathMapNodeSet);
final SortKeyDefinition sortKeyDefinition = getSortKeyDefinition(argument[1]);
// Sort key
sortKeyDefinition.getSortKey().addToPathMap(pathMap, target);
// Sort key parameters
Expression e = sortKeyDefinition.getOrder();
if (e != null) {
e.addToPathMap(pathMap, pathMapNodeSet);
}
e = sortKeyDefinition.getCaseOrder();
if (e != null) {
e.addToPathMap(pathMap, pathMapNodeSet);
}
e = sortKeyDefinition.getDataTypeExpression();
if (e != null) {
e.addToPathMap(pathMap, pathMapNodeSet);
}
e = sortKeyDefinition.getLanguage();
if (e != null) {
e.addToPathMap(pathMap, pathMapNodeSet);
}
e = sortKeyDefinition.getCollationNameExpression();
if (e != null) {
e.addToPathMap(pathMap, pathMapNodeSet);
}
return target;
}
}