blob: 55e84da4db9c6313bfb813bfafc90f0282b2b863 [file] [log] [blame]
/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.digester;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.xml.sax.SAXException;
//import org.apache.commons.logging.impl.SimpleLog;
/**
* <p>Tests for the <code>CallMethodRule</code> and associated
* <code>CallParamRule</code>.
*
* @author Christopher Lenz
*/
public class CallMethodRuleTestCase extends TestCase {
// ----------------------------------------------------- Instance Variables
/**
* The digester instance we will be processing.
*/
protected Digester digester = null;
// ----------------------------------------------------------- Constructors
/**
* Construct a new instance of this test case.
*
* @param name Name of the test case
*/
public CallMethodRuleTestCase(String name) {
super(name);
}
// --------------------------------------------------- Overall Test Methods
/**
* Set up instance variables required by this test case.
*/
public void setUp() {
digester = new Digester();
}
/**
* Return the tests included in this test suite.
*/
public static Test suite() {
return (new TestSuite(CallMethodRuleTestCase.class));
}
/**
* Tear down instance variables required by this test case.
*/
public void tearDown() {
digester = null;
}
// ------------------------------------------------ Individual Test Methods
/**
* Test method calls with the CallMethodRule rule. It should be possible
* to call a method with no arguments using several rule syntaxes.
*/
public void testBasic() throws SAXException, IOException {
// Configure the digester as required
digester.addObjectCreate("employee", Employee.class);
// try all syntax permutations
digester.addCallMethod("employee", "toString", 0, (Class[])null);
digester.addCallMethod("employee", "toString", 0, (String[])null);
digester.addCallMethod("employee", "toString", 0, new Class[] {});
digester.addCallMethod("employee", "toString", 0, new String[] {});
digester.addCallMethod("employee", "toString");
// Parse our test input.
// An exception will be thrown if the method can't be found
Object root1 = digester.parse(getInputStream("Test5.xml"));
assertNotNull(root1);
}
/**
* Test method calls with the CallMethodRule reading from the element
* body, with no CallParamMethod rules added.
*/
public void testCallMethodOnly() throws Exception {
// Configure the digester as required
digester.addObjectCreate("employee", Employee.class);
digester.addCallMethod("employee/firstName", "setFirstName", 0);
digester.addCallMethod("employee/lastName", "setLastName", 0);
// Parse our test input
Employee employee = (Employee)
digester.parse(getInputStream("Test9.xml"));
assertNotNull("parsed an employee", employee);
// Validate that the property setters were called
assertEquals("Set first name", "First Name", employee.getFirstName());
assertEquals("Set last name", "Last Name", employee.getLastName());
}
/**
* Test CallMethodRule variants which specify the classes of the
* parameters to target methods. String, int, boolean, float should all
* be acceptable as parameter types.
*/
public void testSettingProperties() throws SAXException, IOException {
// Configure the digester as required
digester.addObjectCreate("employee", Employee.class);
// try all syntax permutations
digester.addCallMethod("employee", "setLastName", 1,
new String[] {"java.lang.String"});
digester.addCallParam("employee/lastName", 0);
// Parse our test input
Object root1 = null;
// an exception will be thrown if the method can't be found
root1 = digester.parse(getInputStream("Test5.xml"));
Employee employee = (Employee) root1;
assertEquals("Failed to call Employee.setLastName",
"Last Name", employee.getLastName());
digester = new Digester();
// Configure the digester as required
digester.addObjectCreate("employee", Employee.class);
// try out primitive convertion
digester.addCallMethod("employee", "setAge", 1,
new Class[] {int.class});
digester.addCallParam("employee/age", 0);
// Parse our test input
root1 = null;
// an exception will be thrown if the method can't be found
root1 = digester.parse(getInputStream("Test5.xml"));
employee = (Employee) root1;
assertEquals("Failed to call Employee.setAge", 21, employee.getAge());
digester = new Digester();
// Configure the digester as required
digester.addObjectCreate("employee", Employee.class);
digester.addCallMethod("employee", "setActive", 1,
new Class[] {boolean.class});
digester.addCallParam("employee/active", 0);
// Parse our test input
root1 = null;
// an exception will be thrown if the method can't be found
root1 = digester.parse(getInputStream("Test5.xml"));
employee = (Employee) root1;
assertEquals("Failed to call Employee.setActive",
true, employee.isActive());
digester = new Digester();
// Configure the digester as required
digester.addObjectCreate("employee", Employee.class);
digester.addCallMethod("employee", "setSalary", 1,
new Class[] {float.class});
digester.addCallParam("employee/salary", 0);
// Parse our test input
root1 = null;
// an exception will be thrown if the method can't be found
root1 = digester.parse(getInputStream("Test5.xml"));
employee = (Employee) root1;
assertEquals("Failed to call Employee.setSalary",
1000000.0f, employee.getSalary(), 0.1f);
}
/**
* This tests the call methods params enhancement that provides
* for more complex stack-based calls.
*/
public void testParamsFromStack() throws SAXException, IOException {
StringBuffer xml = new StringBuffer().
append("<?xml version='1.0'?>").
append("<map>").
append(" <key name='The key'/>").
append(" <value name='The value'/>").
append("</map>");
digester.addObjectCreate("map", HashMap.class);
digester.addCallMethod("map", "put", 2);
digester.addObjectCreate("map/key", AlphaBean.class);
digester.addSetProperties("map/key");
digester.addCallParam("map/key", 0, true);
digester.addObjectCreate("map/value", BetaBean.class);
digester.addSetProperties("map/value");
digester.addCallParam("map/value", 1, true);
Map map = (Map) digester.parse(new StringReader(xml.toString()));
assertNotNull(map);
assertEquals(1, map.size());
assertEquals("The key",
((AlphaBean)map.keySet().iterator().next()).getName());
assertEquals("The value",
((BetaBean)map.values().iterator().next()).getName());
}
/**
* Test that the target object for a CallMethodRule is the object that was
* on top of the object stack when the CallMethodRule fired, even when other
* rules fire between the CallMethodRule and its associated CallParamRules.
* <p>
* The current implementation of CallMethodRule ensures this works by
* firing only at the end of the tag that CallMethodRule triggered on.
*/
public void testOrderNestedPartA() throws Exception {
// Configure the digester as required
// Here, we use the "grandchild element name" as a parameter to
// the created element, to ensure that all the params aren't
// avaiable to the CallMethodRule until some other rules have fired,
// in particular an ObjectCreateRule. The CallMethodRule should still
// function correctly in this scenario.
digester.addObjectCreate("toplevel/element", NamedBean.class);
digester.addCallMethod("toplevel/element", "setName", 1);
digester.addCallParam("toplevel/element/element/element", 0, "name");
digester.addObjectCreate("toplevel/element/element", NamedBean.class);
// Parse our test input
NamedBean root1 = null;
try {
// an exception will be thrown if the method can't be found
root1 = (NamedBean) digester.parse(getInputStream("Test8.xml"));
} catch (Throwable t) {
// this means that the method can't be found and so the test fails
fail("Digester threw Exception: " + t);
}
// if the CallMethodRule were to incorrectly invoke the method call
// on the second-created NamedBean instance, then the root one would
// have a null name. If it works correctly, the target element will
// be the first-created (root) one, despite the fact that a second
// object instance was created between the firing of the
// CallMethodRule and its associated CallParamRule.
assertEquals("Wrong method call order", "C", root1.getName());
}
/**
* Test nested CallMethod rules.
* <p>
* The current implementation of CallMethodRule, in which the method is
* invoked in its end() method, causes behaviour which some users find
* non-intuitive. In this test it can be seen to "reverse" the order of
* data processed. However this is the way CallMethodRule has always
* behaved, and it is expected that apps out there rely on this call order
* so this test is present to ensure that no-one changes this behaviour.
*/
public void testOrderNestedPartB() throws Exception {
// Configure the digester as required
StringBuffer word = new StringBuffer();
digester.push(word);
digester.addCallMethod("*/element", "append", 1);
digester.addCallParam("*/element", 0, "name");
// Parse our test input
Object root1 = null;
try {
// an exception will be thrown if the method can't be found
root1 = digester.parse(getInputStream("Test8.xml"));
assertNotNull(root1);
} catch (Throwable t) {
// this means that the method can't be found and so the test fails
fail("Digester threw Exception: " + t);
}
assertEquals("Wrong method call order", "CBA", word.toString());
}
public void testPrimitiveReading() throws Exception {
StringReader reader = new StringReader(
"<?xml version='1.0' ?><root><bean good='true'/><bean good='false'/><bean/>"
+ "<beanie bad='Fee Fie Foe Fum' good='true'/><beanie bad='Fee Fie Foe Fum' good='false'/>"
+ "<beanie bad='Fee Fie Foe Fum'/></root>");
Digester digester = new Digester();
//SimpleLog log = new SimpleLog("[testPrimitiveReading:Digester]");
//log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
//digester.setLogger(log);
digester.addObjectCreate("root/bean", PrimitiveBean.class);
digester.addSetNext("root/bean", "add");
Class [] params = { Boolean.TYPE };
digester.addCallMethod("root/bean", "setBoolean", 1, params);
digester.addCallParam("root/bean", 0, "good");
digester.addObjectCreate("root/beanie", PrimitiveBean.class);
digester.addSetNext("root/beanie", "add");
Class [] beanieParams = { String.class, Boolean.TYPE };
digester.addCallMethod("root/beanie", "testSetBoolean", 2, beanieParams);
digester.addCallParam("root/beanie", 0, "bad");
digester.addCallParam("root/beanie", 1, "good");
ArrayList list = new ArrayList();
digester.push(list);
digester.parse(reader);
assertEquals("Wrong number of beans in list", 6, list.size());
PrimitiveBean bean = (PrimitiveBean) list.get(0);
assertTrue("Bean 0 property not called", bean.getSetBooleanCalled());
assertEquals("Bean 0 property incorrect", true, bean.getBoolean());
bean = (PrimitiveBean) list.get(1);
assertTrue("Bean 1 property not called", bean.getSetBooleanCalled());
assertEquals("Bean 1 property incorrect", false, bean.getBoolean());
bean = (PrimitiveBean) list.get(2);
// no attibute, no call is what's expected
assertTrue("Bean 2 property called", !bean.getSetBooleanCalled());
bean = (PrimitiveBean) list.get(3);
assertTrue("Bean 3 property not called", bean.getSetBooleanCalled());
assertEquals("Bean 3 property incorrect", true, bean.getBoolean());
bean = (PrimitiveBean) list.get(4);
assertTrue("Bean 4 property not called", bean.getSetBooleanCalled());
assertEquals("Bean 4 property incorrect", false, bean.getBoolean());
bean = (PrimitiveBean) list.get(5);
assertTrue("Bean 5 property not called", bean.getSetBooleanCalled());
assertEquals("Bean 5 property incorrect", false, bean.getBoolean());
}
public void testFromStack() throws Exception {
StringReader reader = new StringReader(
"<?xml version='1.0' ?><root><one/><two/><three/><four/><five/></root>");
Digester digester = new Digester();
Class [] params = { String.class };
digester.addObjectCreate("root/one", NamedBean.class);
digester.addSetNext("root/one", "add");
digester.addCallMethod("root/one", "setName", 1, params);
digester.addCallParam("root/one", 0, 2);
digester.addObjectCreate("root/two", NamedBean.class);
digester.addSetNext("root/two", "add");
digester.addCallMethod("root/two", "setName", 1, params);
digester.addCallParam("root/two", 0, 3);
digester.addObjectCreate("root/three", NamedBean.class);
digester.addSetNext("root/three", "add");
digester.addCallMethod("root/three", "setName", 1, params);
digester.addCallParam("root/three", 0, 4);
digester.addObjectCreate("root/four", NamedBean.class);
digester.addSetNext("root/four", "add");
digester.addCallMethod("root/four", "setName", 1, params);
digester.addCallParam("root/four", 0, 5);
digester.addObjectCreate("root/five", NamedBean.class);
digester.addSetNext("root/five", "add");
Class [] newParams = { String.class, String.class };
digester.addCallMethod("root/five", "test", 2, newParams);
digester.addCallParam("root/five", 0, 10);
digester.addCallParam("root/five", 1, 3);
// prepare stack
digester.push("That lamb was sure to go.");
digester.push("And everywhere that Mary went,");
digester.push("It's fleece was white as snow.");
digester.push("Mary had a little lamb,");
ArrayList list = new ArrayList();
digester.push(list);
digester.parse(reader);
assertEquals("Wrong number of beans in list", 5, list.size());
NamedBean bean = (NamedBean) list.get(0);
assertEquals("Parameter not set from stack (1)", "Mary had a little lamb,", bean.getName());
bean = (NamedBean) list.get(1);
assertEquals("Parameter not set from stack (2)", "It's fleece was white as snow.", bean.getName());
bean = (NamedBean) list.get(2);
assertEquals("Parameter not set from stack (3)", "And everywhere that Mary went,", bean.getName());
bean = (NamedBean) list.get(3);
assertEquals("Parameter not set from stack (4)", "That lamb was sure to go.", bean.getName());
bean = (NamedBean) list.get(4);
assertEquals("Out of stack not set to null", null , bean.getName());
}
public void testTwoCalls() throws Exception {
StringReader reader = new StringReader(
"<?xml version='1.0' ?><root>"
+ "<param class='int' coolness='true'>25</param>"
+ "<param class='long'>50</param>"
+ "<param class='float' coolness='false'>90</param></root>");
Digester digester = new Digester();
//SimpleLog log = new SimpleLog("{testTwoCalls:Digester]");
//log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
//digester.setLogger(log);
digester.addObjectCreate( "root/param", ParamBean.class );
digester.addSetNext( "root/param", "add" );
digester.addCallMethod( "root/param", "setThisAndThat", 2 );
digester.addCallParam( "root/param", 0, "class" );
digester.addCallParam( "root/param", 1 );
digester.addCallMethod( "root/param", "setCool", 1, new Class[] {boolean.class } );
digester.addCallParam( "root/param", 0, "coolness" );
ArrayList list = new ArrayList();
digester.push(list);
digester.parse(reader);
assertEquals("Wrong number of objects created", 3, list.size());
ParamBean bean = (ParamBean) list.get(0);
assertEquals("Coolness wrong (1)", true, bean.isCool());
assertEquals("This wrong (1)", "int", bean.getThis());
assertEquals("That wrong (1)", "25", bean.getThat());
bean = (ParamBean) list.get(1);
assertEquals("Coolness wrong (2)", false, bean.isCool());
assertEquals("This wrong (2)", "long", bean.getThis());
assertEquals("That wrong (2)", "50", bean.getThat());
bean = (ParamBean) list.get(2);
assertEquals("Coolness wrong (3)", false, bean.isCool());
assertEquals("This wrong (3)", "float", bean.getThis());
assertEquals("That wrong (3)", "90", bean.getThat());
}
public void testNestedBody() throws Exception {
StringReader reader = new StringReader(
"<?xml version='1.0' ?><root>"
+ "<spam>Simple</spam>"
+ "<spam>Complex<spam>Deep<spam>Deeper<spam>Deepest</spam></spam></spam></spam>"
+ "</root>");
Digester digester = new Digester();
//SimpleLog log = new SimpleLog("[testPrimitiveReading:Digester]");
//log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
//digester.setLogger(log);
digester.addObjectCreate("root/spam", NamedBean.class);
digester.addSetRoot("root/spam", "add");
digester.addCallMethod( "root/spam", "setName", 1 );
digester.addCallParam( "root/spam", 0);
digester.addObjectCreate("root/spam/spam", NamedBean.class);
digester.addSetRoot("root/spam/spam", "add");
digester.addCallMethod( "root/spam/spam", "setName", 1 );
digester.addCallParam( "root/spam/spam", 0);
digester.addObjectCreate("root/spam/spam/spam", NamedBean.class);
digester.addSetRoot("root/spam/spam/spam", "add");
digester.addCallMethod( "root/spam/spam/spam", "setName", 1 );
digester.addCallParam( "root/spam/spam/spam", 0);
digester.addObjectCreate("root/spam/spam/spam/spam", NamedBean.class);
digester.addSetRoot("root/spam/spam/spam/spam", "add");
digester.addCallMethod( "root/spam/spam/spam/spam", "setName", 1 );
digester.addCallParam( "root/spam/spam/spam/spam", 0);
ArrayList list = new ArrayList();
digester.push(list);
digester.parse(reader);
NamedBean bean = (NamedBean) list.get(0);
assertEquals("Wrong name (1)", "Simple", bean.getName());
// these are added in deepest first order by the addRootRule
bean = (NamedBean) list.get(4);
assertEquals("Wrong name (2)", "Complex", bean.getName());
bean = (NamedBean) list.get(3);
assertEquals("Wrong name (3)", "Deep", bean.getName());
bean = (NamedBean) list.get(2);
assertEquals("Wrong name (4)", "Deeper", bean.getName());
bean = (NamedBean) list.get(1);
assertEquals("Wrong name (5)", "Deepest", bean.getName());
}
public void testProcessingHook() throws Exception {
class TestCallMethodRule extends CallMethodRule {
Object result;
TestCallMethodRule(String methodName, int paramCount)
{
super(methodName, paramCount);
}
protected void processMethodCallResult(Object result) {
this.result = result;
}
}
StringReader reader = new StringReader(
"<?xml version='1.0' ?><root>"
+ "<param class='float' coolness='false'>90</param></root>");
Digester digester = new Digester();
//SimpleLog log = new SimpleLog("{testTwoCalls:Digester]");
//log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
//digester.setLogger(log);
digester.addObjectCreate( "root/param", ParamBean.class );
digester.addSetNext( "root/param", "add" );
TestCallMethodRule rule = new TestCallMethodRule( "setThisAndThat" , 2 );
digester.addRule( "root/param", rule );
digester.addCallParam( "root/param", 0, "class" );
digester.addCallParam( "root/param", 1, "coolness" );
ArrayList list = new ArrayList();
digester.push(list);
digester.parse(reader);
assertEquals("Wrong number of objects created", 1, list.size());
assertEquals("Result not passed into hook", "The Other", rule.result);
}
/** Test for the PathCallParamRule */
public void testPathCallParam() throws Exception {
String xml = "<?xml version='1.0'?><main>"
+ "<alpha><beta>Ignore this</beta></alpha>"
+ "<beta><epsilon><gamma>Ignore that</gamma></epsilon></beta>"
+ "</main>";
SimpleTestBean bean = new SimpleTestBean();
bean.setAlphaBeta("[UNSET]", "[UNSET]");
StringReader in = new StringReader(xml);
Digester digester = new Digester();
digester.setRules(new ExtendedBaseRules());
digester.addCallParamPath("*/alpha/?", 0);
digester.addCallParamPath("*/epsilon/?", 1);
digester.addCallMethod("main", "setAlphaBeta", 2);
digester.push(bean);
digester.parse(in);
assertEquals("Test alpha property setting", "main/alpha/beta" , bean.getAlpha());
assertEquals("Test beta property setting", "main/beta/epsilon/gamma" , bean.getBeta());
}
/**
* Test invoking an object which does not exist on the stack.
*/
public void testCallInvalidTarget() throws Exception {
Digester digester = new Digester();
digester.addObjectCreate("employee", HashMap.class);
// there should be only one object on the stack (index zero),
// so selecting a target object with index 1 on the object stack
// should result in an exception.
CallMethodRule r = new CallMethodRule(1, "put", 0);
digester.addRule("employee", r);
try {
digester.parse(getInputStream("Test5.xml"));
fail("Exception should be thrown for invalid target offset");
}
catch(SAXException e) {
// ok, exception expected
}
}
/**
* Test invoking an object which is at top-1 on the stack, like
* SetNextRule does...
*/
public void testCallNext() throws Exception {
Digester digester = new Digester();
digester.addObjectCreate("employee", HashMap.class);
digester.addObjectCreate("employee/address", Address.class);
digester.addSetNestedProperties("employee/address");
CallMethodRule r = new CallMethodRule(1, "put", 2);
digester.addRule("employee/address", r);
digester.addCallParam("employee/address/type", 0);
digester.addCallParam("employee/address", 1, 0);
HashMap map = (HashMap) digester.parse(getInputStream("Test5.xml"));
assertNotNull(map);
java.util.Set keys = map.keySet();
assertEquals(2, keys.size());
Address home = (Address) map.get("home");
assertNotNull(home);
assertEquals("HmZip", home.getZipCode());
Address office = (Address) map.get("office");
assertNotNull(office);
assertEquals("OfZip", office.getZipCode());
}
/**
* Test invoking an object which is at the root of the stack, like
* SetRoot does...
*/
public void testCallRoot() throws Exception {
Digester digester = new Digester();
digester.addObjectCreate("employee", HashMap.class);
digester.addObjectCreate("employee/address", Address.class);
digester.addSetNestedProperties("employee/address");
CallMethodRule r = new CallMethodRule(-1, "put", 2);
digester.addRule("employee/address", r);
digester.addCallParam("employee/address/type", 0);
digester.addCallParam("employee/address", 1, 0);
HashMap map = (HashMap) digester.parse(getInputStream("Test5.xml"));
assertNotNull(map);
java.util.Set keys = map.keySet();
assertEquals(2, keys.size());
Address home = (Address) map.get("home");
assertNotNull(home);
assertEquals("HmZip", home.getZipCode());
Address office = (Address) map.get("office");
assertNotNull(office);
assertEquals("OfZip", office.getZipCode());
}
// ------------------------------------------------ Utility Support Methods
/**
* Return an appropriate InputStream for the specified test file (which
* must be inside our current package.
*
* @param name Name of the test file we want
*
* @exception IOException if an input/output error occurs
*/
protected InputStream getInputStream(String name) throws IOException {
return (this.getClass().getResourceAsStream
("/org/apache/commons/digester/" + name));
}
}