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.configuration.tree;
18  
19  import static org.junit.Assert.assertEquals;
20  import static org.junit.Assert.assertFalse;
21  import static org.junit.Assert.assertSame;
22  import static org.junit.Assert.assertTrue;
23  
24  import java.util.Iterator;
25  import java.util.List;
26  
27  import org.junit.Before;
28  import org.junit.Test;
29  
30  /**
31   * Test class for DefaultExpressionEngine.
32   *
33   * @author <a
34   * href="http://commons.apache.org/configuration/team-list.html">Commons
35   * Configuration team</a>
36   * @version $Id: TestDefaultExpressionEngine.java 1225918 2011-12-30 20:54:47Z oheger $
37   */
38  public class TestDefaultExpressionEngine
39  {
40      /** Stores the names of the test nodes representing tables. */
41      private static String[] tables =
42      { "users", "documents"};
43  
44      /** Stores the types of the test table nodes. */
45      private static String[] tabTypes =
46      { "system", "application"};
47  
48      /** Test data fields for the node hierarchy. */
49      private static String[][] fields =
50      {
51      { "uid", "uname", "firstName", "lastName", "email"},
52      { "docid", "name", "creationDate", "authorID", "version"}};
53  
54      /** The object to be tested. */
55      DefaultExpressionEngine engine;
56  
57      /** The root of a hierarchy with configuration nodes. */
58      ConfigurationNode root;
59  
60      @Before
61      public void setUp() throws Exception
62      {
63          root = setUpNodes();
64          engine = new DefaultExpressionEngine();
65      }
66  
67      /**
68       * Tests some simple queries.
69       */
70      @Test
71      public void testQueryKeys()
72      {
73          checkKey("tables.table.name", "name", 2);
74          checkKey("tables.table.fields.field.name", "name", 10);
75          checkKey("tables.table[@type]", "type", 2);
76          checkKey("tables.table(0).fields.field.name", "name", 5);
77          checkKey("tables.table(1).fields.field.name", "name", 5);
78          checkKey("tables.table.fields.field(1).name", "name", 2);
79      }
80  
81      /**
82       * Performs some queries and evaluates the values of the result nodes.
83       */
84      @Test
85      public void testQueryNodes()
86      {
87          for (int i = 0; i < tables.length; i++)
88          {
89              checkKeyValue("tables.table(" + i + ").name", "name", tables[i]);
90              checkKeyValue("tables.table(" + i + ")[@type]", "type", tabTypes[i]);
91  
92              for (int j = 0; j < fields[i].length; j++)
93              {
94                  checkKeyValue("tables.table(" + i + ").fields.field(" + j
95                          + ").name", "name", fields[i][j]);
96              }
97          }
98      }
99  
100     /**
101      * Tests querying keys that do not exist.
102      */
103     @Test
104     public void testQueryNonExistingKeys()
105     {
106         checkKey("tables.tablespace.name", null, 0);
107         checkKey("tables.table(2).name", null, 0);
108         checkKey("a complete unknown key", null, 0);
109         checkKey("tables.table(0).fields.field(-1).name", null, 0);
110         checkKey("tables.table(0).fields.field(28).name", null, 0);
111         checkKey("tables.table(0).fields.field().name", null, 0);
112         checkKey("connection.settings.usr.name", null, 0);
113     }
114 
115     /**
116      * Tests querying nodes whose names contain a delimiter.
117      */
118     @Test
119     public void testQueryEscapedKeys()
120     {
121         checkKeyValue("connection..settings.usr..name", "usr.name", "scott");
122         checkKeyValue("connection..settings.usr..pwd", "usr.pwd", "tiger");
123     }
124 
125     /**
126      * Tests some queries when the same delimiter is used for properties and
127      * attributes.
128      */
129     @Test
130     public void testQueryAttributeEmulation()
131     {
132         engine.setAttributeEnd(null);
133         engine.setAttributeStart(engine.getPropertyDelimiter());
134         checkKeyValue("tables.table(0).name", "name", tables[0]);
135         checkKeyValue("tables.table(0).type", "type", tabTypes[0]);
136         checkKey("tables.table.type", "type", 2);
137     }
138 
139     /**
140      * Tests accessing the root node.
141      */
142     @Test
143     public void testQueryRootNode()
144     {
145         List<ConfigurationNode> nodes = checkKey(null, null, 1);
146         assertSame("Root node not found", root, nodes.get(0));
147         nodes = checkKey("", null, 1);
148         assertSame("Root node not found", root, nodes.get(0));
149         checkKeyValue("[@test]", "test", "true");
150     }
151 
152     /**
153      * Tests a different query syntax. Sets other strings for the typical tokens
154      * used by the expression engine.
155      */
156     @Test
157     public void testQueryAlternativeSyntax()
158     {
159         setUpAlternativeSyntax();
160         checkKeyValue("tables/table[1]/name", "name", tables[1]);
161         checkKeyValue("tables/table[0]@type", "type", tabTypes[0]);
162         checkKeyValue("@test", "test", "true");
163         checkKeyValue("connection.settings/usr.name", "usr.name", "scott");
164     }
165 
166     /**
167      * Tests obtaining keys for nodes.
168      */
169     @Test
170     public void testNodeKey()
171     {
172         ConfigurationNode node = root.getChild(0);
173         assertEquals("Invalid name for descendant of root", "tables", engine
174                 .nodeKey(node, ""));
175         assertEquals("Parent key not respected", "test.tables", engine.nodeKey(
176                 node, "test"));
177         assertEquals("Full parent key not taken into account",
178                 "a.full.parent.key.tables", engine.nodeKey(node,
179                         "a.full.parent.key"));
180     }
181 
182     /**
183      * Tests obtaining keys when the root node is involved.
184      */
185     @Test
186     public void testNodeKeyWithRoot()
187     {
188         assertEquals("Wrong name for root noot", "", engine.nodeKey(root, null));
189         assertEquals("Null name not detected", "test", engine.nodeKey(root,
190                 "test"));
191     }
192 
193     /**
194      * Tests obtaining keys for attribute nodes.
195      */
196     @Test
197     public void testNodeKeyWithAttribute()
198     {
199         ConfigurationNode node = root.getChild(0).getChild(0).getAttribute(0);
200         assertEquals("Wrong attribute node", "type", node.getName());
201         assertEquals("Wrong attribute key", "tables.table[@type]", engine
202                 .nodeKey(node, "tables.table"));
203         assertEquals("Wrong key for root attribute", "[@test]", engine.nodeKey(
204                 root.getAttribute(0), ""));
205     }
206 
207     /**
208      * Tests obtaining keys for nodes that contain the delimiter character.
209      */
210     @Test
211     public void testNodeKeyWithEscapedDelimiters()
212     {
213         ConfigurationNode node = root.getChild(1);
214         assertEquals("Wrong escaped key", "connection..settings", engine
215                 .nodeKey(node, ""));
216         assertEquals("Wrong complex escaped key",
217                 "connection..settings.usr..name", engine.nodeKey(node
218                         .getChild(0), engine.nodeKey(node, "")));
219     }
220 
221     /**
222      * Tests obtaining node keys when a different syntax is set.
223      */
224     @Test
225     public void testNodeKeyWithAlternativeSyntax()
226     {
227         setUpAlternativeSyntax();
228         assertEquals("Wrong child key", "tables/table", engine.nodeKey(root
229                 .getChild(0).getChild(0), "tables"));
230         assertEquals("Wrong attribute key", "@test", engine.nodeKey(root
231                 .getAttribute(0), ""));
232 
233         engine.setAttributeStart(engine.getPropertyDelimiter());
234         assertEquals("Wrong attribute key", "/test", engine.nodeKey(root
235                 .getAttribute(0), ""));
236     }
237 
238     /**
239      * Tests adding direct child nodes to the existing hierarchy.
240      */
241     @Test
242     public void testPrepareAddDirectly()
243     {
244         NodeAddData data = engine.prepareAdd(root, "newNode");
245         assertSame("Wrong parent node", root, data.getParent());
246         assertTrue("Path nodes available", data.getPathNodes().isEmpty());
247         assertEquals("Wrong name of new node", "newNode", data.getNewNodeName());
248         assertFalse("New node is an attribute", data.isAttribute());
249 
250         data = engine.prepareAdd(root, "tables.table.fields.field.name");
251         assertEquals("Wrong name of new node", "name", data.getNewNodeName());
252         assertTrue("Path nodes available", data.getPathNodes().isEmpty());
253         assertEquals("Wrong parent node", "field", data.getParent().getName());
254         ConfigurationNode nd = data.getParent().getChild(0);
255         assertEquals("Field has no name node", "name", nd.getName());
256         assertEquals("Incorrect name", "version", nd.getValue());
257     }
258 
259     /**
260      * Tests adding when indices are involved.
261      */
262     @Test
263     public void testPrepareAddWithIndex()
264     {
265         NodeAddData data = engine
266                 .prepareAdd(root, "tables.table(0).tableSpace");
267         assertEquals("Wrong name of new node", "tableSpace", data
268                 .getNewNodeName());
269         assertTrue("Path nodes available", data.getPathNodes().isEmpty());
270         assertEquals("Wrong type of parent node", "table", data.getParent()
271                 .getName());
272         ConfigurationNode node = data.getParent().getChild(0);
273         assertEquals("Wrong table", tables[0], node.getValue());
274 
275         data = engine.prepareAdd(root, "tables.table(1).fields.field(2).alias");
276         assertEquals("Wrong name of new node", "alias", data.getNewNodeName());
277         assertEquals("Wrong type of parent node", "field", data.getParent()
278                 .getName());
279         assertEquals("Wrong field node", "creationDate", data.getParent()
280                 .getChild(0).getValue());
281     }
282 
283     /**
284      * Tests adding new attributes.
285      */
286     @Test
287     public void testPrepareAddAttribute()
288     {
289         NodeAddData data = engine.prepareAdd(root,
290                 "tables.table(0)[@tableSpace]");
291         assertEquals("Wrong table node", tables[0], data.getParent()
292                 .getChild(0).getValue());
293         assertEquals("Wrong name of new node", "tableSpace", data
294                 .getNewNodeName());
295         assertTrue("Attribute not detected", data.isAttribute());
296         assertTrue("Path nodes available", data.getPathNodes().isEmpty());
297 
298         data = engine.prepareAdd(root, "[@newAttr]");
299         assertSame("Root node is not parent", root, data.getParent());
300         assertEquals("Wrong name of new node", "newAttr", data.getNewNodeName());
301         assertTrue("Attribute not detected", data.isAttribute());
302     }
303 
304     /**
305      * Tests add operations where complete paths are added.
306      */
307     @Test
308     public void testPrepareAddWithPath()
309     {
310         NodeAddData data = engine.prepareAdd(root,
311                 "tables.table(1).fields.field(-1).name");
312         assertEquals("Wrong name of new node", "name", data.getNewNodeName());
313         checkNodePath(data, new String[]
314         { "field"});
315         assertEquals("Wrong type of parent node", "fields", data.getParent()
316                 .getName());
317 
318         data = engine.prepareAdd(root, "tables.table(-1).name");
319         assertEquals("Wrong name of new node", "name", data.getNewNodeName());
320         checkNodePath(data, new String[]
321         { "table"});
322         assertEquals("Wrong type of parent node", "tables", data.getParent()
323                 .getName());
324 
325         data = engine.prepareAdd(root, "a.complete.new.path");
326         assertEquals("Wrong name of new node", "path", data.getNewNodeName());
327         checkNodePath(data, new String[]
328         { "a", "complete", "new"});
329         assertSame("Root is not parent", root, data.getParent());
330     }
331 
332     /**
333      * Tests add operations when property and attribute delimiters are equal.
334      * Then it is not possible to add new attribute nodes.
335      */
336     @Test
337     public void testPrepareAddWithSameAttributeDelimiter()
338     {
339         engine.setAttributeEnd(null);
340         engine.setAttributeStart(engine.getPropertyDelimiter());
341 
342         NodeAddData data = engine.prepareAdd(root, "tables.table(0).test");
343         assertEquals("Wrong name of new node", "test", data.getNewNodeName());
344         assertFalse("New node is an attribute", data.isAttribute());
345         assertEquals("Wrong type of parent node", "table", data.getParent()
346                 .getName());
347 
348         data = engine.prepareAdd(root, "a.complete.new.path");
349         assertFalse("New node is an attribute", data.isAttribute());
350         checkNodePath(data, new String[]
351         { "a", "complete", "new"});
352     }
353 
354     /**
355      * Tests add operations when an alternative syntax is set.
356      */
357     @Test
358     public void testPrepareAddWithAlternativeSyntax()
359     {
360         setUpAlternativeSyntax();
361         NodeAddData data = engine.prepareAdd(root, "tables/table[0]/test");
362         assertEquals("Wrong name of new node", "test", data.getNewNodeName());
363         assertFalse("New node is attribute", data.isAttribute());
364         assertEquals("Wrong parent node", tables[0], data.getParent().getChild(
365                 0).getValue());
366 
367         data = engine.prepareAdd(root, "a/complete/new/path@attr");
368         assertEquals("Wrong name of new attribute", "attr", data
369                 .getNewNodeName());
370         checkNodePath(data, new String[]
371         { "a", "complete", "new", "path"});
372         assertSame("Root is not parent", root, data.getParent());
373     }
374 
375     /**
376      * Tests using invalid keys, e.g. if something should be added to
377      * attributes.
378      */
379     @Test(expected = IllegalArgumentException.class)
380     public void testPrepareAddInvalidKey()
381     {
382         engine.prepareAdd(root, "tables.table(0)[@type].new");
383     }
384 
385     @Test(expected = IllegalArgumentException.class)
386     public void testPrepareAddInvalidKeyAttribute()
387     {
388         engine
389         .prepareAdd(root,
390                 "a.complete.new.path.with.an[@attribute].at.a.non.allowed[@position]");
391     }
392 
393     @Test(expected = IllegalArgumentException.class)
394     public void testPrepareAddNullKey()
395     {
396         engine.prepareAdd(root, null);
397     }
398 
399     @Test(expected = IllegalArgumentException.class)
400     public void testPrepareAddEmptyKey()
401     {
402         engine.prepareAdd(root, "");
403     }
404 
405     /**
406      * Creates a node hierarchy for testing that consists of tables, their
407      * fields, and some additional data:
408      *
409      * <pre>
410      *  tables
411      *       table
412      *          name
413      *          fields
414      *              field
415      *                  name
416      *              field
417      *                  name
418      * </pre>
419      *
420      * @return the root of the test node hierarchy
421      */
422     protected ConfigurationNode setUpNodes()
423     {
424         DefaultConfigurationNode rootNode = new DefaultConfigurationNode();
425 
426         DefaultConfigurationNode nodeTables = new DefaultConfigurationNode(
427                 "tables");
428         rootNode.addChild(nodeTables);
429         for (int i = 0; i < tables.length; i++)
430         {
431             DefaultConfigurationNode nodeTable = new DefaultConfigurationNode(
432                     "table");
433             nodeTables.addChild(nodeTable);
434             nodeTable.addChild(new DefaultConfigurationNode("name", tables[i]));
435             nodeTable.addAttribute(new DefaultConfigurationNode("type",
436                     tabTypes[i]));
437             DefaultConfigurationNode nodeFields = new DefaultConfigurationNode(
438                     "fields");
439             nodeTable.addChild(nodeFields);
440 
441             for (int j = 0; j < fields[i].length; j++)
442             {
443                 nodeFields.addChild(createFieldNode(fields[i][j]));
444             }
445         }
446 
447         DefaultConfigurationNode nodeConn = new DefaultConfigurationNode(
448                 "connection.settings");
449         rootNode.addChild(nodeConn);
450         nodeConn.addChild(new DefaultConfigurationNode("usr.name", "scott"));
451         nodeConn.addChild(new DefaultConfigurationNode("usr.pwd", "tiger"));
452         rootNode.addAttribute(new DefaultConfigurationNode("test", "true"));
453 
454         return rootNode;
455     }
456 
457     /**
458      * Configures the expression engine to use a different syntax.
459      */
460     private void setUpAlternativeSyntax()
461     {
462         engine.setAttributeEnd(null);
463         engine.setAttributeStart("@");
464         engine.setPropertyDelimiter("/");
465         engine.setEscapedDelimiter(null);
466         engine.setIndexStart("[");
467         engine.setIndexEnd("]");
468     }
469 
470     /**
471      * Helper method for checking the evaluation of a key. Queries the
472      * expression engine and tests if the expected results are returned.
473      *
474      * @param key the key
475      * @param name the name of the nodes to be returned
476      * @param count the number of expected result nodes
477      * @return the list with the results of the query
478      */
479     private List<ConfigurationNode> checkKey(String key, String name, int count)
480     {
481         List<ConfigurationNode> nodes = engine.query(root, key);
482         assertEquals("Wrong number of result nodes for key " + key, count,
483                 nodes.size());
484         for (Iterator<ConfigurationNode> it = nodes.iterator(); it.hasNext();)
485         {
486             assertEquals("Wrong result node for key " + key, name,
487                     it.next().getName());
488         }
489         return nodes;
490     }
491 
492     /**
493      * Helper method for checking the value of a node specified by the given
494      * key. This method evaluates the key and checks whether the resulting node
495      * has the expected value.
496      *
497      * @param key the key
498      * @param name the expected name of the result node
499      * @param value the expected value of the result node
500      */
501     private void checkKeyValue(String key, String name, String value)
502     {
503         List<ConfigurationNode> nodes = checkKey(key, name, 1);
504         assertEquals("Wrong value for key " + key, value,
505                 nodes.get(0).getValue());
506     }
507 
508     /**
509      * Helper method for checking the path of an add operation.
510      *
511      * @param data the add data object
512      * @param expected the expected path nodes
513      */
514     private void checkNodePath(NodeAddData data, String[] expected)
515     {
516         assertEquals("Wrong number of path nodes", expected.length, data
517                 .getPathNodes().size());
518         Iterator<String> it = data.getPathNodes().iterator();
519         for (int i = 0; i < expected.length; i++)
520         {
521             assertEquals("Wrong path node " + i, expected[i], it.next());
522         }
523     }
524 
525     /**
526      * Helper method for creating a field node with its children for the test
527      * node hierarchy.
528      *
529      * @param name the name of the field
530      * @return the field node
531      */
532     private static ConfigurationNode createFieldNode(String name)
533     {
534         DefaultConfigurationNode nodeField = new DefaultConfigurationNode(
535                 "field");
536         nodeField.addChild(new DefaultConfigurationNode("name", name));
537         return nodeField;
538     }
539 }