/* * eXist Open Source Native XML Database * Copyright (C) 2001-04 Wolfgang M. Meier (wolfgang@exist-db.org) * and others (see http://exist-db.org) * * 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 * 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. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package org.exist.xquery.value; import org.exist.dom.persistent.ContextItem; import org.exist.dom.persistent.NodeProxy; import org.exist.dom.persistent.NodeSet; import org.exist.xquery.Constants; import org.exist.xquery.Expression; import org.exist.xquery.OrderSpec; import org.exist.xquery.XPathException; import java.util.Arrays; import java.util.Comparator; /** * A sequence that sorts its items in the order specified by the order specs * of an "order by" clause. Used by {@link org.exist.xquery.ForExpr}. * <p> * For better performance, the whole input sequence is sorted in one single step. * However, this only works if every order expression returns a result of type * node. * * @author wolf */ public class PreorderedValueSequence extends AbstractSequence { private final OrderSpec[] orderSpecs; private final OrderedNodeProxy[] nodes; public PreorderedValueSequence(OrderSpec specs[], Sequence input, int contextId) throws XPathException { this.orderSpecs = specs; nodes = new OrderedNodeProxy[input.getItemCount()]; int j = 0; for (final SequenceIterator i = input.unorderedIterator(); i.hasNext(); j++) { final NodeProxy p = (NodeProxy) i.nextItem(); nodes[j] = new OrderedNodeProxy(p); p.addContextNode(contextId, nodes[j]); } processAll(); } private void processAll() throws XPathException { for (int i = 0; i < orderSpecs.length; i++) { final Expression expr = orderSpecs[i].getSortExpression(); final NodeSet result = expr.eval(null).toNodeSet(); for (final NodeProxy p : result) { ContextItem context = p.getContext(); //TODO : review to consider transverse context while (context != null) { if (context.getNode() instanceof OrderedNodeProxy) { final OrderedNodeProxy cp = (OrderedNodeProxy) context.getNode(); cp.values[i] = p.atomize(); } context = context.getNextDirect(); } } } } public void clearContext(int contextId) throws XPathException { for (OrderedNodeProxy node : nodes) { node.clearContext(contextId); } } /* (non-Javadoc) * @see org.exist.xquery.value.AbstractSequence#getItemType() */ public int getItemType() { return Type.NODE; } /* (non-Javadoc) * @see org.exist.xquery.value.AbstractSequence#iterate() */ public SequenceIterator iterate() throws XPathException { sort(); return new PreorderedValueSequenceIterator(); } /* (non-Javadoc) * @see org.exist.xquery.value.AbstractSequence#unorderedIterator() */ public SequenceIterator unorderedIterator() throws XPathException { return new PreorderedValueSequenceIterator(); } /* (non-Javadoc) * @see org.exist.xquery.value.AbstractSequence#getLength() */ public int getItemCount() { return nodes.length; } public boolean isEmpty() { return nodes.length == 0; } public boolean hasOne() { return nodes.length == 1; } /* (non-Javadoc) * @see org.exist.xquery.value.AbstractSequence#add(org.exist.xquery.value.Item) */ public void add(Item item) throws XPathException { } /* (non-Javadoc) * @see org.exist.xquery.value.AbstractSequence#itemAt(int) */ public Item itemAt(int pos) { return nodes[pos]; } /* (non-Javadoc) * @see org.exist.xquery.value.Sequence#toNodeSet() */ public NodeSet toNodeSet() throws XPathException { return null; } public MemoryNodeSet toMemNodeSet() throws XPathException { return null; } /* (non-Javadoc) * @see org.exist.xquery.value.Sequence#removeDuplicates() */ public void removeDuplicates() { // TODO: is this ever relevant? } private void sort() { Arrays.sort(nodes, new OrderedComparator()); } private class OrderedComparator implements Comparator<OrderedNodeProxy> { /* (non-Javadoc) * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ public int compare(OrderedNodeProxy p1, OrderedNodeProxy p2) { int cmp = 0; AtomicValue a, b; for (int i = 0; i < p1.values.length; i++) { try { a = p1.values[i]; b = p2.values[i]; if (a == AtomicValue.EMPTY_VALUE && b != AtomicValue.EMPTY_VALUE) { if ((orderSpecs[i].getModifiers() & OrderSpec.EMPTY_LEAST) != 0) { cmp = Constants.INFERIOR; } else { cmp = Constants.SUPERIOR; } } else if (a != AtomicValue.EMPTY_VALUE && b == AtomicValue.EMPTY_VALUE) { if ((orderSpecs[i].getModifiers() & OrderSpec.EMPTY_LEAST) != 0) { cmp = Constants.SUPERIOR; } else { cmp = Constants.INFERIOR; } } else { cmp = a.compareTo(orderSpecs[i].getCollator(), b); } if ((orderSpecs[i].getModifiers() & OrderSpec.DESCENDING_ORDER) != 0) { cmp = cmp * -1; } if (cmp != Constants.EQUAL) { break; } } catch (final XPathException e) { } } return cmp; } } private class OrderedNodeProxy extends NodeProxy { AtomicValue[] values; public OrderedNodeProxy(NodeProxy p) { super(p); values = new AtomicValue[orderSpecs.length]; for (int i = 0; i < values.length; i++) values[i] = AtomicValue.EMPTY_VALUE; } } private class PreorderedValueSequenceIterator implements SequenceIterator { int pos = 0; /* (non-Javadoc) * @see org.exist.xquery.value.SequenceIterator#hasNext() */ public boolean hasNext() { return pos < nodes.length; } /* (non-Javadoc) * @see org.exist.xquery.value.SequenceIterator#nextItem() */ public Item nextItem() { if (pos < nodes.length) { return nodes[pos++]; } return null; } } }