View Javadoc
1   package org.jaxen.dom4j;
2   
3   /*
4    * $Header$
5    * $Revision$
6    * $Date$
7    *
8    * ====================================================================
9    *
10   * Copyright 2000-2005 bob mcwhirter & James Strachan.
11   * All rights reserved.
12   *
13   *
14   * Redistribution and use in source and binary forms, with or without
15   * modification, are permitted provided that the following conditions are
16   * met:
17   * 
18   *   * Redistributions of source code must retain the above copyright
19   *     notice, this list of conditions and the following disclaimer.
20   * 
21   *   * Redistributions in binary form must reproduce the above copyright
22   *     notice, this list of conditions and the following disclaimer in the
23   *     documentation and/or other materials provided with the distribution.
24   * 
25   *   * Neither the name of the Jaxen Project nor the names of its
26   *     contributors may be used to endorse or promote products derived 
27   *     from this software without specific prior written permission.
28   * 
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
30   * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31   * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
32   * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
33   * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
34   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * ====================================================================
42   * This software consists of voluntary contributions made by many
43   * individuals on behalf of the Jaxen Project and was originally
44   * created by bob mcwhirter <bob@werken.com> and
45   * James Strachan <jstrachan@apache.org>.  For more information on the
46   * Jaxen Project, please see <http://www.jaxen.org/>.
47   *
48   * $Id$
49  */
50  
51  import java.util.ArrayList;
52  import java.util.HashSet;
53  import java.util.Iterator;
54  import java.util.List;
55  
56  import org.dom4j.Attribute;
57  import org.dom4j.Branch;
58  import org.dom4j.CDATA;
59  import org.dom4j.Comment;
60  import org.dom4j.Document;
61  import org.dom4j.DocumentException;
62  import org.dom4j.Element;
63  import org.dom4j.Namespace;
64  import org.dom4j.Node;
65  import org.dom4j.ProcessingInstruction;
66  import org.dom4j.QName;
67  import org.dom4j.Text;
68  import org.dom4j.io.SAXReader;
69  import org.jaxen.DefaultNavigator;
70  import org.jaxen.FunctionCallException;
71  import org.jaxen.NamedAccessNavigator;
72  import org.jaxen.Navigator;
73  import org.jaxen.XPath;
74  import org.jaxen.JaxenConstants;
75  import org.jaxen.saxpath.SAXPathException;
76  import org.jaxen.util.SingleObjectIterator;
77  
78  /** 
79   * Interface for navigating around the DOM4J object model.
80   *
81   * <p>
82   * This class is not intended for direct usage, but is
83   * used by the Jaxen engine during evaluation.
84   * </p>
85   *
86   * @see XPath
87   *
88   * @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
89   * @author Stephen Colebourne
90   */
91  public class DocumentNavigator extends DefaultNavigator implements NamedAccessNavigator
92  {
93      
94  
95      private static final long serialVersionUID = 5582300797286535936L;
96      private transient SAXReader reader;
97  
98      /** Singleton implementation.
99       */
100     private static class Singleton
101     {
102         /** Singleton instance.
103          */
104         private static DocumentNavigator instance = new DocumentNavigator();
105     }
106 
107     /** Retrieve the singleton instance of this <code>DocumentNavigator</code>.
108      */
109     public static Navigator getInstance()
110     {
111         return Singleton.instance;
112     }
113 
114     public boolean isElement(Object obj)
115     {
116         return obj instanceof Element;
117     }
118 
119     public boolean isComment(Object obj)
120     {
121         return obj instanceof Comment;
122     }
123 
124     public boolean isText(Object obj)
125     {
126         return ( obj instanceof Text 
127                  ||
128                  obj instanceof CDATA );
129     }
130 
131     public boolean isAttribute(Object obj)
132     {
133         return obj instanceof Attribute;
134     }
135 
136     public boolean isProcessingInstruction(Object obj)
137     {
138         return obj instanceof ProcessingInstruction;
139     }
140 
141     public boolean isDocument(Object obj)
142     {
143         return obj instanceof Document;
144     }
145 
146     public boolean isNamespace(Object obj)
147     {
148         return obj instanceof Namespace;
149     }
150 
151     public String getElementName(Object obj)
152     {
153         Element elem = (Element) obj;
154 
155         return elem.getName();
156     }
157 
158     public String getElementNamespaceUri(Object obj)
159     {
160         Element elem = (Element) obj;
161         
162         String uri = elem.getNamespaceURI();
163         if ( uri == null)
164             return "";
165         else
166             return uri;
167     }
168 
169     public String getElementQName(Object obj)
170     {
171         Element elem = (Element) obj;
172 
173         return elem.getQualifiedName();
174     }
175 
176     public String getAttributeName(Object obj)
177     {
178         Attribute attr = (Attribute) obj;
179 
180         return attr.getName();
181     }
182 
183     public String getAttributeNamespaceUri(Object obj)
184     {
185         Attribute attr = (Attribute) obj;
186 
187         String uri = attr.getNamespaceURI();
188         if ( uri == null)
189             return "";
190         else
191             return uri;
192     }
193 
194     public String getAttributeQName(Object obj)
195     {
196         Attribute attr = (Attribute) obj;
197 
198         return attr.getQualifiedName();
199     }
200 
201     public Iterator getChildAxisIterator(Object contextNode)
202     {
203         Iterator result = null;
204         if ( contextNode instanceof Branch )
205         {
206             Branch node = (Branch) contextNode;
207             result = node.nodeIterator();
208         }
209         if (result != null) {
210             return result;
211         }
212         return JaxenConstants.EMPTY_ITERATOR;
213     }
214 
215     /**
216      * Retrieves an <code>Iterator</code> over the child elements that
217      * match the supplied name.
218      *
219      * @param contextNode      the origin context node
220      * @param localName        the local name of the children to return, always present
221      * @param namespacePrefix  the prefix of the namespace of the children to return
222      * @param namespaceURI     the uri of the namespace of the children to return
223      * 
224      * @return an Iterator that traverses the named children, or null if none
225      */
226     public Iterator getChildAxisIterator(
227             Object contextNode, String localName, String namespacePrefix, String namespaceURI) {
228 
229         if ( contextNode instanceof Element ) {
230             Element node = (Element) contextNode;
231             return node.elementIterator(QName.get(localName, namespacePrefix, namespaceURI));
232         }
233         if ( contextNode instanceof Document ) {
234             Document node = (Document) contextNode;
235             Element el = node.getRootElement();
236             if (el == null || el.getName().equals(localName) == false) {
237                 return JaxenConstants.EMPTY_ITERATOR;
238             }
239             if (namespaceURI != null) {
240                 if (namespaceURI.equals(el.getNamespaceURI()) == false) {
241                     return JaxenConstants.EMPTY_ITERATOR;
242                 }
243             }
244             return new SingleObjectIterator(el);
245         }
246 
247         return JaxenConstants.EMPTY_ITERATOR;
248     }
249 
250     public Iterator getParentAxisIterator(Object contextNode)
251     {
252         if ( contextNode instanceof Document )
253         {
254             return JaxenConstants.EMPTY_ITERATOR;
255         }
256 
257         Node node = (Node) contextNode;
258 
259         Object parent = node.getParent();
260 
261         if ( parent == null )
262         {
263             parent = node.getDocument();
264         }
265         
266         return new SingleObjectIterator( parent );
267     }
268 
269     public Iterator getAttributeAxisIterator(Object contextNode)
270     {
271         if ( ! ( contextNode instanceof Element ) )
272         {
273             return JaxenConstants.EMPTY_ITERATOR;
274         }
275 
276         Element elem = (Element) contextNode;
277 
278         return elem.attributeIterator();
279     }
280 
281     /**
282      * Retrieves an <code>Iterator</code> over the attribute elements that
283      * match the supplied name.
284      *
285      * @param contextNode  the origin context node
286      * @param localName  the local name of the attributes to return, always present
287      * @param namespacePrefix  the prefix of the namespace of the attributes to return
288      * @param namespaceURI  the URI of the namespace of the attributes to return
289      * @return an Iterator that traverses the named attributes, not null
290      */
291     public Iterator getAttributeAxisIterator(
292             Object contextNode, String localName, String namespacePrefix, String namespaceURI) {
293 
294         if ( contextNode instanceof Element ) {
295             Element node = (Element) contextNode;
296             Attribute attr = node.attribute(QName.get(localName, namespacePrefix, namespaceURI));
297             if (attr == null) {
298                 return JaxenConstants.EMPTY_ITERATOR;
299             }
300             return new SingleObjectIterator(attr);
301         }
302         return JaxenConstants.EMPTY_ITERATOR;
303     }
304         
305     public Iterator getNamespaceAxisIterator(Object contextNode)
306     {
307         if ( ! ( contextNode instanceof Element ) )
308         {
309             return JaxenConstants.EMPTY_ITERATOR;
310         }
311 
312         Element element = (Element) contextNode;
313         List nsList = new ArrayList();
314         HashSet prefixes = new HashSet();
315         for ( Element context = element; context != null; context = context.getParent() ) {
316             List declaredNS = new ArrayList(context.declaredNamespaces());
317             declaredNS.add(context.getNamespace());
318 
319             for ( Iterator iter = context.attributes().iterator(); iter.hasNext(); )
320             {
321                 Attribute attr = (Attribute) iter.next();
322                 declaredNS.add(attr.getNamespace());
323             }
324 
325             for ( Iterator iter = declaredNS.iterator(); iter.hasNext(); )
326             {
327                 Namespace namespace = (Namespace) iter.next();
328                 if (namespace != Namespace.NO_NAMESPACE)
329                 {
330                     String prefix = namespace.getPrefix();
331                     if ( ! prefixes.contains( prefix ) ) {
332                         prefixes.add( prefix );
333                         nsList.add( namespace.asXPathResult( element ) );
334                     }
335                 }
336             }
337         }
338         nsList.add( Namespace.XML_NAMESPACE.asXPathResult( element ) );
339         return nsList.iterator();
340     }
341 
342     public Object getDocumentNode(Object contextNode)
343     {
344         if ( contextNode instanceof Document ) 
345         {
346             return contextNode;
347         }
348         else if ( contextNode instanceof Node ) 
349         {
350             Node node = (Node) contextNode;
351             return node.getDocument();
352         }
353         return null;
354     }
355 
356     /** Returns a parsed form of the given XPath string, which will be suitable
357      *  for queries on DOM4J documents.
358      */
359     public XPath parseXPath (String xpath) throws SAXPathException
360     {
361         return new Dom4jXPath(xpath);
362     }
363 
364     public Object getParentNode(Object contextNode)
365     {
366         if ( contextNode instanceof Node ) 
367         {
368             Node node = (Node) contextNode;
369             Object answer = node.getParent();
370             if ( answer == null ) 
371             {
372                 answer = node.getDocument();
373                 if (answer == contextNode) {
374                     return null;
375                 }
376             }
377             return answer;            
378         }
379         return null;
380     }
381 
382     public String getTextStringValue(Object obj)
383     {
384         return getNodeStringValue( (Node) obj );
385     }
386 
387     public String getElementStringValue(Object obj)
388     {
389         return getNodeStringValue( (Node) obj );
390     }
391 
392     public String getAttributeStringValue(Object obj)
393     {
394         return getNodeStringValue( (Node) obj );
395     }
396 
397     private String getNodeStringValue(Node node)
398     {
399         return node.getStringValue();
400     }
401 
402     public String getNamespaceStringValue(Object obj)
403     {
404         Namespace ns = (Namespace) obj;
405 
406         return ns.getURI();
407     }
408 
409     public String getNamespacePrefix(Object obj)
410     {
411         Namespace ns = (Namespace) obj;
412 
413         return ns.getPrefix();
414     }
415 
416     public String getCommentStringValue(Object obj)
417     {
418         Comment cmt = (Comment) obj;
419 
420         return cmt.getText();
421     }
422     
423     public String translateNamespacePrefixToUri(String prefix, Object context)
424     {
425         Element element = null;
426         if ( context instanceof Element ) 
427         {
428             element = (Element) context;
429         }
430         else if ( context instanceof Node )
431         {
432             Node node = (Node) context;
433             element = node.getParent();
434         }
435         if ( element != null )
436         {
437             Namespace namespace = element.getNamespaceForPrefix( prefix );
438 
439             if ( namespace != null ) 
440             {
441                 return namespace.getURI();
442             }
443         }
444         return null;
445     }
446     
447     public short getNodeType(Object node) 
448     {
449         if ( node instanceof Node )
450         {
451             return ((Node) node).getNodeType();
452         }
453         return 0;
454     }
455     
456     public Object getDocument(String uri) throws FunctionCallException
457     {
458         try
459         {
460             return getSAXReader().read( uri );
461         }
462         catch (DocumentException e)
463         {
464             throw new FunctionCallException("Failed to parse document for URI: " + uri, e);
465         }
466     }
467 
468     public String getProcessingInstructionTarget(Object obj)
469     {
470         ProcessingInstruction pi = (ProcessingInstruction) obj;
471 
472         return pi.getTarget();
473     }
474 
475     public String getProcessingInstructionData(Object obj)
476     {
477         ProcessingInstruction pi = (ProcessingInstruction) obj;
478 
479         return pi.getText();
480     }
481     
482     // Properties
483     //-------------------------------------------------------------------------    
484     public SAXReader getSAXReader()
485     {
486         if ( reader == null ) 
487         {
488             reader = new SAXReader();
489             reader.setMergeAdjacentText( true );
490         }
491         return reader;
492     }
493     
494     public void setSAXReader(SAXReader reader)
495     {
496         this.reader = reader;
497     }
498     
499 }