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