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.assertNull;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Locale;
29
30 import org.apache.commons.jxpath.AbstractFactory;
31 import org.apache.commons.jxpath.AbstractJXPathTest;
32 import org.apache.commons.jxpath.ClassFunctions;
33 import org.apache.commons.jxpath.JXPathContext;
34 import org.apache.commons.jxpath.NestedTestBean;
35 import org.apache.commons.jxpath.Pointer;
36 import org.apache.commons.jxpath.ri.QName;
37 import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
38 import org.apache.commons.jxpath.ri.compiler.TestFunctions;
39 import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
40 import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
41 import org.apache.commons.jxpath.ri.model.dynabeans.DynaBeanModelTest;
42 import org.junit.jupiter.api.BeforeEach;
43 import org.junit.jupiter.api.Test;
44
45
46
47
48 public abstract class AbstractBeanModelTest extends AbstractJXPathTest {
49
50 private JXPathContext context;
51
52 protected abstract Object createContextBean();
53
54 protected abstract AbstractFactory getAbstractFactory();
55
56 private int relativeProperty(final PropertyPointer holder, final int offset) {
57 final String[] names = holder.getPropertyNames();
58 for (int i = 0; i < names.length; i++) {
59 if (names[i].equals("integers")) {
60 return i + offset;
61 }
62 }
63 return -1;
64 }
65
66 @Override
67 @BeforeEach
68 public void setUp() {
69
70 context = JXPathContext.newContext(createContextBean());
71 context.setLocale(Locale.US);
72 context.setFactory(getAbstractFactory());
73
74 }
75
76 @Test
77 public void testAttributeLang() {
78 assertXPathValue(context, "@xml:lang", "en-US");
79 assertXPathValue(context, "count(@xml:*)", Double.valueOf(1));
80 assertXPathValue(context, "lang('en')", Boolean.TRUE);
81 assertXPathValue(context, "lang('fr')", Boolean.FALSE);
82 }
83
84
85
86
87 @Test
88 public void testAttributeName() {
89 assertXPathValue(context, "nestedBean[@name = 'int']", Integer.valueOf(1));
90 assertXPathPointer(context, "nestedBean[@name = 'int']", "/nestedBean/int");
91 }
92
93 @Test
94 public void testAxisAncestor() {
95
96 assertXPathValue(context, "int/ancestor::root = /", Boolean.TRUE);
97 assertXPathValue(context, "count(beans/name/ancestor-or-self::node())", Double.valueOf(5));
98 assertXPathValue(context, "beans/name/ancestor-or-self::node()[3] = /", Boolean.TRUE);
99 }
100
101 @Test
102 public void testAxisAttribute() {
103
104 assertXPathValue(context, "count(@*)", Double.valueOf(21.0));
105
106 assertXPathValueLenient(context, "@foo", null);
107 }
108
109 @Test
110 public void testAxisChild() {
111 assertXPathValue(context, "boolean", Boolean.FALSE);
112 assertXPathPointer(context, "boolean", "/boolean");
113 assertXPathPointerIterator(context, "boolean", list("/boolean"));
114
115 assertXPathValue(context, "count(set)", Double.valueOf(3));
116
117
118 assertXPathValueIterator(context, "foo:boolean", list());
119
120 assertXPathValue(context, "count(*)", Double.valueOf(21));
121
122 assertXPathValue(context, "count(child::node())", Double.valueOf(21));
123 }
124
125 @Test
126 public void testAxisChildNestedBean() {
127
128 assertXPathValue(context, "nestedBean/name", "Name 0");
129 assertXPathPointer(context, "nestedBean/name", "/nestedBean/name");
130 assertXPathPointerIterator(context, "nestedBean/name", list("/nestedBean/name"));
131 }
132
133 @Test
134 public void testAxisChildNestedCollection() {
135 assertXPathValueIterator(context, "integers", list(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4)));
136 assertXPathPointer(context, "integers", "/integers");
137 assertXPathPointerIterator(context, "integers", list("/integers[1]", "/integers[2]", "/integers[3]", "/integers[4]"));
138 }
139
140 @Test
141 public void testAxisDescendant() {
142
143 assertXPathValue(context, "count(descendant::node())", Double.valueOf(65));
144
145 assertXPathValue(context, "count(descendant::root)", Double.valueOf(0));
146 assertXPathValue(context, "count(descendant::name)", Double.valueOf(7));
147 }
148
149 @Test
150 public void testAxisDescendantOrSelf() {
151
152 assertXPathValueIterator(context, "descendant-or-self::name", set("Name 1", "Name 2", "Name 3", "Name 6", "Name 0", "Name 5", "Name 4"));
153
154 assertXPathValueIterator(context, "//name", set("Name 1", "Name 2", "Name 3", "Name 6", "Name 0", "Name 5", "Name 4"));
155
156 assertXPathValue(context, "count(descendant-or-self::root)", Double.valueOf(1));
157
158 assertXPathValue(context, "count(nestedBean//.)", Double.valueOf(7));
159
160 assertXPathValue(context, "count(//self::beans)", Double.valueOf(2));
161
162 assertXPathValue(context, "count(descendant-or-self::node())", Double.valueOf(66));
163 }
164
165 @Test
166 public void testAxisFollowing() {
167
168 assertXPathValue(context, "count(nestedBean/strings[2]/following::node())", Double.valueOf(21));
169 assertXPathValue(context, "count(nestedBean/strings[2]/following::strings)", Double.valueOf(7));
170 }
171
172 @Test
173 public void testAxisFollowingSibling() {
174
175 assertXPathValue(context, "count(/nestedBean/following-sibling::node())", Double.valueOf(8));
176 assertXPathValue(context, "count(/nestedBean/following-sibling::object)", Double.valueOf(1));
177
178 assertXPathValue(context, "count(/nestedBean/boolean/../following-sibling::node())", Double.valueOf(8));
179 assertXPathValue(context, "count(/nestedBean/boolean/../following-sibling::object)", Double.valueOf(1));
180
181 assertXPathValue(context, "count(/descendant::boolean/following-sibling::node())", Double.valueOf(53));
182 assertXPathValue(context, "count(/descendant::boolean/following-sibling::name)", Double.valueOf(7));
183 }
184
185 @Test
186 public void testAxisParent() {
187
188 assertXPathValue(context, "count(/beans/..)", Double.valueOf(1));
189 assertXPathValue(context, "count(//..)", Double.valueOf(9));
190 assertXPathValue(context, "count(//../..)", Double.valueOf(2));
191 assertXPathValueIterator(context, "//parent::beans/name", list("Name 1", "Name 2"));
192 }
193
194 @Test
195 public void testAxisPreceding() {
196
197 assertXPathValue(context, "count(beans[2]/int/preceding::node())", Double.valueOf(8));
198 assertXPathValue(context, "count(beans[2]/int/preceding::boolean)", Double.valueOf(2));
199 }
200
201 @Test
202 public void testAxisPrecedingSibling() {
203
204 assertXPathValue(context, "count(/boolean/preceding-sibling::node())", Double.valueOf(2));
205 assertXPathValue(context, "count(/nestedBean/int/../preceding-sibling::node())", Double.valueOf(12));
206 assertXPathValue(context, "count(/descendant::int/preceding-sibling::node())", Double.valueOf(10));
207 }
208
209 @Test
210 public void testAxisSelf() {
211
212 assertXPathValue(context, "self::node() = /", Boolean.TRUE);
213 assertXPathValue(context, "self::root = /", Boolean.TRUE);
214 }
215
216 @Test
217 public void testBooleanPredicate() {
218
219
220
221 assertXPathValue(context, "beans[int > 2]/name", "Name 2");
222 assertXPathValueIterator(context, "beans[int > 2]/name", list("Name 2"));
223 assertXPathValueIterator(context, "beans[int >= 1]/name", list("Name 1", "Name 2"));
224 assertXPathValueIterator(context, "beans[int < 2]/name", list("Name 1"));
225 assertXPathValueIterator(context, "beans[int <= 3]/name", list("Name 1", "Name 2"));
226 assertXPathValueIterator(context, "beans[1]/strings[string-length() = 8]", list("String 1", "String 2", "String 3"));
227
228 assertXPathValueIterator(context, "//self::node()[name = 'Name 0']/name", list("Name 0"));
229
230 assertXPathValue(context, "beans/strings[name(.)='strings'][2]", "String 2");
231
232 assertXPathValueIterator(context, "//self::node()[name(.) = concat('n', 'a', 'm', 'e')]",
233 list("Name 1", "Name 2", "Name 3", "Name 6", "Name 0", "Name 5", "Name 4"));
234 assertXPathValueIterator(context, "integers[position()<3]", list(Integer.valueOf(1), Integer.valueOf(2)));
235 context.getVariables().declareVariable("temp", context.getValue("beans"));
236 assertXPathValueIterator(context, "$temp[int < 2]/int", list(Integer.valueOf(1)));
237 }
238
239 @Test
240 public void testCoreFunctions() {
241 assertXPathValue(context, "boolean(boolean)", Boolean.TRUE);
242 assertXPathValue(context, "boolean(boolean = false())", Boolean.TRUE);
243 assertXPathValue(context, "boolean(integers[position() < 3])", Boolean.TRUE);
244 assertXPathValue(context, "boolean(integers[position() > 4])", Boolean.FALSE);
245 assertXPathValue(context, "sum(integers)", Double.valueOf(10));
246 assertXPathValueAndPointer(context, "integers[last()]", Integer.valueOf(4), "/integers[4]");
247 assertXPathValueAndPointer(context, "//strings[last()]", "String 3", "/beans[1]/strings[3]");
248 }
249
250 @Test
251 public void testCreatePath() {
252 context.setValue("nestedBean", null);
253
254 assertXPathCreatePath(context, "/nestedBean/int", Integer.valueOf(1), "/nestedBean/int");
255 assertThrows(Exception.class,
256 () -> assertXPathCreatePath(context, "/nestedBean/beans[last() + 1]", Integer.valueOf(1), "/nestedBean/beans[last() + 1]"),
257 "Exception thrown on invalid path for creation");
258 }
259
260 @Test
261 public void testCreatePathAndSetValue() {
262 context.setValue("nestedBean", null);
263
264 assertXPathCreatePathAndSetValue(context, "/nestedBean/int", Integer.valueOf(2), "/nestedBean/int");
265 }
266
267 @Test
268 public void testCreatePathAndSetValueCreateBeanExpandCollection() {
269 context.setValue("nestedBean", null);
270
271
272 assertXPathCreatePathAndSetValue(context, "/nestedBean/strings[2]", "Test", "/nestedBean/strings[2]");
273 }
274
275 @Test
276 public void testCreatePathAndSetValueExpandExistingCollection() {
277
278 assertXPathCreatePathAndSetValue(context, "/beans[3]/int", Integer.valueOf(2), "/beans[3]/int");
279 }
280
281 @Test
282 public void testCreatePathAndSetValueExpandNewCollection() {
283 context.setValue("beans", null);
284
285
286 assertXPathCreatePathAndSetValue(context, "/beans[2]/int", Integer.valueOf(2), "/beans[2]/int");
287 }
288
289 @Test
290 public void testCreatePathCreateBeanExpandCollection() {
291 context.setValue("nestedBean", null);
292
293
294 assertXPathCreatePath(context, "/nestedBean/strings[2]", "String 2", "/nestedBean/strings[2]");
295 }
296
297 @Test
298 public void testCreatePathExpandExistingCollection() {
299
300
301 assertXPathCreatePathAndSetValue(context, "/integers[5]", Integer.valueOf(3), "/integers[5]");
302 }
303
304 @Test
305 public void testCreatePathExpandExistingCollectionAndSetProperty() {
306
307 assertXPathCreatePath(context, "/beans[3]/int", Integer.valueOf(1), "/beans[3]/int");
308 }
309
310 @Test
311 public void testCreatePathExpandNewCollection() {
312 context.setValue("beans", null);
313
314
315 assertXPathCreatePath(context, "/beans[2]/int", Integer.valueOf(1), "/beans[2]/int");
316 }
317
318 @Test
319 public void testDocumentOrder() {
320 assertDocumentOrder(context, "boolean", "int", -1);
321 assertDocumentOrder(context, "integers[1]", "integers[2]", -1);
322 assertDocumentOrder(context, "integers[1]", "integers[1]", 0);
323 assertDocumentOrder(context, "nestedBean/int", "nestedBean", 1);
324 assertDocumentOrder(context, "nestedBean/int", "nestedBean/strings", -1);
325 assertDocumentOrder(context, "nestedBean/int", "object/int", -1);
326 }
327
328 @Test
329 public void testIndexPredicate() {
330 assertXPathValue(context, "integers[2]", Integer.valueOf(2));
331 assertXPathPointer(context, "integers[2]", "/integers[2]");
332 assertXPathPointerIterator(context, "integers[2]", list("/integers[2]"));
333 assertXPathValue(context, "beans[1]/name", "Name 1");
334 assertXPathPointer(context, "beans[1]/name", "/beans[1]/name");
335 assertXPathValueIterator(context, "beans[1]/strings", list("String 1", "String 2", "String 3"));
336 assertXPathValueIterator(context, "beans/strings[2]", list("String 2", "String 2"));
337
338 assertXPathValue(context, "beans/strings[2]", "String 2");
339
340 assertXPathValue(context, "(beans/strings[2])[1]", "String 2");
341 }
342
343 private void testIndividual(final int relativePropertyIndex, final int offset, final boolean useStartLocation, final boolean reverse, final int expected) {
344 final PropertyOwnerPointer root = (PropertyOwnerPointer) NodePointer.newNodePointer(new QName(null, "root"), createContextBean(), Locale.getDefault());
345 NodeIterator it;
346 PropertyPointer start = null;
347 if (useStartLocation) {
348 start = root.getPropertyPointer();
349 start.setPropertyIndex(relativeProperty(start, relativePropertyIndex));
350 start.setIndex(offset);
351 }
352 it = root.childIterator(new NodeNameTest(new QName(null, "integers")), reverse, start);
353 int size = 0;
354 while (it.setPosition(it.getPosition() + 1)) {
355 size++;
356 }
357 assertEquals(expected, size, "ITERATIONS: Individual, relativePropertyIndex=" + relativePropertyIndex + ", offset=" + offset + ", useStartLocation="
358 + useStartLocation + ", reverse=" + reverse);
359 }
360
361
362
363
364 @Test
365 public void testIndividualIterators() {
366 testIndividual(+1, 0, true, false, 0);
367 testIndividual(-1, 0, true, false, 4);
368 testIndividual(0, -1, true, true, 4);
369 testIndividual(+1, -1, true, true, 4);
370 testIndividual(-1, -1, true, true, 0);
371 testIndividual(0, 1, true, false, 2);
372 testIndividual(0, 1, true, true, 1);
373 testIndividual(0, 0, false, false, 4);
374 testIndividual(0, 0, false, true, 4);
375 }
376
377 @Test
378 public void testIterateAndSet() {
379 final JXPathContext context = JXPathContext.newContext(createContextBean());
380 Iterator<Pointer> it = context.iteratePointers("beans/int");
381 int i = 5;
382 while (it.hasNext()) {
383 final NodePointer pointer = (NodePointer) it.next();
384 pointer.setValue(Integer.valueOf(i++));
385 }
386 it = context.iteratePointers("beans/int");
387 final List<Object> actual = new ArrayList<>();
388 while (it.hasNext()) {
389 actual.add(it.next().getValue());
390 }
391 assertEquals(list(Integer.valueOf(5), Integer.valueOf(6)), actual, "Iterating <" + "beans/int" + ">");
392 }
393
394
395
396
397 @Test
398 public void testIteratePointerSetValue() {
399 final JXPathContext context = JXPathContext.newContext(createContextBean());
400 assertXPathValue(context, "/beans[1]/name", "Name 1");
401 assertXPathValue(context, "/beans[2]/name", "Name 2");
402
403 context.setValue("/beans[2]/name", "Name 2 set");
404 assertXPathValue(context, "/beans[2]/name", "Name 2 set");
405
406 context.setValue("/beans[2]/name", "Name 2");
407 assertXPathValue(context, "/beans[2]/name", "Name 2");
408 int iterCount = 0;
409 final Iterator<Pointer> iter = context.iteratePointers("/beans/name");
410 while (iter.hasNext()) {
411 iterCount++;
412 final Pointer pointer = iter.next();
413 String s = (String) pointer.getValue();
414 s += "suffix";
415 pointer.setValue(s);
416 assertEquals(s, pointer.getValue(), "pointer.getValue");
417
418 assertEquals(s, context.getValue(pointer.asPath()), "context.getValue");
419 }
420 assertEquals(2, iterCount, "Iteration count");
421 assertXPathValue(context, "/beans[1]/name", "Name 1suffix");
422 assertXPathValue(context, "/beans[2]/name", "Name 2suffix");
423 }
424
425 @Test
426 public void testIteratePropertyArrayWithHasNext() {
427 final JXPathContext context = JXPathContext.newContext(createContextBean());
428 final Iterator<Pointer> it = context.iteratePointers("/integers");
429 final List<String> actual = new ArrayList<>();
430 while (it.hasNext()) {
431 actual.add(it.next().asPath());
432 }
433 assertEquals(list("/integers[1]", "/integers[2]", "/integers[3]", "/integers[4]"), actual, "Iterating 'hasNext'/'next'<" + "/integers" + ">");
434 }
435
436 @Test
437 public void testIteratePropertyArrayWithoutHasNext() {
438 final JXPathContext context = JXPathContext.newContext(createContextBean());
439 final Iterator<Pointer> it = context.iteratePointers("/integers");
440 final List<String> actual = new ArrayList<>();
441 for (int i = 0; i < 4; i++) {
442 actual.add(it.next().toString());
443 }
444 assertEquals(list("/integers[1]", "/integers[2]", "/integers[3]", "/integers[4]"), actual, "Iterating 'next'<" + "/integers" + ">");
445 }
446
447 private void testMultiple(final int propertyIndex, final int offset, final boolean useStartLocation, final boolean reverse, final int expected) {
448 final PropertyOwnerPointer root = (PropertyOwnerPointer) NodePointer.newNodePointer(new QName(null, "root"), createContextBean(), Locale.getDefault());
449 NodeIterator it;
450 PropertyPointer start = null;
451 if (useStartLocation) {
452 start = root.getPropertyPointer();
453 start.setPropertyIndex(propertyIndex);
454 start.setIndex(offset);
455 }
456 it = root.childIterator(null, reverse, start);
457 int size = 0;
458 while (it.setPosition(it.getPosition() + 1)) {
459
460 size++;
461 }
462 assertEquals(expected, size, "ITERATIONS: Multiple, propertyIndex=" + propertyIndex + ", offset=" + offset + ", useStartLocation=" + useStartLocation
463 + ", reverse=" + reverse);
464 }
465
466
467
468
469 @Test
470 public void testMultipleIterators() {
471 testMultiple(0, 0, true, false, 20);
472 testMultiple(3, 0, true, false, 16);
473 testMultiple(3, -1, true, true, 8);
474 testMultiple(3, 0, true, true, 4);
475 testMultiple(0, 0, false, false, 21);
476 testMultiple(0, 0, false, true, 21);
477 testMultiple(3, 1, true, false, 15);
478 testMultiple(3, 3, true, false, 13);
479 }
480
481 @Test
482 public void testRelativeContextAbsolutePath() {
483 final JXPathContext relative = context.getRelativeContext(context.getPointer("nestedBean"));
484 assertXPathValueAndPointer(relative, "/integers[2]", Integer.valueOf(2), "/integers[2]");
485 }
486
487 @Test
488 public void testRelativeContextInheritance() {
489 context.setFunctions(new ClassFunctions(TestFunctions.class, "test"));
490 final JXPathContext relative = context.getRelativeContext(context.getPointer("nestedBean"));
491 assertXPathValue(relative, "test:countPointers(strings)", Integer.valueOf(3));
492 }
493
494 @Test
495 public void testRelativeContextParent() {
496 final JXPathContext relative = context.getRelativeContext(context.getPointer("nestedBean"));
497 assertXPathValueAndPointer(relative, "../integers[2]", Integer.valueOf(2), "/integers[2]");
498 }
499
500 @Test
501 public void testRelativeContextRelativePath() {
502 final JXPathContext relative = context.getRelativeContext(context.getPointer("nestedBean"));
503 assertXPathValueAndPointer(relative, "int", Integer.valueOf(1), "/nestedBean/int");
504 }
505
506 @Test
507 public void testRemoveAllArrayElements() {
508 context.removeAll("nestedBean/strings");
509 assertXPathValueIterator(context, "nestedBean/strings", list());
510 }
511
512 @Test
513 public void testRemoveAllListElements() {
514 context.removeAll("list");
515 assertXPathValueIterator(context, "list", this instanceof DynaBeanModelTest ? list(null, null, null) : list());
516 }
517
518 @Test
519 public void testRemoveAllMapEntries() {
520 context.removeAll("map/*");
521 assertXPathValue(context, "map", Collections.EMPTY_MAP);
522 }
523
524 @Test
525 public void testRemovePathArrayElement() {
526
527 context.removePath("nestedBean/strings[1]");
528 assertEquals("String 2", context.getValue("nestedBean/strings[1]"), "Remove array element");
529 }
530
531 @Test
532 public void testRemovePathBeanValue() {
533 context.removePath("nestedBean");
534 assertNull(context.getValue("nestedBean"), "Remove collection element");
535 }
536
537 @Test
538 public void testRemovePathPropertyValue() {
539
540 context.removePath("nestedBean/int");
541 assertEquals(Integer.valueOf(0), context.getValue("nestedBean/int"), "Remove property value");
542 }
543
544 @Test
545 public void testRoot() {
546 assertXPathValueAndPointer(context, "/", context.getContextBean(), "/");
547 }
548
549 @Test
550 public void testSetCollectionElement() {
551
552 assertXPathSetValue(context, "integers[2]", Integer.valueOf(5));
553
554 assertXPathSetValue(context, "integers[2]", new int[] { 6 }, Integer.valueOf(6));
555 }
556
557 @Test
558 public void testSetContextDependentNode() {
559
560 assertXPathSetValue(context, "integers[position() = 1]", Integer.valueOf(8));
561
562 assertXPathSetValue(context, "beans[name = 'Name 1']/int", Integer.valueOf(9));
563 }
564
565 @Test
566 public void testSetNonPrimitiveValue() {
567
568 assertXPathSetValue(context, "beans[2]", null);
569
570 context.setValue("beans[2]", new NestedTestBean("Name 9"));
571 assertEquals("Name 9", context.getValue("beans[2]/name"), "Modified <" + "beans[2]/name" + ">");
572 }
573
574 @Test
575 public void testSetPropertyValue() {
576
577 assertXPathSetValue(context, "int", Integer.valueOf(2));
578
579 assertXPathSetValue(context, "int", "3", Integer.valueOf(3));
580
581 assertXPathSetValue(context, "int", new int[] { 4 }, Integer.valueOf(4));
582
583 assertXPathSetValue(context, "@int", Integer.valueOf(10));
584 }
585
586 @Test
587 public void testUnion() {
588
589 assertXPathValueIterator(context, "integers | beans[1]/strings",
590 list("String 1", "String 2", "String 3", Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4)));
591 assertXPathValue(context, "count((integers | beans[1]/strings)[contains(., '1')])", Double.valueOf(2));
592 assertXPathValue(context, "count((integers | beans[1]/strings)[name(.) = 'strings'])", Double.valueOf(3));
593
594
595 assertXPathValue(context, "(integers)[2]", Integer.valueOf(2));
596 }
597 }