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
9
* http://www.apache.org/licenses/LICENSE-2.0
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.
17
package org.apache.commons.configuration.tree.xpath;
19
import java.util.ArrayList;
20
import java.util.Iterator;
21
import java.util.List;
23
import junit.framework.TestCase;
25
import org.apache.commons.configuration.tree.ConfigurationNode;
26
import org.apache.commons.configuration.tree.DefaultConfigurationNode;
27
import org.apache.commons.configuration.tree.NodeAddData;
28
import org.apache.commons.jxpath.JXPathContext;
29
import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
30
import org.apache.commons.jxpath.ri.model.NodePointerFactory;
33
* Test class for XPathExpressionEngine.
35
* @author Oliver Heger
36
* @version $Id: TestXPathExpressionEngine.java 1152343 2011-07-29 19:23:09Z oheger $
38
public class TestXPathExpressionEngine extends TestCase
40
/** Constant for the test root node. */
41
static final ConfigurationNode ROOT = new DefaultConfigurationNode(
44
/** Constant for the valid test key. */
45
static final String TEST_KEY = "TESTKEY";
47
/** The expression engine to be tested. */
48
XPathExpressionEngine engine;
50
protected void setUp() throws Exception
53
engine = new MockJXPathContextExpressionEngine();
57
* Tests the query() method with a normal expression.
59
public void testQueryExpression()
61
List nodes = engine.query(ROOT, TEST_KEY);
62
assertEquals("Incorrect number of results", 1, nodes.size());
63
assertSame("Wrong result node", ROOT, nodes.get(0));
68
* Tests a query that has no results. This should return an empty list.
70
public void testQueryWithoutResult()
72
List nodes = engine.query(ROOT, "a non existing key");
73
assertTrue("Result list is not empty", nodes.isEmpty());
78
* Tests a query with an empty key. This should directly return the root
79
* node without invoking the JXPathContext.
81
public void testQueryWithEmptyKey()
87
* Tests a query with a null key. Same as an empty key.
89
public void testQueryWithNullKey()
95
* Helper method for testing undefined keys.
99
private void checkEmptyKey(String key)
101
List nodes = engine.query(ROOT, key);
102
assertEquals("Incorrect number of results", 1, nodes.size());
103
assertSame("Wrong result node", ROOT, nodes.get(0));
108
* Tests if the used JXPathContext is correctly initialized.
110
public void testCreateContext()
112
JXPathContext ctx = new XPathExpressionEngine().createContext(ROOT,
114
assertNotNull("Context is null", ctx);
115
assertTrue("Lenient mode is not set", ctx.isLenient());
116
assertSame("Incorrect context bean set", ROOT, ctx.getContextBean());
118
NodePointerFactory[] factories = JXPathContextReferenceImpl
119
.getNodePointerFactories();
120
boolean found = false;
121
for (int i = 0; i < factories.length; i++)
123
if (factories[i] instanceof ConfigurationNodePointerFactory)
128
assertTrue("No configuration pointer factory found", found);
132
* Tests a normal call of nodeKey().
134
public void testNodeKeyNormal()
136
assertEquals("Wrong node key", "parent/child", engine.nodeKey(
137
new DefaultConfigurationNode("child"), "parent"));
141
* Tests nodeKey() for an attribute node.
143
public void testNodeKeyAttribute()
145
ConfigurationNode node = new DefaultConfigurationNode("attr");
146
node.setAttribute(true);
147
assertEquals("Wrong attribute key", "node/@attr", engine.nodeKey(node,
152
* Tests nodeKey() for the root node.
154
public void testNodeKeyForRootNode()
156
assertEquals("Wrong key for root node", "", engine.nodeKey(ROOT, null));
157
assertEquals("Null name not detected", "test", engine.nodeKey(
158
new DefaultConfigurationNode(), "test"));
162
* Tests node key() for direct children of the root node.
164
public void testNodeKeyForRootChild()
166
ConfigurationNode node = new DefaultConfigurationNode("child");
167
assertEquals("Wrong key for root child node", "child", engine.nodeKey(
169
node.setAttribute(true);
170
assertEquals("Wrong key for root attribute", "@child", engine.nodeKey(
175
* Tests adding a single child node.
177
public void testPrepareAddNode()
179
NodeAddData data = engine.prepareAdd(ROOT, TEST_KEY + " newNode");
180
checkAddPath(data, new String[]
181
{ "newNode" }, false);
186
* Tests adding a new attribute node.
188
public void testPrepareAddAttribute()
190
NodeAddData data = engine.prepareAdd(ROOT, TEST_KEY + "\t@newAttr");
191
checkAddPath(data, new String[]
192
{ "newAttr" }, true);
197
* Tests adding a complete path.
199
public void testPrepareAddPath()
201
NodeAddData data = engine.prepareAdd(ROOT, TEST_KEY
202
+ " \t a/full/path/node");
203
checkAddPath(data, new String[]
204
{ "a", "full", "path", "node" }, false);
209
* Tests adding a complete path whose final node is an attribute.
211
public void testPrepareAddAttributePath()
213
NodeAddData data = engine.prepareAdd(ROOT, TEST_KEY
214
+ " a/full/path@attr");
215
checkAddPath(data, new String[]
216
{ "a", "full", "path", "attr" }, true);
221
* Tests adding a new node to the root.
223
public void testPrepareAddRootChild()
225
NodeAddData data = engine.prepareAdd(ROOT, " newNode");
226
checkAddPath(data, new String[]
227
{ "newNode" }, false);
232
* Tests adding a new attribute to the root.
234
public void testPrepareAddRootAttribute()
236
NodeAddData data = engine.prepareAdd(ROOT, " @attr");
237
checkAddPath(data, new String[]
243
* Tests an add operation with a query that does not return a single node.
245
public void testPrepareAddInvalidParent()
249
engine.prepareAdd(ROOT, "invalidKey newNode");
250
fail("Could add to invalid parent!");
252
catch (IllegalArgumentException iex)
259
* Tests an add operation with an empty path for the new node.
261
public void testPrepareAddEmptyPath()
265
engine.prepareAdd(ROOT, TEST_KEY + " ");
266
fail("Could add empty path!");
268
catch (IllegalArgumentException iex)
275
* Tests an add operation where the key is null.
277
public void testPrepareAddNullKey()
281
engine.prepareAdd(ROOT, null);
282
fail("Could add null path!");
284
catch (IllegalArgumentException iex)
291
* Tests an add operation where the key is null.
293
public void testPrepareAddEmptyKey()
297
engine.prepareAdd(ROOT, "");
298
fail("Could add empty path!");
300
catch (IllegalArgumentException iex)
307
* Tests an add operation with an invalid path.
309
public void testPrepareAddInvalidPath()
313
engine.prepareAdd(ROOT, TEST_KEY + " an/invalid//path");
314
fail("Could add invalid path!");
316
catch (IllegalArgumentException iex)
323
* Tests an add operation with an invalid path: the path contains an
324
* attribute in the middle part.
326
public void testPrepareAddInvalidAttributePath()
330
engine.prepareAdd(ROOT, TEST_KEY + " a/path/with@an/attribute");
331
fail("Could add invalid attribute path!");
333
catch (IllegalArgumentException iex)
340
* Tests an add operation with an invalid path: the path contains an
341
* attribute after a slash.
343
public void testPrepareAddInvalidAttributePath2()
347
engine.prepareAdd(ROOT, TEST_KEY + " a/path/with/@attribute");
348
fail("Could add invalid attribute path!");
350
catch (IllegalArgumentException iex)
357
* Tests an add operation with an invalid path that starts with a slash.
359
public void testPrepareAddInvalidPathWithSlash()
363
engine.prepareAdd(ROOT, TEST_KEY + " /a/path/node");
364
fail("Could add path starting with a slash!");
366
catch (IllegalArgumentException iex)
373
* Tests an add operation with an invalid path that contains multiple
374
* attribute components.
376
public void testPrepareAddInvalidPathMultipleAttributes()
380
engine.prepareAdd(ROOT, TEST_KEY + " an@attribute@path");
381
fail("Could add path with multiple attributes!");
383
catch (IllegalArgumentException iex)
390
* Helper method for testing the path nodes in the given add data object.
392
* @param data the data object to check
393
* @param expected an array with the expected path elements
394
* @param attr a flag if the new node is an attribute
396
private void checkAddPath(NodeAddData data, String[] expected, boolean attr)
398
assertSame("Wrong parent node", ROOT, data.getParent());
399
List path = data.getPathNodes();
400
assertEquals("Incorrect number of path nodes", expected.length - 1,
402
Iterator it = path.iterator();
403
for (int idx = 0; idx < expected.length - 1; idx++)
405
assertEquals("Wrong node at position " + idx, expected[idx], it
408
assertEquals("Wrong name of new node", expected[expected.length - 1],
409
data.getNewNodeName());
410
assertEquals("Incorrect attribute flag", attr, data.isAttribute());
414
* Checks if the JXPath context's selectNodes() method was called as often
417
* @param expected the number of expected calls
419
protected void checkSelectCalls(int expected)
421
MockJXPathContext ctx = ((MockJXPathContextExpressionEngine) engine).getContext();
422
int calls = (ctx == null) ? 0 : ctx.selectInvocations;
423
assertEquals("Incorrect number of select calls", expected, calls);
427
* A mock implementation of the JXPathContext class. This implementation
428
* will overwrite the <code>selectNodes()</code> method that is used by
429
* <code>XPathExpressionEngine</code> to count the invocations of this
432
static class MockJXPathContext extends JXPathContextReferenceImpl
434
int selectInvocations;
436
public MockJXPathContext(Object bean)
442
* Dummy implementation of this method. If the passed in string is the
443
* test key, the root node will be returned in the list. Otherwise the
444
* return value is <b>null</b>.
446
public List selectNodes(String xpath)
449
if (TEST_KEY.equals(xpath))
451
List result = new ArrayList(1);
463
* A special implementation of XPathExpressionEngine that overrides
464
* createContext() to return a mock context object.
466
static class MockJXPathContextExpressionEngine extends
467
XPathExpressionEngine
469
/** Stores the context instance. */
470
private MockJXPathContext context;
472
protected JXPathContext createContext(ConfigurationNode root, String key)
474
context = new MockJXPathContext(root);
479
* Returns the context created by the last newContext() call.
481
* @return the current context
483
public MockJXPathContext getContext()