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 java.util.Iterator;
020: import java.util.List;
021:
022: import junit.framework.TestCase;
023:
024: /**
025: * Test class for DefaultExpressionEngine.
026: *
027: * @author Oliver Heger
028: */
029: public class TestDefaultExpressionEngine extends TestCase {
030: /** Stores the names of the test nodes representing tables. */
031: private static String[] tables = { "users", "documents" };
032:
033: /** Stores the types of the test table nodes. */
034: private static String[] tabTypes = { "system", "application" };
035:
036: /** Test data fields for the node hierarchy. */
037: private static String[][] fields = {
038: { "uid", "uname", "firstName", "lastName", "email" },
039: { "docid", "name", "creationDate", "authorID", "version" } };
040:
041: /** The object to be tested. */
042: DefaultExpressionEngine engine;
043:
044: /** The root of a hierarchy with configuration nodes. */
045: ConfigurationNode root;
046:
047: protected void setUp() throws Exception {
048: super .setUp();
049: root = setUpNodes();
050: engine = new DefaultExpressionEngine();
051: }
052:
053: /**
054: * Tests some simple queries.
055: */
056: public void testQueryKeys() {
057: checkKey("tables.table.name", "name", 2);
058: checkKey("tables.table.fields.field.name", "name", 10);
059: checkKey("tables.table[@type]", "type", 2);
060: checkKey("tables.table(0).fields.field.name", "name", 5);
061: checkKey("tables.table(1).fields.field.name", "name", 5);
062: checkKey("tables.table.fields.field(1).name", "name", 2);
063: }
064:
065: /**
066: * Performs some queries and evaluates the values of the result nodes.
067: */
068: public void testQueryNodes() {
069: for (int i = 0; i < tables.length; i++) {
070: checkKeyValue("tables.table(" + i + ").name", "name",
071: tables[i]);
072: checkKeyValue("tables.table(" + i + ")[@type]", "type",
073: tabTypes[i]);
074:
075: for (int j = 0; j < fields[i].length; j++) {
076: checkKeyValue("tables.table(" + i + ").fields.field("
077: + j + ").name", "name", fields[i][j]);
078: }
079: }
080: }
081:
082: /**
083: * Tests querying keys that do not exist.
084: */
085: public void testQueryNonExistingKeys() {
086: checkKey("tables.tablespace.name", null, 0);
087: checkKey("tables.table(2).name", null, 0);
088: checkKey("a complete unknown key", null, 0);
089: checkKey("tables.table(0).fields.field(-1).name", null, 0);
090: checkKey("tables.table(0).fields.field(28).name", null, 0);
091: checkKey("tables.table(0).fields.field().name", null, 0);
092: checkKey("connection.settings.usr.name", null, 0);
093: }
094:
095: /**
096: * Tests querying nodes whose names contain a delimiter.
097: */
098: public void testQueryEscapedKeys() {
099: checkKeyValue("connection..settings.usr..name", "usr.name",
100: "scott");
101: checkKeyValue("connection..settings.usr..pwd", "usr.pwd",
102: "tiger");
103: }
104:
105: /**
106: * Tests some queries when the same delimiter is used for properties and
107: * attributes.
108: */
109: public void testQueryAttributeEmulation() {
110: engine.setAttributeEnd(null);
111: engine.setAttributeStart(engine.getPropertyDelimiter());
112: checkKeyValue("tables.table(0).name", "name", tables[0]);
113: checkKeyValue("tables.table(0).type", "type", tabTypes[0]);
114: checkKey("tables.table.type", "type", 2);
115: }
116:
117: /**
118: * Tests accessing the root node.
119: */
120: public void testQueryRootNode() {
121: List nodes = checkKey(null, null, 1);
122: assertSame("Root node not found", root, nodes.get(0));
123: nodes = checkKey("", null, 1);
124: assertSame("Root node not found", root, nodes.get(0));
125: checkKeyValue("[@test]", "test", "true");
126: }
127:
128: /**
129: * Tests a different query snytax. Sets other strings for the typical tokens
130: * used by the expression engine.
131: */
132: public void testQueryAlternativeSyntax() {
133: setUpAlternativeSyntax();
134: checkKeyValue("tables/table[1]/name", "name", tables[1]);
135: checkKeyValue("tables/table[0]@type", "type", tabTypes[0]);
136: checkKeyValue("@test", "test", "true");
137: checkKeyValue("connection.settings/usr.name", "usr.name",
138: "scott");
139: }
140:
141: /**
142: * Tests obtaining keys for nodes.
143: */
144: public void testNodeKey() {
145: ConfigurationNode node = root.getChild(0);
146: assertEquals("Invalid name for descendant of root", "tables",
147: engine.nodeKey(node, ""));
148: assertEquals("Parent key not respected", "test.tables", engine
149: .nodeKey(node, "test"));
150: assertEquals("Full parent key not taken into account",
151: "a.full.parent.key.tables", engine.nodeKey(node,
152: "a.full.parent.key"));
153: }
154:
155: /**
156: * Tests obtaining keys when the root node is involved.
157: */
158: public void testNodeKeyWithRoot() {
159: assertEquals("Wrong name for root noot", "", engine.nodeKey(
160: root, null));
161: assertEquals("Null name not detected", "test", engine.nodeKey(
162: root, "test"));
163: }
164:
165: /**
166: * Tests obtaining keys for attribute nodes.
167: */
168: public void testNodeKeyWithAttribute() {
169: ConfigurationNode node = root.getChild(0).getChild(0)
170: .getAttribute(0);
171: assertEquals("Wrong attribute node", "type", node.getName());
172: assertEquals("Wrong attribute key", "tables.table[@type]",
173: engine.nodeKey(node, "tables.table"));
174: assertEquals("Wrong key for root attribute", "[@test]", engine
175: .nodeKey(root.getAttribute(0), ""));
176: }
177:
178: /**
179: * Tests obtaining keys for nodes that contain the delimiter character.
180: */
181: public void testNodeKeyWithEscapedDelimiters() {
182: ConfigurationNode node = root.getChild(1);
183: assertEquals("Wrong escaped key", "connection..settings",
184: engine.nodeKey(node, ""));
185: assertEquals("Wrong complex escaped key",
186: "connection..settings.usr..name", engine.nodeKey(node
187: .getChild(0), engine.nodeKey(node, "")));
188: }
189:
190: /**
191: * Tests obtaining node keys when a different syntax is set.
192: */
193: public void testNodeKeyWithAlternativeSyntax() {
194: setUpAlternativeSyntax();
195: assertEquals("Wrong child key", "tables/table", engine.nodeKey(
196: root.getChild(0).getChild(0), "tables"));
197: assertEquals("Wrong attribute key", "@test", engine.nodeKey(
198: root.getAttribute(0), ""));
199:
200: engine.setAttributeStart(engine.getPropertyDelimiter());
201: assertEquals("Wrong attribute key", "/test", engine.nodeKey(
202: root.getAttribute(0), ""));
203: }
204:
205: /**
206: * Tests adding direct child nodes to the existing hierarchy.
207: */
208: public void testPrepareAddDirectly() {
209: NodeAddData data = engine.prepareAdd(root, "newNode");
210: assertSame("Wrong parent node", root, data.getParent());
211: assertTrue("Path nodes available", data.getPathNodes()
212: .isEmpty());
213: assertEquals("Wrong name of new node", "newNode", data
214: .getNewNodeName());
215: assertFalse("New node is an attribute", data.isAttribute());
216:
217: data = engine
218: .prepareAdd(root, "tables.table.fields.field.name");
219: assertEquals("Wrong name of new node", "name", data
220: .getNewNodeName());
221: assertTrue("Path nodes available", data.getPathNodes()
222: .isEmpty());
223: assertEquals("Wrong parent node", "field", data.getParent()
224: .getName());
225: ConfigurationNode nd = data.getParent().getChild(0);
226: assertEquals("Field has no name node", "name", nd.getName());
227: assertEquals("Incorrect name", "version", nd.getValue());
228: }
229:
230: /**
231: * Tests adding when indices are involved.
232: */
233: public void testPrepareAddWithIndex() {
234: NodeAddData data = engine.prepareAdd(root,
235: "tables.table(0).tableSpace");
236: assertEquals("Wrong name of new node", "tableSpace", data
237: .getNewNodeName());
238: assertTrue("Path nodes available", data.getPathNodes()
239: .isEmpty());
240: assertEquals("Wrong type of parent node", "table", data
241: .getParent().getName());
242: ConfigurationNode node = data.getParent().getChild(0);
243: assertEquals("Wrong table", tables[0], node.getValue());
244:
245: data = engine.prepareAdd(root,
246: "tables.table(1).fields.field(2).alias");
247: assertEquals("Wrong name of new node", "alias", data
248: .getNewNodeName());
249: assertEquals("Wrong type of parent node", "field", data
250: .getParent().getName());
251: assertEquals("Wrong field node", "creationDate", data
252: .getParent().getChild(0).getValue());
253: }
254:
255: /**
256: * Tests adding new attributes.
257: */
258: public void testPrepareAddAttribute() {
259: NodeAddData data = engine.prepareAdd(root,
260: "tables.table(0)[@tableSpace]");
261: assertEquals("Wrong table node", tables[0], data.getParent()
262: .getChild(0).getValue());
263: assertEquals("Wrong name of new node", "tableSpace", data
264: .getNewNodeName());
265: assertTrue("Attribute not detected", data.isAttribute());
266: assertTrue("Path nodes available", data.getPathNodes()
267: .isEmpty());
268:
269: data = engine.prepareAdd(root, "[@newAttr]");
270: assertSame("Root node is not parent", root, data.getParent());
271: assertEquals("Wrong name of new node", "newAttr", data
272: .getNewNodeName());
273: assertTrue("Attribute not detected", data.isAttribute());
274: }
275:
276: /**
277: * Tests add operations where complete pathes are added.
278: */
279: public void testPrepareAddWithPath() {
280: NodeAddData data = engine.prepareAdd(root,
281: "tables.table(1).fields.field(-1).name");
282: assertEquals("Wrong name of new node", "name", data
283: .getNewNodeName());
284: checkNodePath(data, new String[] { "field" });
285: assertEquals("Wrong type of parent node", "fields", data
286: .getParent().getName());
287:
288: data = engine.prepareAdd(root, "tables.table(-1).name");
289: assertEquals("Wrong name of new node", "name", data
290: .getNewNodeName());
291: checkNodePath(data, new String[] { "table" });
292: assertEquals("Wrong type of parent node", "tables", data
293: .getParent().getName());
294:
295: data = engine.prepareAdd(root, "a.complete.new.path");
296: assertEquals("Wrong name of new node", "path", data
297: .getNewNodeName());
298: checkNodePath(data, new String[] { "a", "complete", "new" });
299: assertSame("Root is not parent", root, data.getParent());
300: }
301:
302: /**
303: * Tests add operations when property and attribute delimiters are equal.
304: * Then it is not possible to add new attribute nodes.
305: */
306: public void testPrepareAddWithSameAttributeDelimiter() {
307: engine.setAttributeEnd(null);
308: engine.setAttributeStart(engine.getPropertyDelimiter());
309:
310: NodeAddData data = engine.prepareAdd(root,
311: "tables.table(0).test");
312: assertEquals("Wrong name of new node", "test", data
313: .getNewNodeName());
314: assertFalse("New node is an attribute", data.isAttribute());
315: assertEquals("Wrong type of parent node", "table", data
316: .getParent().getName());
317:
318: data = engine.prepareAdd(root, "a.complete.new.path");
319: assertFalse("New node is an attribute", data.isAttribute());
320: checkNodePath(data, new String[] { "a", "complete", "new" });
321: }
322:
323: /**
324: * Tests add operations when an alternative syntax is set.
325: */
326: public void testPrepareAddWithAlternativeSyntax() {
327: setUpAlternativeSyntax();
328: NodeAddData data = engine.prepareAdd(root,
329: "tables/table[0]/test");
330: assertEquals("Wrong name of new node", "test", data
331: .getNewNodeName());
332: assertFalse("New node is attribute", data.isAttribute());
333: assertEquals("Wrong parent node", tables[0], data.getParent()
334: .getChild(0).getValue());
335:
336: data = engine.prepareAdd(root, "a/complete/new/path@attr");
337: assertEquals("Wrong name of new attribute", "attr", data
338: .getNewNodeName());
339: checkNodePath(data, new String[] { "a", "complete", "new",
340: "path" });
341: assertSame("Root is not parent", root, data.getParent());
342: }
343:
344: /**
345: * Tests using invalid keys, e.g. if something should be added to
346: * attributes.
347: */
348: public void testPrepareAddInvalidKeys() {
349: try {
350: engine.prepareAdd(root, "tables.table(0)[@type].new");
351: fail("Could add node to existing attribute!");
352: } catch (IllegalArgumentException iex) {
353: // ok
354: }
355:
356: try {
357: engine
358: .prepareAdd(root,
359: "a.complete.new.path.with.an[@attribute].at.a.non.allowed[@position]");
360: fail("Could add invalid path!");
361: } catch (IllegalArgumentException iex) {
362: // ok
363: }
364:
365: try {
366: engine.prepareAdd(root, null);
367: fail("Could add null key!");
368: } catch (IllegalArgumentException iex) {
369: // ok
370: }
371:
372: try {
373: engine.prepareAdd(root, "");
374: fail("Could add undefined key!");
375: } catch (IllegalArgumentException iex) {
376: // ok
377: }
378: }
379:
380: /**
381: * Creates a node hierarchy for testing that consists of tables, their
382: * fields, and some additional data:
383: *
384: * <pre>
385: * tables
386: * table
387: * name
388: * fields
389: * field
390: * name
391: * field
392: * name
393: * </pre>
394: *
395: * @return the root of the test node hierarchy
396: */
397: protected ConfigurationNode setUpNodes() {
398: DefaultConfigurationNode rootNode = new DefaultConfigurationNode();
399:
400: DefaultConfigurationNode nodeTables = new DefaultConfigurationNode(
401: "tables");
402: rootNode.addChild(nodeTables);
403: for (int i = 0; i < tables.length; i++) {
404: DefaultConfigurationNode nodeTable = new DefaultConfigurationNode(
405: "table");
406: nodeTables.addChild(nodeTable);
407: nodeTable.addChild(new DefaultConfigurationNode("name",
408: tables[i]));
409: nodeTable.addAttribute(new DefaultConfigurationNode("type",
410: tabTypes[i]));
411: DefaultConfigurationNode nodeFields = new DefaultConfigurationNode(
412: "fields");
413: nodeTable.addChild(nodeFields);
414:
415: for (int j = 0; j < fields[i].length; j++) {
416: nodeFields.addChild(createFieldNode(fields[i][j]));
417: }
418: }
419:
420: DefaultConfigurationNode nodeConn = new DefaultConfigurationNode(
421: "connection.settings");
422: rootNode.addChild(nodeConn);
423: nodeConn.addChild(new DefaultConfigurationNode("usr.name",
424: "scott"));
425: nodeConn.addChild(new DefaultConfigurationNode("usr.pwd",
426: "tiger"));
427: rootNode.addAttribute(new DefaultConfigurationNode("test",
428: "true"));
429:
430: return rootNode;
431: }
432:
433: /**
434: * Configures the expression engine to use a different syntax.
435: */
436: private void setUpAlternativeSyntax() {
437: engine.setAttributeEnd(null);
438: engine.setAttributeStart("@");
439: engine.setPropertyDelimiter("/");
440: engine.setEscapedDelimiter(null);
441: engine.setIndexStart("[");
442: engine.setIndexEnd("]");
443: }
444:
445: /**
446: * Helper method for checking the evaluation of a key. Queries the
447: * expression engine and tests if the expected results are returned.
448: *
449: * @param key the key
450: * @param name the name of the nodes to be returned
451: * @param count the number of expected result nodes
452: * @return the list with the results of the query
453: */
454: private List checkKey(String key, String name, int count) {
455: List nodes = engine.query(root, key);
456: assertEquals("Wrong number of result nodes for key " + key,
457: count, nodes.size());
458: for (Iterator it = nodes.iterator(); it.hasNext();) {
459: assertEquals("Wrong result node for key " + key, name,
460: ((ConfigurationNode) it.next()).getName());
461: }
462: return nodes;
463: }
464:
465: /**
466: * Helper method for checking the value of a node specified by the given
467: * key. This method evaluates the key and checks whether the resulting node
468: * has the expected value.
469: *
470: * @param key the key
471: * @param name the expected name of the result node
472: * @param value the expected value of the result node
473: */
474: private void checkKeyValue(String key, String name, String value) {
475: List nodes = checkKey(key, name, 1);
476: assertEquals("Wrong value for key " + key, value,
477: ((ConfigurationNode) nodes.get(0)).getValue());
478: }
479:
480: /**
481: * Helper method for checking the path of an add operation.
482: *
483: * @param data the add data object
484: * @param expected the expected path nodes
485: */
486: private void checkNodePath(NodeAddData data, String[] expected) {
487: assertEquals("Wrong number of path nodes", expected.length,
488: data.getPathNodes().size());
489: Iterator it = data.getPathNodes().iterator();
490: for (int i = 0; i < expected.length; i++) {
491: assertEquals("Wrong path node " + i, expected[i], it.next());
492: }
493: }
494:
495: /**
496: * Helper method for creating a field node with its children for the test
497: * node hierarchy.
498: *
499: * @param name the name of the field
500: * @return the field node
501: */
502: private static ConfigurationNode createFieldNode(String name) {
503: DefaultConfigurationNode nodeField = new DefaultConfigurationNode(
504: "field");
505: nodeField.addChild(new DefaultConfigurationNode("name", name));
506: return nodeField;
507: }
508: }
|