View Javadoc
1   package org.jaxen.jdom;
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.HashMap;
52  import java.util.Iterator;
53  import java.util.List;
54  import java.util.Map;
55  
56  import org.jaxen.DefaultNavigator;
57  import org.jaxen.FunctionCallException;
58  import org.jaxen.NamedAccessNavigator;
59  import org.jaxen.Navigator;
60  import org.jaxen.XPath;
61  import org.jaxen.JaxenConstants;
62  import org.jaxen.saxpath.SAXPathException;
63  import org.jaxen.util.SingleObjectIterator;
64  import org.jdom.Attribute;
65  import org.jdom.CDATA;
66  import org.jdom.Comment;
67  import org.jdom.Document;
68  import org.jdom.Element;
69  import org.jdom.Namespace;
70  import org.jdom.ProcessingInstruction;
71  import org.jdom.Text;
72  import org.jdom.input.SAXBuilder;
73  
74  /** 
75   * Interface for navigating around the JDOM object model.
76   *
77   * <p>
78   * This class is not intended for direct usage, but is
79   * used by the Jaxen engine during evaluation.
80   * </p>
81   *
82   * @see XPath
83   *
84   * @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
85   * @author Stephen Colebourne
86   */
87  public class DocumentNavigator extends DefaultNavigator implements NamedAccessNavigator
88  {
89      /**
90       * 
91       */
92      private static final long serialVersionUID = -1636727587303584165L;
93  
94      /** Singleton implementation.
95       */
96      private static class Singleton
97      {
98          /** Singleton instance.
99           */
100         private static DocumentNavigator instance = new DocumentNavigator();
101     }
102 
103     public static Navigator getInstance()
104     {
105         return Singleton.instance;
106     }
107 
108     public boolean isElement(Object obj)
109     {
110         return obj instanceof Element;
111     }
112 
113     public boolean isComment(Object obj)
114     {
115         return obj instanceof Comment;
116     }
117 
118     public boolean isText(Object obj)
119     {
120         return ( obj instanceof Text
121                  ||
122                  obj instanceof CDATA );
123     }
124 
125     public boolean isAttribute(Object obj)
126     {
127         return obj instanceof Attribute;
128     }
129 
130     public boolean isProcessingInstruction(Object obj)
131     {
132         return obj instanceof ProcessingInstruction;
133     }
134 
135     public boolean isDocument(Object obj)
136     {
137         return obj instanceof Document;
138     }
139 
140     public boolean isNamespace(Object obj)
141     {
142         return obj instanceof Namespace || obj instanceof XPathNamespace;
143     }
144 
145     public String getElementName(Object obj)
146     {
147         Element elem = (Element) obj;
148 
149         return elem.getName();
150     }
151 
152     public String getElementNamespaceUri(Object obj)
153     {
154         Element elem = (Element) obj;
155         
156         String uri = elem.getNamespaceURI();
157         if ( uri != null && uri.length() == 0 ) 
158             return null;
159         else
160             return uri;
161     }
162 
163     public String getAttributeName(Object obj)
164     {
165         Attribute attr = (Attribute) obj;
166 
167         return attr.getName();
168     }
169 
170     public String getAttributeNamespaceUri(Object obj)
171     {
172         Attribute attr = (Attribute) obj;
173 
174         String uri = attr.getNamespaceURI();
175         if ( uri != null && uri.length() == 0 ) 
176             return null;
177         else
178             return uri;
179     }
180 
181     public Iterator getChildAxisIterator(Object contextNode)
182     {
183         if ( contextNode instanceof Element )
184         {
185             return ((Element)contextNode).getContent().iterator();
186         }
187         else if ( contextNode instanceof Document )
188         {
189             return ((Document)contextNode).getContent().iterator();
190         }
191 
192         return JaxenConstants.EMPTY_ITERATOR;
193     }
194 
195     /**
196      * Retrieves an <code>Iterator</code> over the child elements that
197      * match the supplied local name and namespace URI.
198      *
199      * @param contextNode      the origin context node
200      * @param localName        the local name of the children to return, always present
201      * @param namespacePrefix  ignored; prefixes are not used when matching in XPath
202      * @param namespaceURI     the URI of the namespace of the children to return
203      * @return an Iterator     that traverses the named children, or null if none
204      */
205     public Iterator getChildAxisIterator(
206             Object contextNode, String localName, String namespacePrefix, String namespaceURI) {
207 
208         if ( contextNode instanceof Element ) {
209             Element node = (Element) contextNode;
210             if (namespaceURI == null) {
211                 return node.getChildren(localName).iterator();
212             }
213             return node.getChildren(localName, Namespace.getNamespace(namespacePrefix, namespaceURI)).iterator();
214         }
215         if ( contextNode instanceof Document ) {
216             Document node = (Document) contextNode;
217             
218             Element el = node.getRootElement();
219             if (el.getName().equals(localName) == false) {
220                 return JaxenConstants.EMPTY_ITERATOR;
221             }
222             if (namespaceURI != null) {
223                 // JDOM's equals method does not consider the prefix when comparing namespace objects
224                 if (!Namespace.getNamespace(namespacePrefix, namespaceURI).equals(el.getNamespace())) {
225                     return JaxenConstants.EMPTY_ITERATOR;
226                 }
227             }
228             else if(el.getNamespace() != Namespace.NO_NAMESPACE) { 
229                 return JaxenConstants.EMPTY_ITERATOR; 
230             }
231             
232             return new SingleObjectIterator(el);
233         }
234 
235         return JaxenConstants.EMPTY_ITERATOR;
236     }
237     
238     public Iterator getNamespaceAxisIterator(Object contextNode)
239     {
240         if ( ! ( contextNode instanceof Element ) )
241         {
242             return JaxenConstants.EMPTY_ITERATOR;
243         }
244 
245         Element elem = (Element) contextNode;
246 
247         Map nsMap = new HashMap();
248 
249         Element current = elem;
250 
251         while ( current != null ) {
252         
253             Namespace ns = current.getNamespace();
254             
255             if ( ns != Namespace.NO_NAMESPACE ) {
256                 if ( !nsMap.containsKey(ns.getPrefix()) )
257                     nsMap.put( ns.getPrefix(), new XPathNamespace(elem, ns) );
258             }
259         
260             Iterator additional = current.getAdditionalNamespaces().iterator();
261 
262             while ( additional.hasNext() ) {
263 
264                 ns = (Namespace)additional.next();
265                 if ( !nsMap.containsKey(ns.getPrefix()) )
266                     nsMap.put( ns.getPrefix(), new XPathNamespace(elem, ns) );
267             }
268 
269             Iterator attributes = current.getAttributes().iterator();
270 
271             while ( attributes.hasNext() ) {
272 
273                 Attribute attribute = (Attribute)attributes.next();
274 
275                 Namespace attrNS = attribute.getNamespace();
276             
277                 if ( attrNS != Namespace.NO_NAMESPACE ) {
278                     if ( !nsMap.containsKey(attrNS.getPrefix()) )
279                         nsMap.put( attrNS.getPrefix(), new XPathNamespace(elem, attrNS) );
280                 }
281             }
282 
283             if (current.getParent() instanceof Element) {
284                 current = (Element)current.getParent();
285             } else {
286                 current = null;
287             }
288         }
289 
290         nsMap.put( "xml", new XPathNamespace(elem, Namespace.XML_NAMESPACE) );
291 
292         return nsMap.values().iterator();
293     }
294 
295     public Iterator getParentAxisIterator(Object contextNode)
296     {
297         Object parent = null;
298 
299         if ( contextNode instanceof Document )
300         {
301             return JaxenConstants.EMPTY_ITERATOR;
302         }
303         else if ( contextNode instanceof Element )
304         {
305             parent = ((Element)contextNode).getParent();
306 
307             if ( parent == null )
308             {
309                 if ( ((Element)contextNode).isRootElement() )
310                 {
311                     parent = ((Element)contextNode).getDocument();
312                 }
313             }
314         }
315         else if ( contextNode instanceof Attribute )
316         {
317             parent = ((Attribute)contextNode).getParent();
318         }
319         else if ( contextNode instanceof XPathNamespace )
320         {
321             parent = ((XPathNamespace)contextNode).getJDOMElement();
322         }
323         else if ( contextNode instanceof ProcessingInstruction )
324         {
325             parent = ((ProcessingInstruction)contextNode).getParent();
326         }
327         else if ( contextNode instanceof Comment )
328         {
329             parent = ((Comment)contextNode).getParent();
330         }
331         else if ( contextNode instanceof Text )
332         {
333             parent = ((Text)contextNode).getParent();
334         }
335         
336         if ( parent != null )
337         {
338             return new SingleObjectIterator( parent );
339         }
340 
341         return JaxenConstants.EMPTY_ITERATOR;
342     }
343 
344     public Iterator getAttributeAxisIterator(Object contextNode)
345     {
346         if ( ! ( contextNode instanceof Element ) )
347         {
348             return JaxenConstants.EMPTY_ITERATOR;
349         }
350 
351         Element elem = (Element) contextNode;
352 
353         return elem.getAttributes().iterator();
354     }
355 
356     /**
357      * Retrieves an <code>Iterator</code> over the attribute elements that
358      * match the supplied name.
359      *
360      * @param contextNode      the origin context node
361      * @param localName        the local name of the attributes to return, always present
362      * @param namespacePrefix  the prefix of the namespace of the attributes to return
363      * @param namespaceURI     the URI of the namespace of the attributes to return
364      * @return an Iterator     that traverses the named attributes, not null
365      */
366     public Iterator getAttributeAxisIterator(
367             Object contextNode, String localName, String namespacePrefix, String namespaceURI) {
368 
369         if ( contextNode instanceof Element ) {
370             Element node = (Element) contextNode;
371             Namespace namespace = (namespaceURI == null ? Namespace.NO_NAMESPACE : 
372                                     Namespace.getNamespace(namespacePrefix, namespaceURI));
373             Attribute attr = node.getAttribute(localName, namespace);
374             if (attr != null) {
375                 return new SingleObjectIterator(attr);
376             }
377         }
378         return JaxenConstants.EMPTY_ITERATOR;
379     }
380 
381     /** Returns a parsed form of the given XPath string, which will be suitable
382      *  for queries on JDOM documents.
383      */
384     public XPath parseXPath (String xpath) throws SAXPathException
385     {
386         return new JDOMXPath(xpath);
387     }
388 
389     public Object getDocumentNode(Object contextNode)
390     {
391         if ( contextNode instanceof Document )
392         {
393             return contextNode;
394         }
395 
396         Element elem = (Element) contextNode;
397 
398         return elem.getDocument();
399     }
400 
401     public String getElementQName(Object obj)
402     {
403         Element elem = (Element) obj;
404 
405         String prefix = elem.getNamespacePrefix();
406 
407         if ( prefix == null || prefix.length() == 0 )
408         {
409             return elem.getName();
410         }
411 
412         return prefix + ":" + elem.getName();
413     }
414 
415     public String getAttributeQName(Object obj)
416     {
417         Attribute attr = (Attribute) obj;
418 
419         String prefix = attr.getNamespacePrefix();
420 
421         if ( prefix == null || "".equals( prefix ) )
422         {
423             return attr.getName();
424         }
425 
426         return prefix + ":" + attr.getName();
427     }
428 
429     public String getNamespaceStringValue(Object obj)
430     {
431         if (obj instanceof Namespace) {
432 
433             Namespace ns = (Namespace) obj;
434             return ns.getURI();
435         } else {
436 
437             XPathNamespace ns = (XPathNamespace) obj;
438             return ns.getJDOMNamespace().getURI();
439         }
440         
441     }
442 
443     public String getNamespacePrefix(Object obj)
444     {
445         if (obj instanceof Namespace) {
446 
447             Namespace ns = (Namespace) obj;
448             return ns.getPrefix();
449         } else {
450 
451             XPathNamespace ns = (XPathNamespace) obj;
452             return ns.getJDOMNamespace().getPrefix();
453         }
454     }
455 
456     public String getTextStringValue(Object obj)
457     {
458         if ( obj instanceof Text )
459         {
460             return ((Text)obj).getText();
461         }
462 
463         if ( obj instanceof CDATA )
464         {
465             return ((CDATA)obj).getText();
466         }
467 
468         return "";
469     }
470 
471     public String getAttributeStringValue(Object obj)
472     {
473         Attribute attr = (Attribute) obj;
474 
475         return attr.getValue();
476     }
477 
478     public String getElementStringValue(Object obj)
479     {
480         Element elem = (Element) obj;
481 
482         StringBuffer buf = new StringBuffer();
483 
484         List     content     = elem.getContent();
485         Iterator contentIter = content.iterator();
486         Object   each        = null;
487 
488         while ( contentIter.hasNext() )
489         {
490             each = contentIter.next();
491 
492             if ( each instanceof Text )
493             {
494                 buf.append( ((Text)each).getText() );
495             }
496             else if ( each instanceof CDATA )
497             {
498                 buf.append( ((CDATA)each).getText() );
499             }
500             else if ( each instanceof Element )
501             {
502                 buf.append( getElementStringValue( each ) );
503             }
504         }
505 
506         return buf.toString();
507     }
508 
509     public String getProcessingInstructionTarget(Object obj)
510     {
511         ProcessingInstruction pi = (ProcessingInstruction) obj;
512 
513         return pi.getTarget();
514     }
515 
516     public String getProcessingInstructionData(Object obj)
517     {
518         ProcessingInstruction pi = (ProcessingInstruction) obj;
519 
520         return pi.getData();
521     }
522 
523     public String getCommentStringValue(Object obj)
524     {
525         Comment cmt = (Comment) obj;
526 
527         return cmt.getText();
528     }
529 
530     public String translateNamespacePrefixToUri(String prefix, Object context)
531     {
532         Element element = null;
533         if ( context instanceof Element ) 
534         {
535             element = (Element) context;
536         }
537         else if ( context instanceof Text )
538         {
539             element = (Element)((Text)context).getParent();
540         }
541         else if ( context instanceof Attribute )
542         {
543             element = ((Attribute)context).getParent();
544         }
545         else if ( context instanceof XPathNamespace )
546         {
547             element = ((XPathNamespace)context).getJDOMElement();
548         }
549         else if ( context instanceof Comment )
550         {
551             element = (Element)((Comment)context).getParent();
552         }
553         else if ( context instanceof ProcessingInstruction )
554         {
555             element = (Element)((ProcessingInstruction)context).getParent();
556         }
557 
558         if ( element != null )
559         {
560             Namespace namespace = element.getNamespace( prefix );
561 
562             if ( namespace != null ) 
563             {
564                 return namespace.getURI();
565             }
566         }
567         return null;
568     }
569 
570     public Object getDocument(String url) throws FunctionCallException
571     {
572         try
573         {
574             SAXBuilder builder = new SAXBuilder();
575             
576             return builder.build( url );
577         }
578         catch (Exception e)
579         {
580             throw new FunctionCallException( e.getMessage() );
581         }
582     }
583 }