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.log.Log;
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: import java.util.logging.Logger;
039:
040: /**
041: * A node selection pattern. AbstractPatterns represent compiled XPath node selectors.
042: * They can be used to find nodes, select nodes, and test if a node matches
043: * a pattern.
044: *
045: * <p>There are two types of patterns: select patterns and match patterns.
046: * <p>Select patterns match a node relative to another node.
047: * <code>find</code> and <code>select</code> use select patterns.
048: * <p>Match patterns match a node in isolation. <code>isMatch</code> uses
049: * match patterns.
050: */
051: abstract public class AbstractPattern {
052: protected static final Logger log = Log.open(AbstractPattern.class);
053:
054: // This is the value Axis wants
055: public static final String XMLNS = "http://www.w3.org/2000/xmlns/";
056:
057: protected AbstractPattern _parent;
058: protected AbstractPattern _child;
059:
060: AbstractPattern(AbstractPattern parent) {
061: _parent = parent;
062:
063: if (parent != null && parent._child == null)
064: parent._child = this ;
065: }
066:
067: /**
068: * Returns the parent pattern.
069: */
070: public AbstractPattern getParent() {
071: return _parent;
072: }
073:
074: /**
075: * Returns the pattern's default priority as defined by the XSLT draft.
076: */
077: public double getPriority() {
078: return 0.5;
079: }
080:
081: /**
082: * Returns the name of the matching node or '*' if many nodes match.
083: *
084: * <p>The Xsl package uses this to speed template matching.
085: */
086: public String getNodeName() {
087: return "*";
088: }
089:
090: /**
091: * Returns an iterator selecting nodes in document order.
092: *
093: * @param node the starting node.
094: * @param env the variable environment.
095: *
096: * @return an iterator selecting nodes in document order.
097: */
098: public NodeIterator select(Node node, ExprEnvironment env)
099: throws XPathException {
100: NodeIterator base = createNodeIterator(node, env,
101: copyPosition());
102:
103: if (isStrictlyAscending())
104: return base;
105: else
106: return new MergeIterator(env, base);
107: }
108:
109: /**
110: * Returns an iterator selecting unique nodes. The nodes are not
111: * necessarily in document order.
112: *
113: * @param node the starting node.
114: * @param env the variable environment.
115: * @param context the context node.
116: *
117: * @return an iterator selecting unique nodes.
118: */
119: public NodeIterator selectUnique(Node node, ExprEnvironment env)
120: throws XPathException {
121: NodeIterator base = createNodeIterator(node, env,
122: copyPosition());
123:
124: if (isUnique())
125: return base;
126: else
127: return new UniqueIterator(env, base);
128: }
129:
130: /**
131: * Find any node matching the pattern.
132: *
133: * @param node the current node
134: * @param env the xpath environment
135: *
136: * @return one of the matching nodes
137: */
138: public Node findAny(Node node, ExprEnvironment env)
139: throws XPathException {
140: NodeIterator base = createNodeIterator(node, env,
141: copyPosition());
142:
143: return base.nextNode();
144: }
145:
146: /**
147: * Returns true if the pattern is strictly ascending.
148: */
149: public boolean isStrictlyAscending() {
150: if (_parent != null)
151: return _parent.isStrictlyAscending();
152: else
153: return false;
154: }
155:
156: /**
157: * Returns true if the pattern's iterator returns unique nodes.
158: */
159: public boolean isUnique() {
160: if (_parent != null)
161: return _parent.isUnique();
162: else
163: return false;
164: }
165:
166: /**
167: * Returns true if the pattern selects a single node
168: */
169: boolean isSingleSelect() {
170: if (_parent != null)
171: return _parent.isSingleSelect();
172: else
173: return false;
174: }
175:
176: /**
177: * Returns true if the pattern returns nodes on a single level.
178: */
179: boolean isSingleLevel() {
180: return isSingleSelect();
181: }
182:
183: /**
184: * Creates a new node iterator.
185: *
186: * @param node the starting node
187: * @param env the variable environment
188: * @param pattern the level pattern
189: *
190: * @return the node iterator
191: */
192: public NodeIterator createNodeIterator(Node node,
193: ExprEnvironment env, AbstractPattern pattern)
194: throws XPathException {
195: if (_parent == null)
196: throw new RuntimeException(String.valueOf(this ) + " "
197: + getClass());
198: else
199: return _parent.createNodeIterator(node, env, pattern);
200: }
201:
202: /**
203: * Returns the first node in the selection order.
204: *
205: * @param node the current node
206: * @param variable environment
207: *
208: * @return the first node
209: */
210: public Node firstNode(Node node, ExprEnvironment env)
211: throws XPathException {
212: throw new UnsupportedOperationException(String.valueOf(this )
213: + " " + getClass());
214: }
215:
216: /**
217: * Returns the last node in the selection order.
218: *
219: * @param node the current node
220: *
221: * @return the last node
222: */
223: public Node lastNode(Node node) {
224: return null;
225: }
226:
227: /**
228: * Returns the next node in the selection order.
229: *
230: * @param node the current node
231: * @param last the last node
232: *
233: * @return the next node
234: */
235: public Node nextNode(Node node, Node last) throws XPathException {
236: throw new UnsupportedOperationException();
237: }
238:
239: /**
240: * The core match function test if the pattern matches the node.
241: *
242: * @param node the node to test
243: * @param env the variable environment.
244: *
245: * @return true if the node matches the pattern.
246: */
247: public abstract boolean match(Node node, ExprEnvironment env)
248: throws XPathException;
249:
250: /**
251: * Return true if the iterator is in document-order.
252: */
253: public boolean isAscending() {
254: return true;
255: }
256:
257: /**
258: * Returns the position of the node in its context for a match pattern.
259: *
260: * @param node the current node
261: * @param env the variable environment
262: * @param pattern the position pattern
263: *
264: * @return the node's position.
265: */
266: public int position(Node node, Env env, AbstractPattern pattern)
267: throws XPathException {
268: return _parent.position(node, env, pattern);
269: }
270:
271: /**
272: * Returns the number of nodes in its context for a match pattern.
273: *
274: * @param node the current node
275: * @param env the variable environment
276: * @param pattern the position pattern
277: *
278: * @return the count of nodes in the match selection
279: */
280: public int count(Node node, Env env, AbstractPattern pattern)
281: throws XPathException {
282: return _parent.count(node, env, pattern);
283: }
284:
285: /**
286: * Returns the owning axis for the pattern.
287: */
288: public AbstractPattern copyAxis() {
289: if (_parent != null)
290: return _parent.copyAxis();
291: else
292: return null;
293: }
294:
295: /**
296: * Returns the position matching pattern.
297: */
298: public AbstractPattern copyPosition() {
299: return this ;
300: }
301:
302: /**
303: * For string conversion, returns the string prefix corresponding to
304: * the parents.
305: */
306: protected String getPrefix() {
307: if (_parent == null || _parent instanceof FromAny)
308: return "";
309: else if (_parent instanceof FromContext) { // check for in Filter?
310: FromContext context = (FromContext) _parent;
311:
312: String name = "";
313: for (int i = 0; i < context.getCount(); i++) {
314: name += "../";
315: }
316: return name;
317: } else if (_parent instanceof FromRoot)
318: return "/";
319: else
320: return _parent + "/";
321: }
322:
323: public String toPatternString() {
324: return toString();
325: }
326: }
|