1
2
3
4
5 package com.jcabi.xml;
6
7 import java.io.File;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.io.StringReader;
11 import java.net.URI;
12 import java.net.URL;
13 import java.nio.charset.StandardCharsets;
14 import java.nio.file.Path;
15 import java.util.Collection;
16 import java.util.List;
17 import java.util.stream.Collectors;
18 import javax.xml.namespace.NamespaceContext;
19 import javax.xml.transform.stream.StreamSource;
20 import net.sf.saxon.s9api.DocumentBuilder;
21 import net.sf.saxon.s9api.Processor;
22 import net.sf.saxon.s9api.SaxonApiException;
23 import net.sf.saxon.s9api.XPathCompiler;
24 import net.sf.saxon.s9api.XPathSelector;
25 import net.sf.saxon.s9api.XdmItem;
26 import net.sf.saxon.s9api.XdmNode;
27 import org.w3c.dom.Node;
28 import org.w3c.dom.ls.LSResourceResolver;
29 import org.xml.sax.SAXParseException;
30
31
32
33
34
35
36
37
38 @SuppressWarnings("PMD.TooManyMethods")
39 public final class SaxonDocument implements XML {
40
41
42
43
44 private static final Processor SAXON = new Processor(false);
45
46
47
48
49 private static final DocumentBuilder DOC_BUILDER = SaxonDocument.SAXON.newDocumentBuilder();
50
51
52
53
54 private static final XPathCompiler XPATH_COMPILER = SaxonDocument.SAXON.newXPathCompiler();
55
56
57
58
59 private static final String UNSUPPORTED =
60 "The %s method is not supported yet. You can use XMLDocument instead or if you need to use Saxon specific features, you can open an issue at https://github.com/jcabi/jcabi-xml";
61
62
63
64
65 private final XdmNode xdm;
66
67
68
69
70
71
72 public SaxonDocument(final String text) {
73 this(SaxonDocument.node(text));
74 }
75
76
77
78
79
80
81 public SaxonDocument(final byte[] data) {
82 this(SaxonDocument.node(new String(data, StandardCharsets.UTF_8)));
83 }
84
85
86
87
88
89
90 public SaxonDocument(final Path path) {
91 this(path.toFile());
92 }
93
94
95
96
97
98
99 public SaxonDocument(final File file) {
100 this(SaxonDocument.node(new StreamSource(file)));
101 }
102
103
104
105
106
107
108
109 public SaxonDocument(final URL url) throws IOException {
110 this(SaxonDocument.node(new TextResource(url).toString()));
111 }
112
113
114
115
116
117
118
119 public SaxonDocument(final URI uri) throws IOException {
120 this(SaxonDocument.node(new TextResource(uri).toString()));
121 }
122
123
124
125
126
127
128 public SaxonDocument(final InputStream stream) {
129 this(SaxonDocument.node(new StreamSource(stream)));
130 }
131
132
133
134
135
136
137 public SaxonDocument(final XdmNode xml) {
138 this.xdm = xml;
139 }
140
141 @Override
142 public List<String> xpath(final String query) {
143 try {
144 final XPathSelector selector = SaxonDocument.XPATH_COMPILER.compile(query).load();
145 selector.setContextItem(this.xdm);
146 return selector.evaluate()
147 .stream()
148 .map(XdmItem::getStringValue)
149 .collect(Collectors.toList());
150 } catch (final SaxonApiException exception) {
151 throw new IllegalArgumentException(
152 String.format("Can't evaluate the '%s' XPath query with Saxon API", query),
153 exception
154 );
155 }
156 }
157
158 @Override
159 public List<XML> nodes(final String query) {
160 throw new UnsupportedOperationException(
161 String.format(SaxonDocument.UNSUPPORTED, "nodes")
162 );
163 }
164
165 @Override
166 public XML registerNs(final String prefix, final Object uri) {
167 throw new UnsupportedOperationException(
168 String.format(SaxonDocument.UNSUPPORTED, "registerNs")
169 );
170 }
171
172 @Override
173 public XML merge(final NamespaceContext context) {
174 throw new UnsupportedOperationException(
175 String.format(SaxonDocument.UNSUPPORTED, "merge")
176 );
177 }
178
179
180
181
182
183
184
185 @Deprecated
186 public Node node() {
187 throw new UnsupportedOperationException(
188 String.format(SaxonDocument.UNSUPPORTED, "node")
189 );
190 }
191
192 @Override
193 public Node inner() {
194 throw new UnsupportedOperationException(
195 String.format(SaxonDocument.UNSUPPORTED, "inner")
196 );
197 }
198
199 @Override
200 public Node deepCopy() {
201 throw new UnsupportedOperationException(
202 String.format(SaxonDocument.UNSUPPORTED, "deepCopy")
203 );
204 }
205
206 @Override
207 public Collection<SAXParseException> validate(final LSResourceResolver resolver) {
208 throw new UnsupportedOperationException(
209 String.format(SaxonDocument.UNSUPPORTED, "validate")
210 );
211 }
212
213 @Override
214 public Collection<SAXParseException> validate(final XML xsd) {
215 throw new UnsupportedOperationException(
216 String.format(SaxonDocument.UNSUPPORTED, "validate")
217 );
218 }
219
220
221
222
223
224
225 private static XdmNode node(final String text) {
226 return SaxonDocument.node(new StreamSource(new StringReader(text)));
227 }
228
229
230
231
232
233
234 private static XdmNode node(final StreamSource source) {
235 try {
236 return SaxonDocument.DOC_BUILDER.build(source);
237 } catch (final SaxonApiException exception) {
238 throw new IllegalArgumentException(
239 String.format("SaxonDocument can't parse XML from source '%s'", source),
240 exception
241 );
242 }
243 }
244 }