001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.om.Item;
004: import net.sf.saxon.om.NodeInfo;
005: import net.sf.saxon.om.SequenceIterator;
006: import net.sf.saxon.trace.Location;
007: import net.sf.saxon.trans.XPathException;
008: import net.sf.saxon.value.*;
009:
010: /**
011: * A FilterIterator filters an input sequence using a filter expression. Note that a FilterIterator
012: * is not used where the filter is a constant number (PositionFilter is used for this purpose instead),
013: * so this class does no optimizations for numeric predicates.
014: */
015:
016: public class FilterIterator implements SequenceIterator {
017:
018: protected SequenceIterator base;
019: protected Expression filter;
020: private int position = 0;
021: private Item current = null;
022: protected XPathContext filterContext;
023:
024: /**
025: * Constructor
026: * @param base An iteration of the items to be filtered
027: * @param filter The expression defining the filter predicate
028: * @param context The context in which the expression is being evaluated
029: */
030:
031: public FilterIterator(SequenceIterator base, Expression filter,
032: XPathContext context) {
033: this .base = base;
034: this .filter = filter;
035: filterContext = context.newMinorContext();
036: filterContext.setCurrentIterator(base);
037: filterContext
038: .setOriginatingConstructType(Location.FILTER_EXPRESSION);
039: }
040:
041: /**
042: * Get the next item if there is one
043: */
044:
045: public Item next() throws XPathException {
046: current = getNextMatchingItem();
047: if (current == null) {
048: position = -1;
049: } else {
050: position++;
051: }
052: return current;
053: }
054:
055: /**
056: * Get the next node that matches the filter predicate if there is one,
057: * or null if not.
058: */
059:
060: protected Item getNextMatchingItem() throws XPathException {
061: while (true) {
062: Item next = base.next();
063: if (next == null) {
064: return null;
065: }
066: if (matches()) {
067: return next;
068: }
069: }
070: }
071:
072: /**
073: * Determine whether the context item matches the filter predicate
074: */
075:
076: protected boolean matches() throws XPathException {
077:
078: // This code is carefully designed to avoid reading more items from the
079: // iteration of the filter expression than are absolutely essential.
080:
081: // The code is almost identical to the code in ExpressionTool#effectiveBooleanValue
082: // except for the handling of a numeric result
083:
084: SequenceIterator iterator = filter.iterate(filterContext);
085: Item first = iterator.next();
086: if (first == null) {
087: return false;
088: }
089: if (first instanceof NodeInfo) {
090: return true;
091: } else {
092: if (first instanceof BooleanValue) {
093: if (iterator.next() != null) {
094: ExpressionTool
095: .ebvError("sequence of two or more items starting with an atomic value");
096: }
097: return ((BooleanValue) first).getBooleanValue();
098: } else if (first instanceof StringValue) {
099: if (iterator.next() != null) {
100: ExpressionTool
101: .ebvError("sequence of two or more items starting with an atomic value");
102: }
103: return (first.getStringValueCS().length() != 0);
104: } else if (first instanceof NumericValue) {
105: if (iterator.next() != null) {
106: ExpressionTool
107: .ebvError("sequence of two or more items starting with an atomic value");
108: }
109: IntegerValue basePos = new IntegerValue(base.position());
110: return first.equals(basePos);
111: } else {
112: ExpressionTool
113: .ebvError("sequence starting with an atomic value other than a boolean, number, or string");
114: return false;
115: }
116: }
117: }
118:
119: public Item current() {
120: return current;
121: }
122:
123: public int position() {
124: return position;
125: }
126:
127: /**
128: * Get another iterator to return the same nodes
129: */
130:
131: public SequenceIterator getAnother() throws XPathException {
132: return new FilterIterator(base.getAnother(), filter,
133: filterContext);
134: }
135:
136: /**
137: * Get properties of this iterator, as a bit-significant integer.
138: *
139: * @return the properties of this iterator. This will be some combination of
140: * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
141: * and {@link LOOKAHEAD}. It is always
142: * acceptable to return the value zero, indicating that there are no known special properties.
143: * It is acceptable for the properties of the iterator to change depending on its state.
144: */
145:
146: public int getProperties() {
147: return 0;
148: }
149:
150: /**
151: * Subclass to handle the common special case where it is statically known
152: * that the filter cannot return a numeric value
153: */
154:
155: public static final class NonNumeric extends FilterIterator {
156:
157: public NonNumeric(SequenceIterator base, Expression filter,
158: XPathContext context) {
159: super (base, filter, context);
160: }
161:
162: /**
163: * Determine whether the context item matches the filter predicate
164: */
165:
166: protected boolean matches() throws XPathException {
167: return filter.effectiveBooleanValue(filterContext);
168: }
169:
170: /**
171: * Get another iterator to return the same nodes
172: */
173:
174: public SequenceIterator getAnother() throws XPathException {
175: return new FilterIterator.NonNumeric(base.getAnother(),
176: filter, filterContext);
177: }
178: }
179:
180: /**
181: * Subclass to support the extension function saxon:leading, which terminates
182: * the iteration at the first item whose predicate is false
183: */
184:
185: public static final class Leading extends FilterIterator {
186:
187: public Leading(SequenceIterator base, Expression filter,
188: XPathContext context) {
189: super (base, filter, context);
190: }
191:
192: /**
193: * Determine whether the context item matches the filter predicate
194: */
195:
196: protected boolean matches() throws XPathException {
197: return filter.effectiveBooleanValue(filterContext);
198: }
199:
200: /**
201: * Get the next node that matches the filter predicate if there is one
202: */
203:
204: protected Item getNextMatchingItem() throws XPathException {
205: while (true) {
206: Item next = base.next();
207: if (next == null) {
208: return null;
209: }
210: if (matches()) {
211: return next;
212: } else {
213: // terminate the iteration on the first non-match
214: return null;
215: }
216: }
217: }
218:
219: /**
220: * Get another iterator to return the same nodes
221: */
222:
223: public SequenceIterator getAnother() throws XPathException {
224: return new FilterIterator.Leading(base.getAnother(),
225: filter, filterContext);
226: }
227:
228: }
229:
230: }
231:
232: //
233: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
234: // you may not use this file except in compliance with the License. You may obtain a copy of the
235: // License at http://www.mozilla.org/MPL/
236: //
237: // Software distributed under the License is distributed on an "AS IS" basis,
238: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
239: // See the License for the specific language governing rights and limitations under the License.
240: //
241: // The Original Code is: all this file.
242: //
243: // The Initial Developer of the Original Code is Michael H. Kay.
244: //
245: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
246: //
247: // Contributor(s): none.
248: //
|