KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > xdm > visitor > XPathFinder


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.xml.xdm.visitor;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collections JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.StringTokenizer JavaDoc;
28 import javax.xml.namespace.NamespaceContext JavaDoc;
29 import javax.xml.xpath.XPath JavaDoc;
30 import javax.xml.xpath.XPathConstants JavaDoc;
31 import javax.xml.xpath.XPathExpressionException JavaDoc;
32 import javax.xml.xpath.XPathFactory JavaDoc;
33 import org.netbeans.modules.xml.xdm.nodes.Attribute;
34 import org.netbeans.modules.xml.xdm.nodes.Document;
35 import org.netbeans.modules.xml.xdm.nodes.Element;
36 import org.netbeans.modules.xml.xdm.nodes.Node;
37 import org.w3c.dom.NodeList JavaDoc;
38
39 /**
40  * Finder for nodes from a document using arbitrary XPath expression.
41  *
42  * @author Nam Nguyen
43  */

44 public class XPathFinder extends ChildVisitor {
45     
46     /**
47      * @return an xpath that can be used to retrieve the given node using #findNode
48      * @param root root node
49      * @param target Element or Attribute node to generate the xpath for.
50      */

51     public static String JavaDoc getXpath(Document root, Node target) {
52         if (!(target instanceof Element) && !(target instanceof Attribute)) {
53             throw new IllegalArgumentException JavaDoc("Can only get XPath expression for Element or Attribute");
54         }
55         List JavaDoc<Node> result = new PathFromRootVisitor().findPath(root, target);
56         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(128);
57         Element parent = null;
58         for (int j=result.size()-1; j >= 0; j--) {
59             Node n = result.get(j);
60             boolean isElement = n instanceof Element;
61             boolean isAttribute = n instanceof Attribute;
62             if (! isElement && ! isAttribute) {
63                 continue;
64             }
65             sb.append(SEP);
66             int index = isElement ? xpathIndexOf(parent, (Element) n) : -1;
67             String JavaDoc prefix = n.getPrefix();
68             String JavaDoc namespace = n.lookupNamespaceURI(prefix);
69             if (isAttribute) {
70                 sb.append(AT);
71             }
72             if (prefix != null) {
73                 sb.append(prefix);
74                 sb.append(':');
75             }
76             sb.append(n.getLocalName());
77             if (isElement && index > 0) {
78                 sb.append(BRACKET0);
79                 sb.append(String.valueOf(index));
80                 sb.append(BRACKET1);
81             }
82             if (isAttribute) {
83                 break;
84             }
85             parent = (Element) n;
86         }
87         return sb.toString();
88     }
89     
90     /**
91      * @return the node correspond to specified xpath, if provided xpath result
92      * in more than one nodes, only the first on get returned.
93      * @param root context in which to find the node.
94      * @param xpath location path for the attribute or element.
95      */

96     public Node findNode(Document root, String JavaDoc xpath) {
97         List JavaDoc<Node> nodes = findNodes(root, xpath);
98         return nodes.size() > 0 ? nodes.get(0) : null;
99     }
100     
101     /**
102      * @return the nodes correspond to specified xpath.
103      * @param root context in which to find the node.
104      * @param xpath location path for the attribute or element.
105      */

106     public List JavaDoc<Node> findNodes(Document root, String JavaDoc xpathExpression) {
107         if (root == null || root.getDocumentElement() == null) {
108             return Collections.emptyList();
109         }
110         
111         init(root, xpathExpression);
112         if (! isReadyForEvaluation()) {
113             return new ArrayList JavaDoc<Node>();
114         }
115         
116         XPath JavaDoc xpath = XPathFactory.newInstance().newXPath();
117         xpath.setNamespaceContext(getNamespaceContext());
118         NodeList JavaDoc result = null;
119         try {
120             result = (NodeList JavaDoc) xpath.evaluate(getFixedUpXpath(), root, XPathConstants.NODESET);
121         } catch (XPathExpressionException JavaDoc e) {
122             throw new RuntimeException JavaDoc(e.getCause().getLocalizedMessage());
123         }
124         assert(result != null);
125         List JavaDoc<Node> ret = new ArrayList JavaDoc<Node>();
126         for (int i=0; i<result.getLength(); i++) {
127             ret.add((Node) result.item(i));
128         }
129         return ret;
130     }
131     
132     // 0-based index for xpath
133
private static int xpathIndexOf(Node parentNode, Element child) {
134         assert(child != null);
135         if (!(parentNode instanceof Element || parentNode instanceof Document)) {
136             return -1;
137         }
138
139         NodeList JavaDoc children = parentNode.getChildNodes();
140         int index = 0;
141         String JavaDoc namespace = child.getNamespaceURI();
142         String JavaDoc name = child.getLocalName();
143         for (int i = 0; i<children.getLength(); i++) {
144             if (! (children.item(i) instanceof Element)) {
145                 continue;
146             }
147             Element n = (Element) children.item(i);
148             if (namespace != null && ! namespace.equals(n.getNamespaceURI()) ||
149                 namespace == null && n.getNamespaceURI() != null)
150             {
151                 continue;
152             }
153             
154             if (name.equals(n.getLocalName())) {
155                 index++;
156             }
157             
158             if (n.getId() == child.getId()) {
159                 return index;
160             }
161         }
162         
163         return -1;
164     }
165     
166     private void init(Document root, String JavaDoc xpath) {
167         currentParent = root;
168         initTokens(xpath);
169         ((Element)root.getDocumentElement()).accept(this);
170     }
171     
172     private void initTokens(String JavaDoc xpath) {
173         tokens = new ArrayList JavaDoc<XPathSegment>();
174         StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(xpath, SEP);
175         while (tokenizer.hasMoreTokens()) {
176             String JavaDoc token = tokenizer.nextToken();
177             tokens.add(new XPathSegment(token));
178         }
179     }
180     
181     private static class XPathSegment {
182         private final String JavaDoc token;
183         private String JavaDoc prefix;
184         private String JavaDoc localPart;
185         int index = -1;
186         private String JavaDoc remaining;
187         private boolean isAttribute;
188         private boolean hasConditions;
189         
190         XPathSegment(String JavaDoc token) {
191             this.token = token;
192             parse();
193         }
194              
195         private void parse() {
196             StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(token, ":[");
197             List JavaDoc<String JavaDoc> parts = new ArrayList JavaDoc<String JavaDoc>();
198             while (tokenizer.hasMoreTokens()) {
199                 String JavaDoc t = tokenizer.nextToken();
200                 parts.add(t);
201             }
202             if (parts.size() == 1) {
203                 prefix = "";
204                 localPart = parts.get(0);
205                 remaining = "";
206             } else if (parts.size() == 3) {
207                 prefix = parts.get(0);
208                 localPart = parts.get(1);
209                 remaining = parts.get(2);
210             } else if (parts.size() == 2) {
211                 String JavaDoc part0 = parts.get(0);
212                 if (token.charAt(part0.length()) == ':') {
213                     prefix = part0;
214                     localPart = parts.get(1);
215                     remaining = "";
216                 } else {
217                     localPart = parts.get(0);
218                     remaining = parts.get(1);
219                     prefix = "";
220                 }
221             }
222             if (prefix != null && prefix.startsWith(AT)) {
223                 isAttribute = true;
224                 prefix = prefix.substring(1);
225             } else if (localPart.startsWith(AT)) {
226                 isAttribute = true;
227                 localPart = localPart.substring(1);
228             }
229             if (! remaining.equals("")) {
230                 String JavaDoc indexString = remaining.substring(0, remaining.length()-1);
231                 try {
232                     index = Integer.parseInt(indexString);
233                 } catch (NumberFormatException JavaDoc ex) {
234                     hasConditions = true;
235                 }
236                 remaining = BRACKET0 + remaining;
237             }
238         }
239         
240         public boolean checkTypeAndName(Node e, Node parent) {
241             if (e instanceof Element) {
242                 if (isAttribute()) return false;
243                 if (hasPlainIndex() && getIndex() != xpathIndexOf(parent, (Element) e)) {
244                     return false;
245                 }
246             } else if (e instanceof Attribute) {
247                 if (! isAttribute()) return false;
248             } else {
249                 return false;
250             }
251             if (! e.getLocalName().equals(getLocalPart())){
252                 return false;
253             }
254             return true;
255         }
256              
257         public void addToXpath(StringBuffer JavaDoc xpath, String JavaDoc prefix) {
258             xpath.append(SEP);
259             if (isAttribute()) {
260                 xpath.append(AT);
261             }
262             xpath.append(prefix);
263             xpath.append(COLON);
264             xpath.append(localPart);
265             if (hasConditions() && !prefix.startsWith(XPNS)) {
266                 String JavaDoc prefixing = AT + prefix + COLON;
267                 //TODO: a better RE to skip those already prefixed
268
xpath.append(remaining.replaceAll(AT, prefixing));
269             } else {
270                 xpath.append(remaining);
271             }
272         }
273              
274         public void addToXpath(StringBuffer JavaDoc xpath) {
275             xpath.append(SEP);
276             xpath.append(token);
277         }
278              
279         public String JavaDoc getPrefix() { return prefix; }
280         public String JavaDoc getLocalPart() { return localPart; }
281         public boolean hasPlainIndex() { return index > -1; }
282         public boolean hasConditions() { return hasConditions; }
283         public int getIndex() { return index; }
284         public String JavaDoc getRemaining() { return remaining; }
285         public String JavaDoc getToken() { return token; }
286         public boolean isAttribute() { return isAttribute; }
287         public String JavaDoc toString() { return token; }
288     }
289              
290     protected void visitNode(Node e) {
291         if (tokens.size() == 0) {
292             done = true;
293             return;
294         }
295              
296         XPathSegment segment = tokens.get(0);
297         if (! segment.checkTypeAndName(e, currentParent)) {
298             return; // not matched
299
}
300              
301         String JavaDoc currentNamespace = e.getNamespaceURI();
302         if (currentNamespace != null) {
303             String JavaDoc prefix = segment.getPrefix();
304             if (prefix.length() > 0) {
305                 String JavaDoc namespace = e.lookupNamespaceURI(prefix);
306                 if (namespace == null) {
307                     namespace = namespaces.get(prefix);
308                 } if (namespace != null && ! currentNamespace.equals(namespace)) {
309                     return; // not matched
310
}
311                 segment.addToXpath(fixedUpXpath);
312             } else {
313                 // no prefix, make up one
314
prefix = prefixes.get(currentNamespace);
315                 if (prefix == null) {
316                     prefix = XPNS + String.valueOf(countNamespaceSuffix++);
317                 }
318                 segment.addToXpath(fixedUpXpath, prefix);
319             }
320             // push current namespace context
321
prefixes.put(currentNamespace, prefix);
322             namespaces.put(prefix, currentNamespace);
323         } else {
324             segment.addToXpath(fixedUpXpath);
325         }
326              
327         tokens.remove(0);
328         currentParent = e;
329         super.visitNode(e);
330     }
331              
332     public boolean isReadyForEvaluation() {
333         return done;
334     }
335     public NamespaceContext JavaDoc getNamespaceContext() {
336         return new HashNamespaceResolver(namespaces, prefixes);
337     }
338     private String JavaDoc getFixedUpXpath() {
339         return fixedUpXpath.toString();
340     }
341              
342     private boolean done = false;
343     private Node currentParent = null;
344     private Map JavaDoc<String JavaDoc, String JavaDoc> namespaces = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
345     private Map JavaDoc<String JavaDoc, String JavaDoc> prefixes = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
346     private List JavaDoc<XPathSegment> tokens;
347     private StringBuffer JavaDoc fixedUpXpath = new StringBuffer JavaDoc();
348              
349     public static final String JavaDoc SEP = "/"; //NOI18N
350
public static final String JavaDoc AT = "@"; //NOI18N
351
public static final String JavaDoc COLON = ":"; //NOI18N
352
public static final String JavaDoc BRACKET0 = "["; //NOI18N
353
public static final String JavaDoc BRACKET1 = "]"; //NOI18N
354
public static final String JavaDoc XPNS = "xpns"; //NOI18N
355
private int countNamespaceSuffix = 0;
356 }
357
Popular Tags