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.xml.XmlUtil;
032: import com.caucho.xpath.Env;
033: import com.caucho.xpath.ExprEnvironment;
034: import com.caucho.xpath.XPathException;
035:
036: import org.w3c.dom.Node;
037:
038: /**
039: * Matches nodes preceding the current node, not counting descendants.
040: */
041: public class FromPrevious extends Axis {
042: public FromPrevious(AbstractPattern parent) {
043: super (parent);
044:
045: if (parent == null)
046: throw new RuntimeException();
047: }
048:
049: /**
050: * matches if we can find a following node matching the parent pattern.
051: */
052: public boolean match(Node node, ExprEnvironment env)
053: throws XPathException {
054: if (node == null)
055: return false;
056:
057: return getAxisContext(node, env, node) != null;
058: }
059:
060: /**
061: * The iterator is in reverse document order.
062: */
063: public boolean isAscending() {
064: return false;
065: }
066:
067: /**
068: * Calculates position by counting next nodes matching the pattern.
069: *
070: * The axis is a previous node matching the parent pattern.
071: */
072: public int position(Node node, Env env, AbstractPattern pattern)
073: throws XPathException {
074: int index = env.getPositionIndex();
075:
076: boolean hasMatch = true;
077: Node axis = XmlUtil.getNext(node);
078:
079: for (; axis != null; axis = XmlUtil.getNext(axis)) {
080: if (hasMatch && _parent.match(axis, env)) {
081: boolean hasParent = false;
082: for (Node ptr = node.getParentNode(); ptr != null; ptr = ptr
083: .getParentNode()) {
084: if (ptr == axis) {
085: hasParent = true;
086: break;
087: }
088: }
089:
090: if (!hasParent && --index < 0) {
091: hasMatch = false;
092: break;
093: }
094: }
095:
096: if (!hasMatch && pattern.match(axis, env))
097: hasMatch = true;
098: }
099:
100: int count = 1;
101: Node ptr;
102: for (ptr = axis; ptr != null && ptr.getNextSibling() == null; ptr = ptr
103: .getParentNode()) {
104: }
105:
106: for (ptr = XmlUtil.getPrevious(axis); ptr != null
107: && ptr != node; ptr = XmlUtil.getPrevious(ptr)) {
108: if (pattern.match(ptr, env)) {
109: boolean hasParent = false;
110: for (Node n = node; n != null; n = n.getParentNode()) {
111: if (n == ptr) {
112: hasParent = true;
113: break;
114: }
115: }
116:
117: if (!hasParent)
118: count++;
119: }
120: }
121:
122: for (; axis != null; axis = XmlUtil.getNext(axis)) {
123: if (_parent.match(axis, env)) {
124: env.setMorePositions(true);
125: break;
126: }
127: }
128:
129: return count;
130: }
131:
132: /**
133: * counts matching nodes preceding the axis context.
134: *
135: * count() walks backwards from the axis context.
136: */
137: public int count(Node node, Env env, AbstractPattern pattern)
138: throws XPathException {
139: int index = env.getPositionIndex();
140:
141: Node axis = getAxisContext(node, env, node);
142: for (; index > 0; index--)
143: axis = getAxisContext(axis, env, node);
144:
145: if (getAxisContext(axis, env, node) != null)
146: env.setMorePositions(true);
147:
148: int count = 0;
149: for (Node ptr = axis; ptr != null; ptr = XmlUtil
150: .getPrevious(ptr)) {
151: if (pattern.match(ptr, env) && !isDescendant(ptr, axis))
152: count++;
153: }
154:
155: return count;
156: }
157:
158: /**
159: * Returns true if the pattern is strictly ascending.
160: */
161: public boolean isUnique() {
162: if (_parent == null)
163: return true;
164: else
165: return _parent.isSingleSelect();
166: }
167:
168: /**
169: * Returns the first node in the selection order.
170: *
171: * @param node the current node
172: *
173: * @return the first node
174: */
175: public Node firstNode(Node node, ExprEnvironment env) {
176: return XmlUtil.getPrevious(node);
177: }
178:
179: /**
180: * Returns the next node in the selection order.
181: *
182: * @param node the current node
183: * @param lastNode the original node (for checking ancestors)
184: *
185: * @return the next node
186: */
187: public Node nextNode(Node node, Node lastNode) {
188: loop: while (node != null) {
189: node = XmlUtil.getPrevious(node);
190:
191: for (Node ptr = lastNode; ptr != null; ptr = ptr
192: .getParentNode()) {
193: if (ptr == node)
194: continue loop;
195: }
196:
197: return node;
198: }
199:
200: return null;
201: }
202:
203: /**
204: * Returns the last node in the selection order.
205: *
206: * @param node the current node
207: *
208: * @return the last node
209: */
210: public Node lastNode(Node node) {
211: return node;
212: }
213:
214: /**
215: * Returns the next AxisContext. The axis context is the first following
216: * node matching the parent pattern.
217: */
218: private Node getAxisContext(Node axis, ExprEnvironment env,
219: Node node) throws XPathException {
220: if (axis == null)
221: return null;
222:
223: while ((axis = XmlUtil.getNext(axis)) != null) {
224: if (!isDescendant(axis, node) && _parent.match(axis, env))
225: return axis;
226: }
227:
228: return null;
229: }
230:
231: private boolean isDescendant(Node descendant, Node node) {
232: for (; descendant != node && descendant != null; descendant = descendant
233: .getParentNode()) {
234: }
235:
236: return descendant != null;
237: }
238:
239: public String toString() {
240: return getPrefix() + "preceding::";
241: }
242: }
|