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 @Override
180 @Deprecated
181 public Node node() {
182 throw new UnsupportedOperationException(
183 String.format(SaxonDocument.UNSUPPORTED, "node")
184 );
185 }
186
187 @Override
188 public Node inner() {
189 throw new UnsupportedOperationException(
190 String.format(SaxonDocument.UNSUPPORTED, "inner")
191 );
192 }
193
194 @Override
195 public Node deepCopy() {
196 throw new UnsupportedOperationException(
197 String.format(SaxonDocument.UNSUPPORTED, "deepCopy")
198 );
199 }
200
201 @Override
202 public Collection<SAXParseException> validate(final LSResourceResolver resolver) {
203 throw new UnsupportedOperationException(
204 String.format(SaxonDocument.UNSUPPORTED, "validate")
205 );
206 }
207
208 @Override
209 public Collection<SAXParseException> validate(final XML xsd) {
210 throw new UnsupportedOperationException(
211 String.format(SaxonDocument.UNSUPPORTED, "validate")
212 );
213 }
214
215
216
217
218
219
220 private static XdmNode node(final String text) {
221 return SaxonDocument.node(new StreamSource(new StringReader(text)));
222 }
223
224
225
226
227
228
229 private static XdmNode node(final StreamSource source) {
230 try {
231 return SaxonDocument.DOC_BUILDER.build(source);
232 } catch (final SaxonApiException exception) {
233 throw new IllegalArgumentException(
234 String.format("SaxonDocument can't parse XML from source '%s'", source),
235 exception
236 );
237 }
238 }
239 }