1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.jxpath.ri.model;
19
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertNotEquals;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23
24 import org.apache.commons.jxpath.AbstractFactory;
25 import org.apache.commons.jxpath.AbstractJXPathTest;
26 import org.apache.commons.jxpath.JXPathContext;
27 import org.apache.commons.jxpath.JXPathException;
28 import org.apache.commons.jxpath.Pointer;
29 import org.apache.commons.jxpath.Variables;
30 import org.apache.commons.jxpath.xml.DocumentContainer;
31 import org.junit.jupiter.api.BeforeEach;
32 import org.junit.jupiter.api.Test;
33
34
35
36
37 public abstract class AbstractXMLModelTest extends AbstractJXPathTest {
38
39 protected JXPathContext context;
40
41 protected void assertXMLSignature(final JXPathContext context, final String path, final String signature, final boolean elements, final boolean attributes,
42 final boolean text, final boolean pi) {
43 final Object node = context.getPointer(path).getNode();
44 final String sig = getXMLSignature(node, elements, attributes, text, pi);
45 assertEquals(signature, sig, "XML Signature mismatch: ");
46 }
47
48 protected JXPathContext createContext() {
49 final JXPathContext context = JXPathContext.newContext(createDocumentContainer());
50 context.setFactory(getAbstractFactory());
51 context.registerNamespace("product", "productNS");
52 return context;
53 }
54
55 protected DocumentContainer createDocumentContainer() {
56 return new DocumentContainer(AbstractJXPathTest.class.getResource("Vendor.xml"), getModel());
57 }
58
59 protected abstract AbstractFactory getAbstractFactory();
60
61 protected abstract String getModel();
62
63
64
65
66 protected abstract String getXMLSignature(Object node, boolean elements, boolean attributes, boolean text, boolean pi);
67
68 @Override
69 @BeforeEach
70 public void setUp() {
71 if (context == null) {
72 final DocumentContainer docCtr = createDocumentContainer();
73 context = createContext();
74 final Variables vars = context.getVariables();
75 vars.declareVariable("document", docCtr.getValue());
76 vars.declareVariable("container", docCtr);
77 vars.declareVariable("element", context.getPointer("vendor/location/address/street").getNode());
78 }
79 }
80
81 @Test
82 public void testAxisAncestor() {
83
84 assertXPathValue(context, "vendor/product/price:sale/saleEnds/" + "ancestor::price:sale/saleEnds", "never");
85
86 assertXPathValue(context, "vendor/product/price:sale/saleEnds/ancestor::price:*" + "/saleEnds", "never");
87 }
88
89 @Test
90 public void testAxisAncestorOrSelf() {
91
92 assertXPathValue(context, "vendor/product/price:sale/" + "ancestor-or-self::price:sale/saleEnds", "never");
93 }
94
95 @Test
96 public void testAxisAttribute() {
97
98 assertXPathValue(context, "vendor/location/@id", "100");
99
100 assertXPathPointer(context, "vendor/location/@id", "/vendor[1]/location[1]/@id");
101
102 assertXPathValueIterator(context, "vendor/location/@id", list("100", "101"));
103
104 assertXPathValue(context, "vendor/product/price:amount/@price:discount", "10%");
105
106 assertXPathValue(context, "namespace-uri(vendor/product/price:amount/@price:discount)", "priceNS");
107
108 assertXPathValue(context, "local-name(vendor/product/price:amount/@price:discount)", "discount");
109
110 assertXPathValue(context, "name(vendor/product/price:amount/@price:discount)", "price:discount");
111
112 assertXPathValue(context, "vendor/product/price:amount/@discount", "20%");
113
114 assertXPathValue(context, "namespace-uri(vendor/product/price:amount/@discount)", "");
115
116 assertXPathValue(context, "local-name(vendor/product/price:amount/@discount)", "discount");
117
118 assertXPathValue(context, "name(vendor/product/price:amount/@discount)", "discount");
119
120 assertXPathValueIterator(context, "vendor/product/price:amount/@price:*", list("10%"));
121
122 assertXPathValueIterator(context, "vendor/location[1]/@*", set("100", "", "local"));
123
124 assertXPathValueIterator(context, "vendor/product/price:amount/@*",
125
126 set("10%", "20%"));
127
128 assertXPathValueIterator(context, "vendor/product/price:amount/attribute::node()",
129
130 set("10%", "20%"));
131
132 assertXPathValueIterator(context, "vendor/product/price:amount/@*[namespace-uri() = '']", list("20%"));
133
134 assertXPathValue(context, "vendor/location/@manager", "");
135
136 assertXPathValueLenient(context, "vendor/location/@missing", null);
137
138 assertXPathValueLenient(context, "vendor/location/@miss:missing", null);
139
140 assertXPathValue(context, "vendor/location[@id='101']//street", "Tangerine Drive");
141 assertXPathValueIterator(context, "/vendor/location[1]/@*[name()!= 'manager']", list("100", "local"));
142 }
143
144 @Test
145 public void testAxisChild() {
146 assertXPathValue(context, "vendor/location/address/street", "Orchard Road");
147
148 assertXPathValue(context, "vendor/location/address/city", "Fruit Market");
149
150 assertXPathValue(context, "local-name(vendor/product/price:amount)", "amount");
151
152 assertXPathValue(context, "local-name(vendor/location)", "location");
153
154 assertXPathValue(context, "name(vendor/product/price:amount)", "value:amount");
155
156 assertXPathValue(context, "name(vendor/location)", "location");
157
158 assertXPathValue(context, "namespace-uri(vendor/product/price:amount)", "priceNS");
159
160 assertXPathValue(context, "vendor/product/prix", "934.99");
161 assertXPathValue(context, "/vendor/contact[@name='jim']", "Jim");
162 assertThrows(JXPathException.class, () -> {
163 context.setLenient(false);
164 context.getValue("/vendor/contact[@name='jane']");
165 }, "No such value: /vendor/contact[@name='jim']");
166 assertThrows(JXPathException.class, () -> {
167 context.setLenient(false);
168 context.getValue("/vendor/contact[@name='jane']/*");
169 }, "No such value: /vendor/contact[@name='jane']/*");
170
171 assertXPathValue(context, "count(vendor/product/price:*)", Double.valueOf(2));
172
173 assertXPathValue(context, "count(vendor/product/*)", Double.valueOf(4));
174
175 assertXPathValue(context, "vendor/product/price:amount", "45.95");
176
177 context.registerNamespace("x", "temp");
178 assertXPathValue(context, "vendor/x:pos//number", "109");
179 }
180
181 @Test
182 public void testAxisChildIndexPredicate() {
183 assertXPathValue(context, "vendor/location[2]/address/street", "Tangerine Drive");
184 }
185
186 @Test
187 public void testAxisDescendant() {
188
189 assertXPathValue(context, "//street", "Orchard Road");
190
191 assertXPathValue(context, "count(//price:*)", Double.valueOf(2));
192 assertXPathValueIterator(context, "vendor//saleEnds", list("never"));
193 assertXPathValueIterator(context, "vendor//promotion", list(""));
194 assertXPathValueIterator(context, "vendor//saleEnds[../@stores = 'all']", list("never"));
195 assertXPathValueIterator(context, "vendor//promotion[../@stores = 'all']", list(""));
196 }
197
198
199
200
201
202
203
204
205
206 @Test
207 public void testAxisFollowing() {
208 assertXPathValueIterator(context, "vendor/contact/following::location//street", list("Orchard Road", "Tangerine Drive"));
209
210 assertXPathValue(context, "//location/following::price:sale/saleEnds", "never");
211 assertXPathPointer(context, "//location[2]/following::node()[2]", "/vendor[1]/product[1]");
212 }
213
214 @Test
215 public void testAxisFollowingSibling() {
216
217 assertXPathValue(context, "vendor/location[.//employeeCount = 10]/" + "following-sibling::location//street", "Tangerine Drive");
218
219 assertXPathPointer(context, "vendor/location[.//employeeCount = 10]/" + "following-sibling::location//street",
220 "/vendor[1]/location[2]/address[1]/street[1]");
221 }
222
223 @Test
224 public void testAxisNamespace() {
225
226 assertXPathValueAndPointer(context, "vendor/product/prix/namespace::price", "priceNS", "/vendor[1]/product[1]/prix[1]/namespace::price");
227
228 assertXPathValue(context, "count(vendor/product/namespace::*)", Double.valueOf(3));
229
230 assertXPathValue(context, "name(vendor/product/prix/namespace::price)", "price");
231
232 assertXPathValue(context, "local-name(vendor/product/prix/namespace::price)", "price");
233 }
234
235 @Test
236 public void testAxisParent() {
237
238 assertXPathPointer(context, "//street/..", "/vendor[1]/location[1]/address[1]");
239
240 assertXPathPointerIterator(context, "//street/..", list("/vendor[1]/location[2]/address[1]", "/vendor[1]/location[1]/address[1]"));
241
242 assertXPathValue(context, "vendor/product/price:sale/saleEnds/parent::price:*" + "/saleEnds", "never");
243 }
244
245 @Test
246 public void testAxisPreceding() {
247
248 assertXPathPointer(context, "//location[2]/preceding-sibling::location//street", "/vendor[1]/location[1]/address[1]/street[1]");
249 assertXPathPointer(context, "//location[2]/preceding::*[1]", "/vendor[1]/location[1]/employeeCount[1]");
250 assertXPathPointer(context, "//location[2]/preceding::node()[3]", "/vendor[1]/location[1]/employeeCount[1]/text()[1]");
251 assertXPathPointer(context, "//location[2]/preceding::node()[4]", "/vendor[1]/location[1]/employeeCount[1]");
252 }
253
254 @Test
255 public void testAxisPrecedingSibling() {
256
257 assertXPathPointer(context, "//location[2]/preceding-sibling::location//street", "/vendor[1]/location[1]/address[1]/street[1]");
258 }
259
260 @Test
261 public void testAxisSelf() {
262
263 assertXPathValue(context, "//price:sale/self::price:sale/saleEnds", "never");
264
265 assertXPathValueLenient(context, "//price:sale/self::x/saleEnds", null);
266 }
267
268 @Test
269 public void testBooleanFunction() {
270 assertXPathValue(context, "boolean(vendor//saleEnds[../@stores = 'all'])", Boolean.TRUE);
271 assertXPathValue(context, "boolean(vendor//promotion[../@stores = 'all'])", Boolean.TRUE);
272 assertXPathValue(context, "boolean(vendor//promotion[../@stores = 'some'])", Boolean.FALSE);
273 }
274
275 @Test
276 public void testContainer() {
277 assertXPathValue(context, "$container/vendor//street", "Orchard Road");
278 assertXPathValue(context, "$container//street", "Orchard Road");
279 assertXPathPointer(context, "$container//street", "$container/vendor[1]/location[1]/address[1]/street[1]");
280
281 assertXPathValue(context, "number(vendor/location/employeeCount)", Double.valueOf(10));
282 }
283
284
285
286
287 @Test
288 public void testCreatePath() {
289
290 assertXPathCreatePath(context, "/vendor[1]/location[3]", "", "/vendor[1]/location[3]");
291
292 assertXPathCreatePath(context, "/vendor[1]/location[3]/address/street", "", "/vendor[1]/location[3]/address[1]/street[1]");
293
294 assertXPathCreatePath(context, "/vendor[1]/location[2]/@manager", "", "/vendor[1]/location[2]/@manager");
295 assertXPathCreatePath(context, "/vendor[1]/location[1]/@name", "local", "/vendor[1]/location[1]/@name");
296 assertXPathCreatePathAndSetValue(context, "/vendor[1]/location[4]/@manager", "", "/vendor[1]/location[4]/@manager");
297 context.registerNamespace("price", "priceNS");
298
299 assertXPathCreatePath(context, "/vendor[1]/price:foo/price:bar", "", "/vendor[1]/price:foo[1]/price:bar[1]");
300 }
301
302
303
304
305 @Test
306 public void testCreatePathAndSetValue() {
307
308 assertXPathCreatePathAndSetValue(context, "vendor/location[3]", "", "/vendor[1]/location[3]");
309
310 assertXPathCreatePathAndSetValue(context, "vendor/location[3]/address/street", "Lemon Circle", "/vendor[1]/location[3]/address[1]/street[1]");
311
312 assertXPathCreatePathAndSetValue(context, "vendor/location[2]/@manager", "John Doe", "/vendor[1]/location[2]/@manager");
313 assertXPathCreatePathAndSetValue(context, "vendor/location[1]/@manager", "John Doe", "/vendor[1]/location[1]/@manager");
314 assertXPathCreatePathAndSetValue(context, "/vendor[1]/location[4]/@manager", "James Dow", "/vendor[1]/location[4]/@manager");
315 assertXPathCreatePathAndSetValue(context, "vendor/product/product:name/attribute::price:language", "English",
316 "/vendor[1]/product[1]/product:name[1]/@price:language");
317 context.registerNamespace("price", "priceNS");
318
319 assertXPathCreatePathAndSetValue(context, "/vendor[1]/price:foo/price:bar", "123.20", "/vendor[1]/price:foo[1]/price:bar[1]");
320 }
321
322 @Test
323 public void testDocument() {
324 assertXPathValue(context, "$document/vendor/location[1]//street", "Orchard Road");
325 assertXPathPointer(context, "$document/vendor/location[1]//street", "$document/vendor[1]/location[1]/address[1]/street[1]");
326 assertXPathValue(context, "$document/vendor//street", "Orchard Road");
327 }
328
329 @Test
330 public void testDocumentOrder() {
331 assertDocumentOrder(context, "vendor/location", "vendor/location/address/street", -1);
332 assertDocumentOrder(context, "vendor/location[@id = '100']", "vendor/location[@id = '101']", -1);
333 assertDocumentOrder(context, "vendor//price:amount", "vendor/location", 1);
334 }
335
336 @Test
337 public void testElementInVariable() {
338 assertXPathValue(context, "$element", "Orchard Road");
339 }
340
341 @Test
342 public void testFunctionsLastAndPosition() {
343 assertXPathPointer(context, "vendor//location[last()]", "/vendor[1]/location[2]");
344 }
345
346 @Test
347 public void testID() {
348 context.setIdentityManager((context, id) -> {
349 NodePointer ptr = (NodePointer) context.getPointer("/");
350 ptr = ptr.getValuePointer();
351 return ptr.getPointerByID(context, id);
352 });
353 assertXPathValueAndPointer(context, "id(101)//street", "Tangerine Drive", "id('101')/address[1]/street[1]");
354 assertXPathPointerLenient(context, "id(105)/address/street", "id(105)/address/street");
355 }
356
357 @Test
358 public void testLang() {
359
360 assertXPathValue(context, "//product/prix/@xml:lang", "fr");
361
362 assertXPathValue(context, "//product/prix[lang('fr')]", "934.99");
363
364 assertXPathValue(context, "//product/price:sale[lang('en')]/saleEnds", "never");
365 }
366
367 @Test
368 public void testNamespaceMapping() {
369 context.registerNamespace("rate", "priceNS");
370 context.registerNamespace("goods", "productNS");
371 assertEquals("priceNS", context.getNamespaceURI("price"), "Context node namespace resolution");
372 assertEquals("priceNS", context.getNamespaceURI("rate"), "Registered namespace resolution");
373
374 assertXPathValue(context, "count(vendor/product/rate:*)", Double.valueOf(2));
375 assertXPathValue(context, "vendor[1]/product[1]/rate:amount[1]/@rate:discount", "10%");
376 assertXPathValue(context, "vendor[1]/product[1]/rate:amount[1]/@price:discount", "10%");
377 assertXPathValue(context, "vendor[1]/product[1]/price:amount[1]/@rate:discount", "10%");
378 assertXPathValue(context, "vendor[1]/product[1]/price:amount[1]/@price:discount", "10%");
379
380 assertXPathValueAndPointer(context, "//product:name", "Box of oranges", "/vendor[1]/product[1]/goods:name[1]");
381
382 final JXPathContext childCtx = JXPathContext.newContext(context, context.getContextBean());
383 assertXPathValueAndPointer(childCtx, "//product:name", "Box of oranges", "/vendor[1]/product[1]/goods:name[1]");
384
385 final JXPathContext relativeCtx = context.getRelativeContext(context.getPointer("/vendor"));
386 assertXPathValueAndPointer(relativeCtx, "product/product:name", "Box of oranges", "/vendor[1]/product[1]/goods:name[1]");
387 }
388
389 @Test
390 public void testNodes() {
391 final Pointer pointer = context.getPointer("/vendor[1]/contact[1]");
392 assertNotEquals(pointer.getNode(), pointer.getValue());
393 }
394
395 @Test
396 public void testNodeTypeComment() {
397
398 assertXPathValue(context, "//product/comment()", "We are not buying this product, ever");
399 }
400
401 @Test
402 public void testNodeTypeProcessingInstruction() {
403
404 assertXPathValue(context, "//product/processing-instruction()", "do not show anybody");
405
406 assertXPathValue(context, "//product/processing-instruction('report')", "average only");
407
408 assertXPathPointer(context, "//product/processing-instruction('report')", "/vendor[1]/product[1]/processing-instruction('report')[1]");
409
410 assertXPathValue(context, "name(//product/processing-instruction()[1])", "security");
411 }
412
413 @Test
414 public void testNodeTypeText() {
415
416
417 assertXPathValue(context, "//product/text()[. != '']", "We love this product.");
418
419 assertXPathPointer(context, "//product/text()", "/vendor[1]/product[1]/text()[1]");
420 }
421
422
423
424
425 @Test
426 public void testRemovePath() {
427
428 context.removePath("vendor/location[@id = '101']//street/text()");
429 assertEquals("", context.getValue("vendor/location[@id = '101']//street"), "Remove DOM text");
430 context.removePath("vendor/location[@id = '101']//street");
431 assertEquals(Double.valueOf(0), context.getValue("count(vendor/location[@id = '101']//street)"), "Remove DOM element");
432 context.removePath("vendor/location[@id = '100']/@name");
433 assertEquals(Double.valueOf(0), context.getValue("count(vendor/location[@id = '100']/@name)"), "Remove DOM attribute");
434 }
435
436 @Test
437 public void testSetValue() {
438 assertXPathSetValue(context, "vendor/location[@id = '100']", "New Text");
439 assertXMLSignature(context, "vendor/location[@id = '100']", "<E>New Text</E>", false, false, true, false);
440 assertXPathSetValue(context, "vendor/location[@id = '101']", "Replacement Text");
441 assertXMLSignature(context, "vendor/location[@id = '101']", "<E>Replacement Text</E>", false, false, true, false);
442 }
443
444 @Test
445 public void testTypeConversions() {
446
447 assertXPathValue(context, "vendor/location/employeeCount + 1", Double.valueOf(11));
448
449 assertXPathValue(context, "vendor/location/employeeCount and true()", Boolean.TRUE);
450 }
451
452 @Test
453 public void testUnion() {
454 assertXPathValue(context, "/vendor[1]/contact[1] | /vendor[1]/contact[4]", "John");
455 assertXPathValue(context, "/vendor[1]/contact[4] | /vendor[1]/contact[1]", "John");
456 }
457 }