001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.configuration.tree;
018
019 import static org.junit.Assert.assertEquals;
020 import static org.junit.Assert.assertFalse;
021 import static org.junit.Assert.assertSame;
022 import static org.junit.Assert.assertTrue;
023
024 import java.util.Iterator;
025 import java.util.List;
026
027 import org.junit.Before;
028 import org.junit.Test;
029
030 /**
031 * Test class for DefaultExpressionEngine.
032 *
033 * @author <a
034 * href="http://commons.apache.org/configuration/team-list.html">Commons
035 * Configuration team</a>
036 * @version $Id: TestDefaultExpressionEngine.java 1225918 2011-12-30 20:54:47Z oheger $
037 */
038 public class TestDefaultExpressionEngine
039 {
040 /** Stores the names of the test nodes representing tables. */
041 private static String[] tables =
042 { "users", "documents"};
043
044 /** Stores the types of the test table nodes. */
045 private static String[] tabTypes =
046 { "system", "application"};
047
048 /** Test data fields for the node hierarchy. */
049 private static String[][] fields =
050 {
051 { "uid", "uname", "firstName", "lastName", "email"},
052 { "docid", "name", "creationDate", "authorID", "version"}};
053
054 /** The object to be tested. */
055 DefaultExpressionEngine engine;
056
057 /** The root of a hierarchy with configuration nodes. */
058 ConfigurationNode root;
059
060 @Before
061 public void setUp() throws Exception
062 {
063 root = setUpNodes();
064 engine = new DefaultExpressionEngine();
065 }
066
067 /**
068 * Tests some simple queries.
069 */
070 @Test
071 public void testQueryKeys()
072 {
073 checkKey("tables.table.name", "name", 2);
074 checkKey("tables.table.fields.field.name", "name", 10);
075 checkKey("tables.table[@type]", "type", 2);
076 checkKey("tables.table(0).fields.field.name", "name", 5);
077 checkKey("tables.table(1).fields.field.name", "name", 5);
078 checkKey("tables.table.fields.field(1).name", "name", 2);
079 }
080
081 /**
082 * Performs some queries and evaluates the values of the result nodes.
083 */
084 @Test
085 public void testQueryNodes()
086 {
087 for (int i = 0; i < tables.length; i++)
088 {
089 checkKeyValue("tables.table(" + i + ").name", "name", tables[i]);
090 checkKeyValue("tables.table(" + i + ")[@type]", "type", tabTypes[i]);
091
092 for (int j = 0; j < fields[i].length; j++)
093 {
094 checkKeyValue("tables.table(" + i + ").fields.field(" + j
095 + ").name", "name", fields[i][j]);
096 }
097 }
098 }
099
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 }