1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration2.tree;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertFalse;
21 import static org.junit.jupiter.api.Assertions.assertSame;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24
25 import java.util.Arrays;
26 import java.util.List;
27
28 import org.apache.commons.lang3.StringUtils;
29 import org.junit.jupiter.api.BeforeAll;
30 import org.junit.jupiter.api.BeforeEach;
31 import org.junit.jupiter.api.Test;
32
33
34
35
36 public class TestDefaultExpressionEngine {
37
38
39 private static final String[] TABLES = {"users", "documents"};
40
41
42 private static final String[] TAB_TYPES = {"system", "application"};
43
44
45 private static final String[][] FIELDS = {{"uid", "uname", "firstName", "lastName", "email"}, {"docid", "name", "creationDate", "authorID", "version"}};
46
47
48 private static ImmutableNode root;
49
50
51 private static NodeHandler<ImmutableNode> handler;
52
53
54
55
56
57
58
59 private static ImmutableNode createFieldNode(final String name) {
60 final ImmutableNode.Builder nodeFieldBuilder = new ImmutableNode.Builder(1);
61 nodeFieldBuilder.addChild(createNode("name", name));
62 return nodeFieldBuilder.name("field").create();
63 }
64
65
66
67
68
69
70
71
72 private static ImmutableNode createNode(final String name, final Object value) {
73 return new ImmutableNode.Builder().name(name).value(value).create();
74 }
75
76 @BeforeAll
77 public static void setUpBeforeClass() {
78 root = setUpNodes();
79 handler = new InMemoryNodeModel(root).getNodeHandler();
80 }
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 private static ImmutableNode setUpNodes() {
99 final ImmutableNode.Builder nodeTablesBuilder = new ImmutableNode.Builder(TABLES.length);
100 nodeTablesBuilder.name("tables");
101 for (int i = 0; i < TABLES.length; i++) {
102 final ImmutableNode.Builder nodeTableBuilder = new ImmutableNode.Builder(2);
103 nodeTableBuilder.name("table");
104 nodeTableBuilder.addChild(new ImmutableNode.Builder().name("name").value(TABLES[i]).create());
105 nodeTableBuilder.addAttribute("type", TAB_TYPES[i]);
106
107 final ImmutableNode.Builder nodeFieldsBuilder = new ImmutableNode.Builder(FIELDS[i].length);
108 for (int j = 0; j < FIELDS[i].length; j++) {
109 nodeFieldsBuilder.addChild(createFieldNode(FIELDS[i][j]));
110 }
111 nodeTableBuilder.addChild(nodeFieldsBuilder.name("fields").create());
112 nodeTablesBuilder.addChild(nodeTableBuilder.create());
113 }
114
115 final ImmutableNode.Builder rootBuilder = new ImmutableNode.Builder();
116 rootBuilder.addChild(nodeTablesBuilder.create());
117 final ImmutableNode.Builder nodeConnBuilder = new ImmutableNode.Builder();
118 nodeConnBuilder.name("connection.settings");
119 nodeConnBuilder.addChild(createNode("usr.name", "scott"));
120 nodeConnBuilder.addChild(createNode("usr.pwd", "tiger"));
121 rootBuilder.addAttribute("test", "true");
122 rootBuilder.addChild(nodeConnBuilder.create());
123
124 return rootBuilder.create();
125 }
126
127
128 private DefaultExpressionEngine engine;
129
130
131
132
133
134
135
136
137 private void checkAttributeValue(final String key, final String attr, final Object expValue) {
138 final List<QueryResult<ImmutableNode>> results = checkKey(key, attr, 1);
139 final QueryResult<ImmutableNode> result = results.get(0);
140 assertTrue(result.isAttributeResult());
141 assertEquals(expValue, result.getAttributeValue(handler), "Wrong attribute value for key " + key);
142 }
143
144
145
146
147
148
149
150
151
152
153 private List<QueryResult<ImmutableNode>> checkKey(final String key, final String name, final int count) {
154 final List<QueryResult<ImmutableNode>> nodes = query(key, count);
155 for (final QueryResult<ImmutableNode> result : nodes) {
156 if (result.isAttributeResult()) {
157 assertEquals(name, result.getAttributeName(), "Wrong attribute name for key " + key);
158 } else {
159 assertEquals(name, result.getNode().getNodeName(), "Wrong result node for key " + key);
160 }
161 }
162 return nodes;
163 }
164
165
166
167
168
169
170
171
172
173 private void checkKeyValue(final String key, final String name, final String value) {
174 final List<QueryResult<ImmutableNode>> results = checkKey(key, name, 1);
175 final QueryResult<ImmutableNode> result = results.get(0);
176 assertFalse(result.isAttributeResult());
177 assertEquals(value, result.getNode().getValue(), "Wrong value for key " + key);
178 }
179
180
181
182
183
184
185
186 private void checkNodePath(final NodeAddData<ImmutableNode> data, final String... expected) {
187 assertEquals(Arrays.asList(expected), data.getPathNodes());
188 }
189
190
191
192
193
194
195 private void checkQueryRootNode(final String key) {
196 final List<QueryResult<ImmutableNode>> results = checkKey(key, null, 1);
197 final QueryResult<ImmutableNode> result = results.get(0);
198 assertFalse(result.isAttributeResult());
199 assertSame(root, result.getNode());
200 }
201
202
203
204
205
206
207
208 private ImmutableNode fetchNode(final String key) {
209 final QueryResult<ImmutableNode> result = query(key, 1).get(0);
210 assertFalse(result.isAttributeResult());
211 return result.getNode();
212 }
213
214
215
216
217
218
219
220
221 private List<QueryResult<ImmutableNode>> query(final String key, final int expCount) {
222 final List<QueryResult<ImmutableNode>> nodes = engine.query(root, key, handler);
223 assertEquals(expCount, nodes.size());
224 return nodes;
225 }
226
227 @BeforeEach
228 public void setUp() throws Exception {
229 engine = DefaultExpressionEngine.INSTANCE;
230 }
231
232
233
234
235
236 private void setUpAlternativeMatcher() {
237 final NodeMatcher<String> matcher = new NodeMatcher<String>() {
238 @Override
239 public <T> boolean matches(final T node, final NodeHandler<T> handler, final String criterion) {
240 return handler.nodeName(node).equals(StringUtils.remove(criterion, '_'));
241 }
242 };
243 engine = new DefaultExpressionEngine(engine.getSymbols(), matcher);
244 }
245
246
247
248
249 private void setUpAlternativeSyntax() {
250 final DefaultExpressionEngineSymbols symbols = new DefaultExpressionEngineSymbols.Builder().setAttributeEnd(null).setAttributeStart("@")
251 .setPropertyDelimiter("/").setEscapedDelimiter(null).setIndexStart("[").setIndexEnd("]").create();
252 engine = new DefaultExpressionEngine(symbols);
253 }
254
255
256
257
258 @Test
259 public void testAttributeKey() {
260 assertEquals("tables.table[@type]", engine.attributeKey("tables.table", "type"));
261 }
262
263
264
265
266 @Test
267 public void testAttributeKeyNoParent() {
268 assertEquals("[@test]", engine.attributeKey(null, "test"));
269 }
270
271
272
273
274 @Test
275 public void testAttributeKeyRoot() {
276 assertEquals("[@test]", engine.attributeKey("", "test"));
277 }
278
279
280
281
282 @Test
283 public void testAttributeKeyWithAlternativeSyntax() {
284 setUpAlternativeSyntax();
285 assertEquals("@test", engine.attributeKey("", "test"));
286 }
287
288
289
290
291 @Test
292 public void testCanonicalKeyNoDuplicates() {
293 final ImmutableNode node = fetchNode("tables.table(0).name");
294 assertEquals("table.name(0)", engine.canonicalKey(node, "table", handler));
295 }
296
297
298
299
300 @Test
301 public void testCanonicalKeyNoParentKey() {
302 final ImmutableNode node = fetchNode("tables.table(0).fields.field(1).name");
303 assertEquals("name(0)", engine.canonicalKey(node, null, handler));
304 }
305
306
307
308
309 @Test
310 public void testCanonicalKeyRootNoParentKey() {
311 assertEquals("", engine.canonicalKey(root, null, handler));
312 }
313
314
315
316
317 @Test
318 public void testCanonicalKeyRootWithParentKey() {
319 assertEquals("parent", engine.canonicalKey(root, "parent", handler));
320 }
321
322
323
324
325 @Test
326 public void testCanonicalKeyWithDuplicates() {
327 final ImmutableNode tab1 = fetchNode("tables.table(0)");
328 final ImmutableNode tab2 = fetchNode("tables.table(1)");
329 assertEquals("tables.table(0)", engine.canonicalKey(tab1, "tables", handler));
330 assertEquals("tables.table(1)", engine.canonicalKey(tab2, "tables", handler));
331 }
332
333
334
335
336 @Test
337 public void testDefaultSymbols() {
338 assertSame(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS, engine.getSymbols());
339 }
340
341
342
343
344 @Test
345 public void testInitNoSymbols() {
346 assertThrows(IllegalArgumentException.class, () -> new DefaultExpressionEngine(null));
347 }
348
349
350
351
352 @Test
353 public void testNodeKey() {
354 final ImmutableNode node = root.getChildren().get(0);
355 assertEquals("tables", engine.nodeKey(node, "", handler));
356 assertEquals("test.tables", engine.nodeKey(node, "test", handler));
357 assertEquals("a.full.parent.key.tables", engine.nodeKey(node, "a.full.parent.key", handler));
358 }
359
360
361
362
363 @Test
364 public void testNodeKeyWithAlternativeSyntax() {
365 setUpAlternativeSyntax();
366 assertEquals("tables/table", engine.nodeKey(root.getChildren().get(0).getChildren().get(0), "tables", handler));
367 }
368
369
370
371
372
373 @Test
374 public void testNodeKeyWithAlternativeSyntaxAttributePropertyDelimiter() {
375 setUpAlternativeSyntax();
376 final DefaultExpressionEngineSymbols symbols = new DefaultExpressionEngineSymbols.Builder(engine.getSymbols())
377 .setAttributeStart(engine.getSymbols().getPropertyDelimiter()).create();
378 engine = new DefaultExpressionEngine(symbols);
379 assertEquals("/test", engine.attributeKey("", "test"));
380 }
381
382
383
384
385 @Test
386 public void testNodeKeyWithEscapedDelimiters() {
387 final ImmutableNode node = root.getChildren().get(1);
388 assertEquals("connection..settings", engine.nodeKey(node, "", handler));
389 assertEquals("connection..settings.usr..name", engine.nodeKey(node.getChildren().get(0), engine.nodeKey(node, "", handler), handler));
390 }
391
392
393
394
395 @Test
396 public void testNodeKeyWithRoot() {
397 assertEquals("", engine.nodeKey(root, null, handler));
398 assertEquals("test", engine.nodeKey(root, "test", handler));
399 }
400
401
402
403
404 @Test
405 public void testPrepareAddAttribute() {
406 final NodeAddData<ImmutableNode> data = engine.prepareAdd(root, "tables.table(0)[@tableSpace]", handler);
407 assertEquals(TABLES[0], data.getParent().getChildren().get(0).getValue());
408 assertEquals("tableSpace", data.getNewNodeName());
409 assertTrue(data.isAttribute());
410 assertTrue(data.getPathNodes().isEmpty());
411 }
412
413
414
415
416 @Test
417 public void testPrepareAddAttributeRoot() {
418 final NodeAddData<ImmutableNode> data = engine.prepareAdd(root, "[@newAttr]", handler);
419 assertSame(root, data.getParent());
420 assertEquals("newAttr", data.getNewNodeName());
421 assertTrue(data.isAttribute());
422 }
423
424
425
426
427 @Test
428 public void testPrepareAddDirectly() {
429 NodeAddData<ImmutableNode> data = engine.prepareAdd(root, "newNode", handler);
430 assertSame(root, data.getParent());
431 assertTrue(data.getPathNodes().isEmpty());
432 assertEquals("newNode", data.getNewNodeName());
433 assertFalse(data.isAttribute());
434
435 data = engine.prepareAdd(root, "tables.table.fields.field.name", handler);
436 assertEquals("name", data.getNewNodeName());
437 assertTrue(data.getPathNodes().isEmpty());
438 assertEquals("field", data.getParent().getNodeName());
439 final ImmutableNode nd = data.getParent().getChildren().get(0);
440 assertEquals("name", nd.getNodeName());
441 assertEquals("version", nd.getValue());
442 }
443
444 @Test
445 public void testPrepareAddEmptyKey() {
446 assertThrows(IllegalArgumentException.class, () -> engine.prepareAdd(root, "", handler));
447 }
448
449
450
451
452 @Test
453 public void testPrepareAddInvalidKey() {
454 assertThrows(IllegalArgumentException.class, () -> engine.prepareAdd(root, "tables.table(0)[@type].new", handler));
455 }
456
457 @Test
458 public void testPrepareAddInvalidKeyAttribute() {
459 assertThrows(IllegalArgumentException.class,
460 () -> engine.prepareAdd(root, "a.complete.new.path.with.an[@attribute].at.a.non.allowed[@position]", handler));
461 }
462
463 @Test
464 public void testPrepareAddNullKey() {
465 assertThrows(IllegalArgumentException.class, () -> engine.prepareAdd(root, null, handler));
466 }
467
468
469
470
471 @Test
472 public void testPrepareAddWithAlternativeMatcher() {
473 setUpAlternativeMatcher();
474 final NodeAddData<ImmutableNode> data = engine.prepareAdd(root, "tables_.table._fields__._field.name", handler);
475 assertEquals("name", data.getNewNodeName());
476 assertTrue(data.getPathNodes().isEmpty());
477 }
478
479
480
481
482 @Test
483 public void testPrepareAddWithAlternativeSyntax() {
484 setUpAlternativeSyntax();
485 NodeAddData<ImmutableNode> data = engine.prepareAdd(root, "tables/table[0]/test", handler);
486 assertEquals("test", data.getNewNodeName());
487 assertFalse(data.isAttribute());
488 assertEquals(TABLES[0], data.getParent().getChildren().get(0).getValue());
489
490 data = engine.prepareAdd(root, "a/complete/new/path@attr", handler);
491 assertEquals("attr", data.getNewNodeName());
492 checkNodePath(data, "a", "complete", "new", "path");
493 assertSame(root, data.getParent());
494 }
495
496
497
498
499 @Test
500 public void testPrepareAddWithIndex() {
501 NodeAddData<ImmutableNode> data = engine.prepareAdd(root, "tables.table(0).tableSpace", handler);
502 assertEquals("tableSpace", data.getNewNodeName());
503 assertTrue(data.getPathNodes().isEmpty());
504 assertEquals("table", data.getParent().getNodeName());
505 final ImmutableNode node = data.getParent().getChildren().get(0);
506 assertEquals(TABLES[0], node.getValue());
507
508 data = engine.prepareAdd(root, "tables.table(1).fields.field(2).alias", handler);
509 assertEquals("alias", data.getNewNodeName());
510 assertEquals("field", data.getParent().getNodeName());
511 assertEquals("creationDate", data.getParent().getChildren().get(0).getValue());
512 }
513
514
515
516
517 @Test
518 public void testPrepareAddWithPath() {
519 NodeAddData<ImmutableNode> data = engine.prepareAdd(root, "tables.table(1).fields.field(-1).name", handler);
520 assertEquals("name", data.getNewNodeName());
521 checkNodePath(data, "field");
522 assertEquals("fields", data.getParent().getNodeName());
523
524 data = engine.prepareAdd(root, "tables.table(-1).name", handler);
525 assertEquals("name", data.getNewNodeName());
526 checkNodePath(data, "table");
527 assertEquals("tables", data.getParent().getNodeName());
528
529 data = engine.prepareAdd(root, "a.complete.new.path", handler);
530 assertEquals("path", data.getNewNodeName());
531 checkNodePath(data, "a", "complete", "new");
532 assertSame(root, data.getParent());
533 }
534
535
536
537
538
539 @Test
540 public void testPrepareAddWithSameAttributeDelimiter() {
541 final DefaultExpressionEngineSymbols symbols = new DefaultExpressionEngineSymbols.Builder(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS)
542 .setAttributeEnd(null).setAttributeStart(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS.getPropertyDelimiter()).create();
543 engine = new DefaultExpressionEngine(symbols);
544
545 NodeAddData<ImmutableNode> data = engine.prepareAdd(root, "tables.table(0).test", handler);
546 assertEquals("test", data.getNewNodeName());
547 assertFalse(data.isAttribute());
548 assertEquals("table", data.getParent().getNodeName());
549
550 data = engine.prepareAdd(root, "a.complete.new.path", handler);
551 assertFalse(data.isAttribute());
552 checkNodePath(data, "a", "complete", "new");
553 }
554
555
556
557
558 @Test
559 public void testQueryAlternativeSyntax() {
560 setUpAlternativeSyntax();
561 checkKeyValue("tables/table[1]/name", "name", TABLES[1]);
562 checkAttributeValue("tables/table[0]@type", "type", TAB_TYPES[0]);
563 checkAttributeValue("@test", "test", "true");
564 checkKeyValue("connection.settings/usr.name", "usr.name", "scott");
565 }
566
567
568
569
570 @Test
571 public void testQueryAttributeEmulation() {
572 final DefaultExpressionEngineSymbols symbols = new DefaultExpressionEngineSymbols.Builder(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS)
573 .setAttributeEnd(null).setAttributeStart(DefaultExpressionEngineSymbols.DEFAULT_PROPERTY_DELIMITER).create();
574 engine = new DefaultExpressionEngine(symbols);
575 checkKeyValue("tables.table(0).name", "name", TABLES[0]);
576 checkAttributeValue("tables.table(0).type", "type", TAB_TYPES[0]);
577 checkKey("tables.table.type", "type", 2);
578 }
579
580
581
582
583 @Test
584 public void testQueryEscapedKeys() {
585 checkKeyValue("connection..settings.usr..name", "usr.name", "scott");
586 checkKeyValue("connection..settings.usr..pwd", "usr.pwd", "tiger");
587 }
588
589
590
591
592 @Test
593 public void testQueryKeys() {
594 checkKey("tables.table.name", "name", 2);
595 checkKey("tables.table.fields.field.name", "name", 10);
596 checkKey("tables.table[@type]", "type", 2);
597 checkKey("tables.table(0).fields.field.name", "name", 5);
598 checkKey("tables.table(1).fields.field.name", "name", 5);
599 checkKey("tables.table.fields.field(1).name", "name", 2);
600 }
601
602
603
604
605 @Test
606 public void testQueryKeyWithAlternativeMatcher() {
607 setUpAlternativeMatcher();
608 checkKey("tables_._table_.name_", "name", 2);
609 }
610
611
612
613
614 @Test
615 public void testQueryNodes() {
616 for (int i = 0; i < TABLES.length; i++) {
617 checkKeyValue("tables.table(" + i + ").name", "name", TABLES[i]);
618 checkAttributeValue("tables.table(" + i + ")[@type]", "type", TAB_TYPES[i]);
619
620 for (int j = 0; j < FIELDS[i].length; j++) {
621 checkKeyValue("tables.table(" + i + ").fields.field(" + j + ").name", "name", FIELDS[i][j]);
622 }
623 }
624 }
625
626
627
628
629 @Test
630 public void testQueryNonExistingKeys() {
631 checkKey("tables.tablespace.name", null, 0);
632 checkKey("tables.table(2).name", null, 0);
633 checkKey("a complete unknown key", null, 0);
634 checkKey("tables.table(0).fields.field(-1).name", null, 0);
635 checkKey("tables.table(0).fields.field(28).name", null, 0);
636 checkKey("tables.table(0).fields.field().name", null, 0);
637 checkKey("connection.settings.usr.name", null, 0);
638 checkKey("tables.table(0)[@type].additional", null, 0);
639 }
640
641
642
643
644 @Test
645 public void testQueryRootAttribute() {
646 checkAttributeValue("[@test]", "test", "true");
647 }
648
649
650
651
652 @Test
653 public void testQueryRootNodeEmptyKey() {
654 checkQueryRootNode("");
655 }
656
657
658
659
660 @Test
661 public void testQueryRootNodeNullKey() {
662 checkQueryRootNode(null);
663 }
664 }