View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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.assertNotEquals;
22  import static org.junit.jupiter.api.Assertions.assertNull;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.util.NoSuchElementException;
27  
28  import org.junit.jupiter.api.BeforeEach;
29  import org.junit.jupiter.api.Test;
30  
31  /**
32   * Test class for DefaultConfigurationKey.
33   */
34  public class TestDefaultConfigurationKey {
35      /** Constant for a test key. */
36      private static final String TESTPROPS = "tables.table(0).fields.field(1)";
37  
38      /** Constant for a test attribute key. */
39      private static final String TESTATTR = "[@dataType]";
40  
41      /** Constant for a complex attribute key. */
42      private static final String TESTKEY = TESTPROPS + TESTATTR;
43  
44      /** Stores the expression engine of the key to test. */
45      private DefaultExpressionEngine expressionEngine;
46  
47      /** Stores the object to be tested. */
48      private DefaultConfigurationKey key;
49  
50      /**
51       * Helper method to create a key instance with the given content.
52       *
53       * @param k the key for initialization
54       * @return the newly created {@code DefaultConfigurationKey} instance
55       */
56      private DefaultConfigurationKey key(final String k) {
57          return new DefaultConfigurationKey(expressionEngine, k);
58      }
59  
60      @BeforeEach
61      public void setUp() throws Exception {
62          expressionEngine = DefaultExpressionEngine.INSTANCE;
63          key = new DefaultConfigurationKey(expressionEngine);
64      }
65  
66      /**
67       * Returns a builder for symbols with default property settings.
68       *
69       * @return the initialized builder object
70       */
71      private DefaultExpressionEngineSymbols.Builder symbols() {
72          return new DefaultExpressionEngineSymbols.Builder(expressionEngine.getSymbols());
73      }
74  
75      /**
76       * Tests appending keys.
77       */
78      @Test
79      public void testAppend() {
80          key.append("tables").append("table(0).");
81          key.append("fields.").append("field(1)");
82          key.append(null).append(TESTATTR);
83          assertEquals(TESTKEY, key.toString());
84      }
85  
86      /**
87       * Tests appending attribute keys.
88       */
89      @Test
90      public void testAppendAttribute() {
91          key.appendAttribute("dataType");
92          assertEquals(TESTATTR, key.toString());
93      }
94  
95      /**
96       * Tests constructing a complex key by chaining multiple append operations.
97       */
98      @Test
99      public void testAppendComplexKey() {
100         key.append("tables").append("table.").appendIndex(0);
101         key.append("fields.").append("field").appendIndex(1);
102         key.appendAttribute("dataType");
103         assertEquals(TESTKEY, key.toString());
104     }
105 
106     /**
107      * Tests appending an attribute key that is already decorated-
108      */
109     @Test
110     public void testAppendDecoratedAttributeKey() {
111         key.appendAttribute(TESTATTR);
112         assertEquals(TESTATTR, key.toString());
113     }
114 
115     /**
116      * Tests appending keys that contain delimiters.
117      */
118     @Test
119     public void testAppendDelimiters() {
120         key.append("key..").append("test").append(".");
121         key.append(".more").append("..tests");
122         assertEquals("key...test.more...tests", key.toString());
123     }
124 
125     /**
126      * Tests appending keys that contain delimiters when no escaped delimiter is defined.
127      */
128     @Test
129     public void testAppendDelimitersWithoutEscaping() {
130         expressionEngine = new DefaultExpressionEngine(symbols().setEscapedDelimiter(null).create());
131         key = new DefaultConfigurationKey(expressionEngine);
132         key.append("key.......").append("test").append(".");
133         key.append(".more").append("..tests");
134         assertEquals("key.test.more.tests", key.toString());
135     }
136 
137     /**
138      * Tests appending an index to a key.
139      */
140     @Test
141     public void testAppendIndex() {
142         key.append("test").appendIndex(42);
143         assertEquals("test(42)", key.toString());
144     }
145 
146     /**
147      * Tests appending a null attribute key.
148      */
149     @Test
150     public void testAppendNullAttributeKey() {
151         key.appendAttribute(null);
152         assertEquals("", key.toString());
153     }
154 
155     /**
156      * Tests calling append with the escape flag.
157      */
158     @Test
159     public void testAppendWithEscapeFlag() {
160         key.append(".key.test.", true);
161         key.append(".more").append(".tests", true);
162         assertEquals("..key..test...more...tests", key.toString());
163     }
164 
165     /**
166      * Tests iterating over an attribute key that has an index.
167      */
168     @Test
169     public void testAttributeKeyWithIndex() {
170         key.append(TESTATTR);
171         key.appendIndex(0);
172         assertEquals(TESTATTR + "(0)", key.toString());
173 
174         final DefaultConfigurationKey.KeyIterator it = key.iterator();
175         assertTrue(it.hasNext());
176         it.next();
177         assertTrue(it.hasIndex());
178         assertEquals(0, it.getIndex());
179         assertTrue(it.isAttribute());
180         assertEquals("dataType", it.currentKey(false));
181         assertEquals(TESTATTR, it.currentKey(true));
182     }
183 
184     /**
185      * Tests determining an attribute key's name.
186      */
187     @Test
188     public void testAttributeName() {
189         assertEquals("test", key.attributeName("test"));
190         assertEquals("dataType", key.attributeName(TESTATTR));
191         assertNull(key.attributeName(null));
192     }
193 
194     /**
195      * Tests whether common key parts can be extracted.
196      */
197     @Test
198     public void testCommonKey() {
199         final DefaultConfigurationKey k1 = key(TESTKEY);
200         DefaultConfigurationKey k2 = key("tables.table(0).name");
201         DefaultConfigurationKey kc = k1.commonKey(k2);
202         assertEquals(key("tables.table(0)"), kc);
203         assertEquals(kc, k2.commonKey(k1));
204 
205         k2 = key("tables.table(1).fields.field(1)");
206         kc = k1.commonKey(k2);
207         assertEquals(key("tables"), kc);
208 
209         k2 = key("completely.different.key");
210         kc = k1.commonKey(k2);
211         assertEquals(0, kc.length());
212 
213         kc = k1.commonKey(key);
214         assertEquals(0, kc.length());
215 
216         kc = k1.commonKey(k1);
217         assertEquals(kc, k1);
218     }
219 
220     /**
221      * Tries to call commonKey() with null input.
222      */
223     @Test
224     public void testCommonKeyNull() {
225         assertThrows(IllegalArgumentException.class, () -> key.commonKey(null));
226     }
227 
228     /**
229      * Tests constructing keys for attributes.
230      */
231     @Test
232     public void testConstructAttributeKey() {
233         assertEquals(TESTATTR, key.constructAttributeKey("dataType"));
234         assertEquals(TESTATTR, key.constructAttributeKey(TESTATTR));
235         assertEquals("", key.constructAttributeKey(null));
236     }
237 
238     /**
239      * Tests constructing attribute keys when no end markers are defined. In this test case we use the property delimiter as
240      * attribute prefix.
241      */
242     @Test
243     public void testConstructAttributeKeyWithoutEndMarkers() {
244         final DefaultExpressionEngineSymbols symbols = symbols().setAttributeEnd(null).setAttributeStart(expressionEngine.getSymbols().getPropertyDelimiter())
245             .create();
246         expressionEngine = new DefaultExpressionEngine(symbols);
247         key = new DefaultConfigurationKey(expressionEngine);
248         assertEquals(".test", key.constructAttributeKey("test"));
249         assertEquals(".test", key.constructAttributeKey(".test"));
250     }
251 
252     /**
253      * Tests the differenceKey() method.
254      */
255     @Test
256     public void testDifferenceKey() {
257         final DefaultConfigurationKey k1 = key(TESTKEY);
258         DefaultConfigurationKey k2 = key("tables.table(0).name");
259         DefaultConfigurationKey kd = k1.differenceKey(k2);
260         assertEquals("name", kd.toString());
261 
262         k2 = key("tables.table(1).fields.field(1)");
263         kd = k1.differenceKey(k2);
264         assertEquals("table(1).fields.field(1)", kd.toString());
265 
266         k2 = key("completely.different.key");
267         kd = k1.differenceKey(k2);
268         assertEquals(k2, kd);
269     }
270 
271     /**
272      * Tests differenceKey() on the same object.
273      */
274     @Test
275     public void testDifferenceKeySame() {
276         final DefaultConfigurationKey k1 = key(TESTKEY);
277         final DefaultConfigurationKey kd = k1.differenceKey(k1);
278         assertEquals(0, kd.length());
279     }
280 
281     /**
282      * Tests comparing configuration keys.
283      */
284     @Test
285     public void testEquals() {
286         final DefaultConfigurationKey k1 = key(TESTKEY);
287         assertEquals(k1, k1);
288         final DefaultConfigurationKey k2 = key(TESTKEY);
289         assertEquals(k1, k2);
290         assertEquals(k2, k1);
291         assertEquals(k1.hashCode(), k2.hashCode());
292         k2.append("anotherPart");
293         assertNotEquals(k1, k2);
294         assertNotEquals(k2, k1);
295         assertNotEquals(null, k1);
296         assertNotEquals(TESTKEY, k1);
297     }
298 
299     /**
300      * Tests the isAttributeKey() method with several keys.
301      */
302     @Test
303     public void testIsAttributeKey() {
304         assertTrue(key.isAttributeKey(TESTATTR));
305         assertFalse(key.isAttributeKey(TESTPROPS));
306         assertFalse(key.isAttributeKey(null));
307     }
308 
309     /**
310      * Tests if attribute keys are correctly detected if no end markers are set. (In this test case we use the same
311      * delimiter for attributes as for simple properties.)
312      */
313     @Test
314     public void testIsAttributeKeyWithoutEndMarkers() {
315         final DefaultExpressionEngineSymbols symbols = symbols().setAttributeEnd(null)
316             .setAttributeStart(DefaultExpressionEngineSymbols.DEFAULT_PROPERTY_DELIMITER).create();
317         expressionEngine = new DefaultExpressionEngine(symbols);
318         key = new DefaultConfigurationKey(expressionEngine);
319         assertTrue(key.isAttributeKey(DefaultExpressionEngineSymbols.DEFAULT_PROPERTY_DELIMITER + "test"));
320         assertFalse(key.isAttributeKey(TESTATTR));
321     }
322 
323     /**
324      * Tests to iterate over a simple key.
325      */
326     @Test
327     public void testIterate() {
328         key.append(TESTKEY);
329         final DefaultConfigurationKey.KeyIterator it = key.iterator();
330         assertTrue(it.hasNext());
331         assertEquals("tables", it.nextKey());
332         assertEquals("table", it.nextKey());
333         assertTrue(it.hasIndex());
334         assertEquals(0, it.getIndex());
335         assertEquals("fields", it.nextKey());
336         assertFalse(it.hasIndex());
337         assertEquals("field", it.nextKey(true));
338         assertEquals(1, it.getIndex());
339         assertFalse(it.isAttribute());
340         assertEquals("field", it.currentKey(true));
341         assertEquals("dataType", it.nextKey());
342         assertEquals("[@dataType]", it.currentKey(true));
343         assertTrue(it.isAttribute());
344         assertFalse(it.hasNext());
345         assertThrows(NoSuchElementException.class, it::next);
346     }
347 
348     /**
349      * Tests iterating over keys when a different escaped delimiter is used.
350      */
351     @Test
352     public void testIterateAlternativeEscapeDelimiter() {
353         expressionEngine = new DefaultExpressionEngine(symbols().setEscapedDelimiter("\\.").create());
354         key = new DefaultConfigurationKey(expressionEngine);
355         key.append("\\.my\\.elem");
356         key.append("trailing\\.dot\\.");
357         key.append(".strange");
358         assertEquals("\\.my\\.elem.trailing\\.dot\\..strange", key.toString());
359         final DefaultConfigurationKey.KeyIterator kit = key.iterator();
360         assertEquals(".my.elem", kit.nextKey());
361         assertEquals("trailing.dot.", kit.nextKey());
362         assertEquals("strange", kit.nextKey());
363         assertFalse(kit.hasNext());
364     }
365 
366     /**
367      * Tests iteration when the attribute markers equals the property delimiter.
368      */
369     @Test
370     public void testIterateAttributeEqualsPropertyDelimiter() {
371         expressionEngine = new DefaultExpressionEngine(
372             symbols().setAttributeEnd(null).setAttributeStart(DefaultExpressionEngineSymbols.DEFAULT_PROPERTY_DELIMITER).create());
373         key = new DefaultConfigurationKey(expressionEngine);
374         key.append("this.isa.key");
375         final DefaultConfigurationKey.KeyIterator kit = key.iterator();
376         assertEquals("this", kit.next());
377         assertFalse(kit.isAttribute());
378         assertTrue(kit.isPropertyKey());
379         assertEquals("isa", kit.next());
380         assertFalse(kit.isAttribute());
381         assertTrue(kit.isPropertyKey());
382         assertEquals("key", kit.next());
383         assertTrue(kit.isAttribute());
384         assertTrue(kit.isPropertyKey());
385         assertEquals("key", kit.currentKey(true));
386     }
387 
388     /**
389      * Tests iterating over keys with escaped delimiters.
390      */
391     @Test
392     public void testIterateEscapedDelimiters() {
393         key.append("my..elem");
394         key.append("trailing..dot..");
395         key.append(".strange");
396         assertEquals("my..elem.trailing..dot...strange", key.toString());
397         final DefaultConfigurationKey.KeyIterator kit = key.iterator();
398         assertEquals("my.elem", kit.nextKey());
399         assertEquals("trailing.dot.", kit.nextKey());
400         assertEquals("strange", kit.nextKey());
401         assertFalse(kit.hasNext());
402     }
403 
404     /**
405      * Tests iterating over some funny keys.
406      */
407     @Test
408     public void testIterateStrangeKeys() {
409         key = new DefaultConfigurationKey(expressionEngine, "key.");
410         DefaultConfigurationKey.KeyIterator it = key.iterator();
411         assertTrue(it.hasNext());
412         assertEquals("key", it.next());
413         assertFalse(it.hasNext());
414 
415         key = new DefaultConfigurationKey(expressionEngine, ".");
416         it = key.iterator();
417         assertFalse(it.hasNext());
418 
419         key = new DefaultConfigurationKey(expressionEngine, "key().index()undefined(0).test");
420         it = key.iterator();
421         assertEquals("key()", it.next());
422         assertFalse(it.hasIndex());
423         assertEquals("index()undefined", it.nextKey(false));
424         assertTrue(it.hasIndex());
425         assertEquals(0, it.getIndex());
426     }
427 
428     /**
429      * Tests whether a key with brackets in it can be iterated over.
430      */
431     @Test
432     public void testIterateWithBrackets() {
433         key.append("directory.platform(x86).path");
434         final DefaultConfigurationKey.KeyIterator kit = key.iterator();
435         String part = kit.nextKey();
436         assertEquals("directory", part);
437         assertFalse(kit.hasIndex());
438         part = kit.nextKey();
439         assertEquals("platform(x86)", part);
440         assertFalse(kit.hasIndex());
441         part = kit.nextKey();
442         assertEquals("path", part);
443         assertFalse(kit.hasIndex());
444         assertFalse(kit.hasNext());
445     }
446 
447     /**
448      * Tests iterating when no escape delimiter is defined.
449      */
450     @Test
451     public void testIterateWithoutEscapeDelimiter() {
452         expressionEngine = new DefaultExpressionEngine(symbols().setEscapedDelimiter(null).create());
453         key = new DefaultConfigurationKey(expressionEngine);
454         key.append("..my..elem.trailing..dot...strange");
455         assertEquals("my..elem.trailing..dot...strange", key.toString());
456         final DefaultConfigurationKey.KeyIterator kit = key.iterator();
457         final String[] parts = {"my", "elem", "trailing", "dot", "strange"};
458         for (int i = 0; i < parts.length; i++) {
459             assertEquals(parts[i], kit.next(), "Wrong key part " + i);
460         }
461         assertFalse(kit.hasNext());
462     }
463 
464     /**
465      * Tests an iteration where the remove() method is called. This is not supported.
466      */
467     @Test
468     public void testIterateWithRemove() {
469         assertFalse(key.iterator().hasNext());
470         key.append("simple");
471         final DefaultConfigurationKey.KeyIterator it = key.iterator();
472         assertTrue(it.hasNext());
473         assertEquals("simple", it.next());
474         assertThrows(UnsupportedOperationException.class, it::remove);
475     }
476 
477     /**
478      * Tests getting and setting the key's length.
479      */
480     @Test
481     public void testLength() {
482         key.append(TESTPROPS);
483         assertEquals(TESTPROPS.length(), key.length());
484         key.appendAttribute("dataType");
485         assertEquals(TESTKEY.length(), key.length());
486         key.setLength(TESTPROPS.length());
487         assertEquals(TESTPROPS.length(), key.length());
488         assertEquals(TESTPROPS, key.toString());
489     }
490 
491     /**
492      * Tests setting the expression engine to null. This should not be allowed.
493      */
494     @Test
495     public void testSetNullExpressionEngine() {
496         assertThrows(IllegalArgumentException.class, () -> new DefaultConfigurationKey(null));
497     }
498 
499     /**
500      * Tests removing delimiters.
501      */
502     @Test
503     public void testTrim() {
504         assertEquals("test", key.trim(".test."));
505         assertEquals("", key.trim(null));
506         assertEquals("", key.trim(DefaultExpressionEngineSymbols.DEFAULT_PROPERTY_DELIMITER));
507     }
508 
509     /**
510      * Tests removing leading delimiters.
511      */
512     @Test
513     public void testTrimLeft() {
514         assertEquals("test.", key.trimLeft(".test."));
515         assertEquals("..test.", key.trimLeft("..test."));
516     }
517 
518     /**
519      * Tests removing trailing delimiters.
520      */
521     @Test
522     public void testTrimRight() {
523         assertEquals(".test", key.trimRight(".test."));
524         assertEquals(".test..", key.trimRight(".test.."));
525     }
526 }