View Javadoc
1   /*
2    * Copyright (c) 2012-2022, jcabi.com
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the jcabi.com nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package com.jcabi.xml;
31  
32  import java.util.List;
33  import javax.xml.namespace.NamespaceContext;
34  import org.w3c.dom.Node;
35  
36  /**
37   * XML document.
38   *
39   * <p>Set of convenient XML manipulations:
40   *
41   * <pre> XML xml = new XMLDocument(content);
42   * for (XML employee : xml.nodes("//Employee")) {
43   *   String name = employee.xpath("name/text()").get(0);
44   *   // ...
45   * }</pre>
46   *
47   * <p>You can always get DOM node out of this abstraction using {@link #node()}
48   * method.
49   *
50   * <p>{@code toString()} must produce a full XML.
51   *
52   * <p>Implementation of this interface must be immutable and thread-safe.
53   *
54   * <p> In most cases, you can use the {@link XMLDocument} implementation. It
55   * implements all required features and will be sufficient for most practical tasks.
56   * The only problem with that implementation is that it uses javax.xml classes under
57   * the hood. The issue with the default java implementation is that it only supports
58   * XPath 1.0. If you require XPath 2.0 support and beyond, you can use the Saxon
59   * implementation of {@link XML} - {@link SaxonDocument}. It is based on the Saxon
60   * library and supports XPath 2.0 and higher.
61   * You can read more about Java XPath versioning problems in the following threads:
62   * <ul>
63   *   <li><a href="https://stackoverflow.com/questions/6624149/xpath-2-0-for-java-possible">xpath 2.0 for java possible</a></li>
64   *   <li><a href="https://stackoverflow.com/questions/5802895/does-jdk-6-support-all-features-of-xpath-2-0/5803028#5803028">does JDK 6 support all features of XPath 2.0?</a></li>
65   * </ul>
66   *
67   * @see XMLDocument
68   * @since 0.1
69   * @checkstyle AbbreviationAsWordInNameCheck (5 lines)
70   */
71  public interface XML {
72  
73      /**
74       * Find and return text elements or attributes matched by XPath address.
75       *
76       * <p>The XPath query should point to text elements or attributes in the
77       * XML document. If any nodes of different types (elements, comments, etc.)
78       * are found in result node list -
79       * a {@link RuntimeException} will be thrown.
80       *
81       * <p>Alternatively, the XPath query can be a function or expression that
82       * returns a single value instead of pointing to a set of nodes. In this
83       * case, the result will be a List containing a single String, the content
84       * of which is the result of the evaluation. If the expression result is not
85       * a String, it will be converted to a String representation and returned as
86       * such. For example, a document containing three &lt;a&gt; elements,
87       * the input query "count(//a)", will return a singleton List with a single
88       * string value "3".
89       *
90       * <p>This is a convenient method, which is used (according to our
91       * experience) in 95% of all cases. Usually you don't need to get anything
92       * else but a text value of some node or an attribute. And in most cases
93       * you are interested to get just the first value
94       * (use {@code xpath(..).get(0)}). But when/if you need to get more than
95       * just a plain text - use {@link #nodes(String)}.
96       *
97       * <p>The {@link List} returned will throw {@link IndexOutOfBoundsException}
98       * if you try to access a node which wasn't found by this XPath query.
99       *
100      * <p>An {@link IllegalArgumentException} is thrown if the parameter
101      * passed is not a valid XPath expression.
102      *
103      * @param query The XPath query
104      * @return The list of string values (texts) or single function result
105      */
106     List<String> xpath(String query);
107 
108     /**
109      * Retrieve DOM nodes from the XML response.
110      *
111      * <p>The {@link List} returned will throw {@link IndexOutOfBoundsException}
112      * if you try to access a node which wasn't found by this XPath query.
113      *
114      * <p>An {@link IllegalArgumentException} is thrown if the parameter
115      * passed is not a valid XPath expression.
116      *
117      * @param query The XPath query
118      * @return Collection of DOM nodes
119      */
120     List<XML> nodes(String query);
121 
122     /**
123      * Register additional namespace prefix for XPath.
124      *
125      * <p>For example:
126      *
127      * <pre>
128      * String name = new XMLDocument("...")
129      *   .registerNs("ns1", "http://example.com")
130      *   .registerNs("foo", "http://example.com/foo")
131      *   .xpath("/ns1:root/foo:name/text()")
132      *   .get(0);
133      * </pre>
134      *
135      * <p>A number of standard namespaces are registered by default in
136      * instances of XML. Their
137      * full list is in {@link XMLDocument#XMLDocument(String)}.
138      *
139      * <p>If a namespace prefix is already registered an
140      * {@link IllegalArgumentException} will be thrown.
141      *
142      * @param prefix The XPath prefix to register
143      * @param uri Namespace URI
144      * @return A new XML document, with this additional namespace registered
145      */
146     XML registerNs(String prefix, Object uri);
147 
148     /**
149      * Append this namespace context to the existing one.
150      *
151      * <p>The existing context (inside this object) and the new one provided
152      * will be merged together. The existing context will be have higher
153      * priority.
154      *
155      * @param context The context to append
156      * @return A new XML document, with a merged context on board
157      */
158     XML merge(NamespaceContext context);
159 
160     /**
161      * Retrieve DOM node, represented by this wrapper.
162      * @return DOM node
163      */
164     Node node();
165 
166 }