KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > icl > saxon > om > Navigator


1 package com.icl.saxon.om;
2
3 import com.icl.saxon.pattern.Pattern;
4 import com.icl.saxon.om.Axis;
5 import com.icl.saxon.Context;
6 import com.icl.saxon.expr.XPathException;
7 import com.icl.saxon.pattern.NodeTypeTest;
8 import com.icl.saxon.pattern.AnyNodeTest;
9 import com.icl.saxon.pattern.NameTest;
10 import com.icl.saxon.pattern.NodeTest;
11 import java.util.Vector JavaDoc;
12
13
14 /**
15   * Navigator provides helper classes for navigating a tree, irrespective
16   * of its implementation
17   * @author <A HREF="mailto:mhkay@iclway.co.uk>Michael H. Kay</A>
18   */

19
20
21
22 public class Navigator {
23
24     /**
25     * Determine if a string is all-whitespace
26     */

27
28     public static boolean isWhite(String JavaDoc content) {
29         for (int i=0; i<content.length(); i++) {
30             char c = content.charAt(i);
31             // all valid XML whitespace characters, and only whitespace characters, are <= 0x20
32
if (((int)c) > 32) {
33                 return false;
34             }
35         }
36         return true;
37     }
38           
39     /**
40     * Determine whether this node is an ancestor of another node
41     * @param node the putative ancestor node
42     * @param other the other node (the putative descendant of this node)
43     * @return true of this node is an ancestor of the other node
44     */

45
46     public static boolean isAncestor(NodeInfo node, NodeInfo other) {
47         NodeInfo parent = other.getParent();
48         if (parent==null) return false;
49         if (parent.isSameNode(node)) return true;
50         return isAncestor(node, parent);
51     }
52
53     /**
54     * Get an absolute XPath expression that identifies a given node within its document
55     */

56     
57     public static String JavaDoc getPath(NodeInfo node) {
58         String JavaDoc pre;
59         switch (node.getNodeType()) {
60             case NodeInfo.ROOT:
61                 return "/";
62             case NodeInfo.ELEMENT:
63                 pre = getPath(node.getParent());
64                 return (pre.equals("/") ? "" : pre) +
65                         "/" + node.getDisplayName() + "[" + getNumberSimple(node) + "]";
66             case NodeInfo.ATTRIBUTE:
67                 return getPath(node.getParent()) + "/@" + node.getDisplayName();
68             case NodeInfo.TEXT:
69                 pre = getPath(node.getParent());
70                 return (pre.equals("/") ? "" : pre) +
71                         "/text()[" + getNumberSimple(node) + "]";
72             case NodeInfo.COMMENT:
73                 pre = getPath(node.getParent());
74                 return (pre.equals("/") ? "" : pre) +
75                     "/comment()[" + getNumberSimple(node) + "]";
76             case NodeInfo.PI:
77                 pre = getPath(node.getParent());
78                 return (pre.equals("/") ? "" : pre) +
79                     "/processing-instruction()[" + getNumberSimple(node) + "]";
80             case NodeInfo.NAMESPACE:
81                 return getPath(node.getParent())+ "/namespace::" + node.getLocalName();
82             default:
83                 return "";
84         }
85     }
86                     
87     /**
88     * Get simple node number. This is defined as one plus the number of previous siblings of the
89     * same node type and name. It is not accessible directly in XSL.
90     * @param context Used for remembering previous result, for performance
91     */

92
93     public static int getNumberSimple(NodeInfo node, Context context) throws XPathException {
94
95         //checkNumberable(node);
96

97         int fingerprint = node.getFingerprint();
98         NodeTest same;
99         
100         if (fingerprint==-1) {
101             same = new NodeTypeTest(node.getNodeType());
102         } else {
103             same = new NameTest(node);
104         }
105                                 
106         NodeEnumeration preceding = node.getEnumeration(Axis.PRECEDING_SIBLING, same);
107
108         int i=1;
109         while (preceding.hasMoreElements()) {
110             NodeInfo prev = preceding.nextElement();
111
112             int memo = context.getRememberedNumber(prev);
113             if (memo>0) {
114                 memo += i;
115                 context.setRememberedNumber(node, memo);
116                 return memo;
117             }
118             
119             i++;
120         }
121         
122         context.setRememberedNumber(node, i);
123         return i;
124     }
125
126     /**
127     * Get simple node number. This is defined as one plus the number of previous siblings of the
128     * same node type and name. It is not accessible directly in XSL. This version doesn't require
129     * the context, and therefore doesn't remember previous results
130     */

131
132     public static int getNumberSimple(NodeInfo node) {
133
134         try {
135             int fingerprint = node.getFingerprint();
136             NodeTest same;
137             
138             if (fingerprint==-1) {
139                 same = new NodeTypeTest(node.getNodeType());
140             } else {
141                 same = new NameTest(node);
142             }
143                                     
144             NodeEnumeration preceding = node.getEnumeration(Axis.PRECEDING_SIBLING, same);
145     
146             int i=1;
147             while (preceding.hasMoreElements()) {
148                 NodeInfo prev = preceding.nextElement();
149                 i++;
150             }
151
152             return i;
153         } catch (XPathException err) {
154             // TODO: improve this.
155
return 1;
156         }
157     }
158
159     /**
160     * Get node number (level="single"). If the current node matches the supplied pattern, the returned
161     * number is one plus the number of previous siblings that match the pattern. Otherwise,
162     * return the element number of the nearest ancestor that matches the supplied pattern.
163     * @param count Pattern that identifies which nodes should be counted. Default (null) is the element
164     * name if the current node is an element, or "node()" otherwise.
165     * @param from Pattern that specifies where counting starts from. Default (null) is the root node.
166     * (This parameter does not seem useful but is included for the sake of XSLT conformance.)
167     * @return the node number established as follows: go to the nearest ancestor-or-self that
168     * matches the 'count' pattern and that is a descendant of the nearest ancestor that matches the
169     * 'from' pattern. Return one plus the nunber of preceding siblings of that ancestor that match
170     * the 'count' pattern. If there is no such ancestor, return 0.
171     */

172
173     public static int getNumberSingle(NodeInfo node, Pattern count,
174                     Pattern from, Context context) throws XPathException {
175
176         checkNumberable(node); // TODO: check which nodes aren't numberable?!
177

178         if (count==null && from==null) {
179             return getNumberSimple(node, context);
180         }
181         
182         boolean knownToMatch = false;
183         if (count==null) {
184             if (node.getFingerprint()==-1) { // unnamed node
185
count = new NodeTypeTest(node.getNodeType());
186             } else {
187                 count = new NameTest(node);
188             }
189             knownToMatch = true;
190         }
191
192         NodeInfo target = node;
193         while (!(knownToMatch || count.matches(target, context))) {
194             target = target.getParent();
195             if (target==null) {
196                 return 0;
197             }
198             if (from!=null && from.matches(target, context)) {
199                 return 0;
200             }
201         }
202         
203         // we've found the ancestor to count from
204

205         NodeEnumeration preceding =
206             target.getEnumeration(Axis.PRECEDING_SIBLING, AnyNodeTest.getInstance());
207         int i = 1;
208         while (preceding.hasMoreElements()) {
209             NodeInfo p = preceding.nextElement();
210             if (count.matches(p, context)) {
211                 i++;
212             }
213         }
214         return i;
215     }
216
217     /**
218     * Get node number (level="any").
219     * Return one plus the number of previous nodes in the
220     * document that match the supplied pattern
221     * @param count Pattern that identifies which nodes should be counted. Default (null) is the element
222     * name if the current node is an element, or "node()" otherwise.
223     * @param from Pattern that specifies where counting starts from. Default (null) is the root node.
224     * Only nodes after the first (most recent) node that matches the 'from' pattern are counted.
225     * @return one plus the number of nodes that precede the current node, that match the count pattern,
226     * and that follow the first node that matches the from pattern if specified.
227     */

228
229     public static int getNumberAny(NodeInfo node, Pattern count,
230                     Pattern from, Context context) throws XPathException {
231
232         //checkNumberable(node);
233

234         int num = 0;
235         if (count==null) {
236             if (node.getFingerprint()==-1) { // unnamed node
237
count = new NodeTypeTest(node.getNodeType());
238             } else {
239                 count = new NameTest(node);
240             }
241             num = 1;
242         } else if (count.matches(node, context)) {
243             num = 1;
244         }
245         
246         // We use a special axis invented for the purpose: the union of the preceding and
247
// ancestor axes, but in reverse document order
248

249         NodeEnumeration preceding =
250             node.getEnumeration(Axis.PRECEDING_OR_ANCESTOR, AnyNodeTest.getInstance());
251         
252         while (preceding.hasMoreElements()) {
253             NodeInfo prev = preceding.nextElement();
254             if (from!=null && from.matches(prev, context)) {
255                 return num;
256             }
257             if (count.matches(prev, context)) {
258                 num++;
259             }
260         }
261         return num;
262     }
263
264     /**
265     * Get node number (level="multiple").
266     * Return a vector giving the hierarchic position of this node. See the XSLT spec for details.
267     * @param count Pattern that identifies which nodes (ancestors and their previous siblings)
268     * should be counted. Default (null) is the element
269     * name if the current node is an element, or "node()" otherwise.
270     * @param from Pattern that specifies where counting starts from. Default (null) is the root node.
271     * Only nodes below the first (most recent) node that matches the 'from' pattern are counted.
272     * @return a vector containing for each ancestor-or-self that matches the count pattern and that
273     * is below the nearest node that matches the from pattern, an Integer which is one greater than
274     * the number of previous siblings that match the count pattern.
275     */

276
277     public static Vector JavaDoc getNumberMulti(NodeInfo node, Pattern count,
278                     Pattern from, Context context) throws XPathException {
279
280         //checkNumberable(node);
281

282         Vector JavaDoc v = new Vector JavaDoc();
283
284         if (count==null) {
285             if (node.getFingerprint()==-1) { // unnamed node
286
count = new NodeTypeTest(node.getNodeType());
287             } else {
288                 count = new NameTest(node);
289             }
290         }
291
292         NodeInfo curr = node;
293
294         while(true) {
295             if (count.matches(curr, context)) {
296                 int num = getNumberSingle(curr, count, null, context);
297                 v.insertElementAt(new Integer JavaDoc(num), 0);
298             }
299             curr = curr.getParent();
300             if (curr==null) break;
301             if (from!=null && from.matches(curr, context)) break;
302         }
303
304         return v;
305     }
306
307     private static void checkNumberable(NodeInfo node) throws XPathException {
308         short type = node.getNodeType();
309         if (type == NodeInfo.ATTRIBUTE) {
310             throw new XPathException("Attribute nodes cannot be numbered");
311         }
312         if (type == NodeInfo.NAMESPACE) {
313             throw new XPathException("Namespace nodes cannot be numbered");
314         }
315     }
316
317 }
318
319 //
320
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
321
// you may not use this file except in compliance with the License. You may obtain a copy of the
322
// License at http://www.mozilla.org/MPL/
323
//
324
// Software distributed under the License is distributed on an "AS IS" basis,
325
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
326
// See the License for the specific language governing rights and limitations under the License.
327
//
328
// The Original Code is: all this file.
329
//
330
// The Initial Developer of the Original Code is
331
// Michael Kay of International Computers Limited (mhkay@iclway.co.uk).
332
//
333
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
334
//
335
// Contributor(s): none.
336
//
337
Popular Tags