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 }