001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.xpath.pattern;
030:
031: import com.caucho.xpath.Env;
032: import com.caucho.xpath.Expr;
033: import com.caucho.xpath.ExprEnvironment;
034: import com.caucho.xpath.XPathException;
035:
036: import org.w3c.dom.Node;
037:
038: /**
039: * matches a node if it matches a filter expression. Filter implements
040: * the a[b] pattern.
041: */
042: public class FilterPattern extends AbstractPattern {
043: private Expr _expr;
044:
045: private AbstractPattern _position;
046:
047: public FilterPattern(AbstractPattern parent, Expr expr) {
048: super (parent);
049:
050: _expr = expr;
051:
052: if (parent == null)
053: throw new RuntimeException();
054: }
055:
056: public String getNodeName() {
057: return _parent.getNodeName();
058: }
059:
060: /**
061: * Returns the filter's expression.
062: */
063: public Expr getExpr() {
064: return _expr;
065: }
066:
067: /**
068: * Matches if the filter expression matches. When the filter expression
069: * returns a number, match if the position() equals the expression.
070: *
071: * @param node the current node to test
072: * @param env the variable environment
073: *
074: * @return true if the pattern matches.
075: */
076: /* Because match works from the bottom up, it must sometimes evaluate
077: * the expression several times. Take the match pattern
078: * 'a/preceding-sibling::b[2]'. Given the XML:
079: * <foo>
080: * <b id=1/>
081: * <b id=2/>
082: * <a id=3/>
083: * <b id=4/>
084: * <a id=5/>
085: * </foo>
086: * The node b[@id=1] has two valid positions: 2 and 3, corresponding to
087: * 'axis-contexts' a[@id=3] and a[@id=5].
088: *
089: * The position index iterates through the 'axis-contexts.' The axis,
090: * e.g. preceding-sibling, signals the filter that another axis-context
091: * is available through env.setMorePositions().
092: */
093: public boolean match(Node node, ExprEnvironment env)
094: throws XPathException {
095: if (!_parent.match(node, env))
096: return false;
097:
098: // Select patterns use the first shortcut.
099: int envPosition = env.getContextPosition();
100:
101: if (envPosition > 0) {
102: if (_expr.isBoolean()) {
103: return _expr.evalBoolean(node, env);
104: } else if (_expr.isNumber()) {
105: double test = _expr.evalNumber(node, env);
106:
107: return (envPosition == (int) test);
108: } else {
109: Object value = _expr.evalObject(node, env);
110:
111: if (value instanceof Number)
112: return (envPosition == ((Number) value).intValue());
113:
114: return Expr.toBoolean(value);
115: }
116: }
117:
118: // Match patterns need to use a more complicated test.
119: if (!(env instanceof Env))
120: throw new RuntimeException(String.valueOf(env));
121:
122: Env globalEnv = (Env) env;
123: boolean oldMorePositions = globalEnv.setMorePositions(true);
124: int oldIndex = globalEnv.setPositionIndex(0);
125: try {
126: for (int i = 0; globalEnv.hasMorePositions(); i++) {
127: globalEnv.setPositionIndex(i);
128: globalEnv.setMorePositions(false);
129:
130: if (_expr.isNumber()) {
131: double test = _expr.evalNumber(node, env);
132: double position = _parent.position(node, globalEnv,
133: _parent.copyPosition());
134:
135: if (position == test)
136: return true;
137: } else if (_expr.isBoolean()) {
138: if (_expr.evalBoolean(node, env))
139: return true;
140: } else {
141: Object value = _expr.evalObject(node, env);
142:
143: if (value instanceof Number) {
144: double test = ((Number) value).doubleValue();
145: double position = _parent.position(node,
146: globalEnv, _parent.copyPosition());
147:
148: if (position == test)
149: return true;
150: } else if (Expr.toBoolean(value))
151: return true;
152: }
153: }
154:
155: return false;
156: } finally {
157: globalEnv.setPositionIndex(oldIndex);
158: globalEnv.setMorePositions(oldMorePositions);
159: }
160: }
161:
162: /**
163: * Creates a new node iterator.
164: *
165: * @param node the starting node
166: * @param env the variable environment
167: * @param match the axis match pattern
168: *
169: * @return the node iterator
170: */
171: public NodeIterator createNodeIterator(Node node,
172: ExprEnvironment env, AbstractPattern match)
173: throws XPathException {
174: NodeIterator parentIter;
175: parentIter = _parent.createNodeIterator(node, env, _parent
176: .copyPosition());
177:
178: return new FilterIterator(parentIter, _expr, env, node);
179: }
180:
181: public AbstractPattern copyPosition() {
182: return null;
183: }
184:
185: public String toString() {
186: return (_parent.toString() + "[" + _expr + "]");
187: }
188: }
|