001/* $Id: CallMethodRuleTestCase.java 1212522 2011-12-09 17:05:15Z sebb $ 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.commons.digester3; 020 021import static org.apache.commons.digester3.binder.DigesterLoader.newLoader; 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertNotNull; 024import static org.junit.Assert.assertTrue; 025import static org.junit.Assert.fail; 026 027import java.io.IOException; 028import java.io.InputStream; 029import java.io.StringReader; 030import java.util.ArrayList; 031import java.util.HashMap; 032import java.util.Set; 033 034import org.apache.commons.digester3.binder.AbstractRulesModule; 035import org.junit.Test; 036import org.xml.sax.SAXException; 037 038//import org.apache.commons.logging.impl.SimpleLog; 039 040/** 041 * <p> 042 * Tests for the <code>CallMethodRule</code> and associated <code>CallParamRule</code>. 043 * 044 * @author Christopher Lenz 045 */ 046public class CallMethodRuleTestCase 047{ 048 049 /** 050 * Test method calls with the CallMethodRule rule. It should be possible to call a method with no arguments using 051 * several rule syntaxes. 052 */ 053 @Test 054 public void testBasic() 055 throws SAXException, IOException 056 { 057 Digester digester = newLoader( new AbstractRulesModule() 058 { 059 060 @Override 061 protected void configure() 062 { 063 forPattern( "employee" ).createObject().ofType( Employee.class ); 064 // try all syntax permutations 065 forPattern( "employee" ).callMethod( "toString" ).withParamCount( 0 ).withParamTypes( (Class[]) null ) 066 .then() 067 .callMethod( "toString" ).withParamCount( 0 ).withParamTypes( (String[]) null ) 068 .then() 069 .callMethod( "toString" ).withParamCount( 0 ).withParamTypes( new Class[] {} ) 070 .then() 071 .callMethod( "toString" ).withParamCount( 0 ).withParamTypes( new String[] {} ) 072 .then() 073 .callMethod( "toString" ); 074 } 075 076 }).newDigester(); 077 078 // Parse our test input. 079 // An exception will be thrown if the method can't be found 080 Employee root1 = digester.parse( getInputStream( "Test5.xml" ) ); 081 assertNotNull( root1 ); 082 } 083 084 /** 085 * Test method calls with the CallMethodRule reading from the element body, with no CallParamMethod rules added. 086 */ 087 @Test 088 public void testCallMethodOnly() 089 throws Exception 090 { 091 Digester digester = newLoader( new AbstractRulesModule() 092 { 093 094 @Override 095 protected void configure() 096 { 097 forPattern( "employee" ).createObject().ofType( Employee.class ); 098 forPattern( "employee/firstName" ).callMethod( "setFirstName" ).usingElementBodyAsArgument(); 099 forPattern( "employee/lastName" ).callMethod( "setLastName" ).usingElementBodyAsArgument(); 100 } 101 102 }).newDigester(); 103 104 // Parse our test input 105 Employee employee = digester.parse( getInputStream( "Test9.xml" ) ); 106 assertNotNull( "parsed an employee", employee ); 107 108 // Validate that the property setters were called 109 assertEquals( "Set first name", "First Name", employee.getFirstName() ); 110 assertEquals( "Set last name", "Last Name", employee.getLastName() ); 111 } 112 113 /** 114 * Test CallMethodRule variants which specify the classes of the parameters to target methods. String, int, boolean, 115 * float should all be acceptable as parameter types. 116 */ 117 @Test 118 public void testSettingProperties() 119 throws SAXException, IOException 120 { 121 Digester digester = newLoader( new AbstractRulesModule() 122 { 123 124 @Override 125 protected void configure() 126 { 127 forPattern( "employee" ).createObject().ofType( Employee.class ) 128 .then() 129 .callMethod( "setLastName" ).withParamTypes( "java.lang.String" ); 130 forPattern( "employee/lastName" ).callParam().ofIndex( 0 ); 131 } 132 133 }).newDigester(); 134 135 // Parse our test input 136 137 // an exception will be thrown if the method can't be found 138 Employee employee = digester.parse( getInputStream( "Test5.xml" ) ); 139 assertEquals( "Failed to call Employee.setLastName", "Last Name", employee.getLastName() ); 140 141 digester = newLoader( new AbstractRulesModule() 142 { 143 144 @Override 145 protected void configure() 146 { 147 forPattern( "employee" ).createObject().ofType( Employee.class ) 148 .then() 149 .callMethod( "setAge" ).withParamTypes( int.class ); 150 forPattern( "employee/age" ).callParam(); 151 } 152 153 }).newDigester(); 154 155 // Parse our test input 156 // an exception will be thrown if the method can't be found 157 employee = digester.parse( getInputStream( "Test5.xml" ) ); 158 assertEquals( "Failed to call Employee.setAge", 21, employee.getAge() ); 159 160 digester = newLoader( new AbstractRulesModule() 161 { 162 163 @Override 164 protected void configure() 165 { 166 forPattern( "employee" ).createObject().ofType( Employee.class ) 167 .then() 168 .callMethod( "setActive" ).withParamTypes( boolean.class ); 169 forPattern( "employee/active" ).callParam(); 170 } 171 172 }).newDigester(); 173 174 // Parse our test input 175 // an exception will be thrown if the method can't be found 176 employee = digester.parse( getInputStream( "Test5.xml" ) ); 177 assertEquals( "Failed to call Employee.setActive", true, employee.isActive() ); 178 179 digester = newLoader( new AbstractRulesModule() 180 { 181 182 @Override 183 protected void configure() 184 { 185 forPattern( "employee" ).createObject().ofType( Employee.class ) 186 .then() 187 .callMethod( "setSalary" ).withParamTypes( float.class ); 188 forPattern( "employee/salary" ).callParam(); 189 } 190 191 }).newDigester(); 192 193 // Parse our test input 194 // an exception will be thrown if the method can't be found 195 employee = digester.parse( getInputStream( "Test5.xml" ) ); 196 assertEquals( "Failed to call Employee.setSalary", 1000000.0f, employee.getSalary(), 0.1f ); 197 } 198 199 /** 200 * This tests the call methods params enhancement that provides for more complex stack-based calls. 201 */ 202 @Test 203 public void testParamsFromStack() 204 throws SAXException, IOException 205 { 206 Digester digester = newLoader( new AbstractRulesModule() 207 { 208 209 @Override 210 protected void configure() 211 { 212 forPattern( "map" ).createObject().ofType( HashMap.class ) 213 .then() 214 .callMethod( "put" ).withParamCount( 2 ); 215 forPattern( "map/key" ).createObject().ofType( AlphaBean.class ) 216 .then() 217 .setProperties() 218 .then() 219 .callParam().fromStack( true ); 220 forPattern( "map/value" ).createObject().ofType( BetaBean.class ) 221 .then() 222 .setProperties() 223 .then() 224 .callParam().ofIndex( 1 ).fromStack( true ); 225 } 226 227 }).newDigester(); 228 229 StringBuilder xml = 230 new StringBuilder().append( "<?xml version='1.0'?>" ).append( "<map>" ).append( " <key name='The key'/>" ).append( " <value name='The value'/>" ).append( "</map>" ); 231 232 HashMap<AlphaBean, BetaBean> map = digester.parse( new StringReader( xml.toString() ) ); 233 234 assertNotNull( map ); 235 assertEquals( 1, map.size() ); 236 assertEquals( "The key", map.keySet().iterator().next().getName() ); 237 assertEquals( "The value", map.values().iterator().next().getName() ); 238 } 239 240 /** 241 * Test that the target object for a CallMethodRule is the object that was on top of the object stack when the 242 * CallMethodRule fired, even when other rules fire between the CallMethodRule and its associated CallParamRules. 243 * <p> 244 * The current implementation of CallMethodRule ensures this works by firing only at the end of the tag that 245 * CallMethodRule triggered on. 246 */ 247 @Test 248 public void testOrderNestedPartA() 249 throws Exception 250 { 251 Digester digester = newLoader( new AbstractRulesModule() 252 { 253 254 @Override 255 protected void configure() 256 { 257 // Here, we use the "grandchild element name" as a parameter to 258 // the created element, to ensure that all the params aren't 259 // avaiable to the CallMethodRule until some other rules have fired, 260 // in particular an ObjectCreateRule. The CallMethodRule should still 261 // function correctly in this scenario. 262 forPattern( "toplevel/element" ).createObject().ofType( NamedBean.class ) 263 .then() 264 .callMethod( "setName" ).withParamCount( 1 ); 265 forPattern( "toplevel/element/element/element" ).callParam().ofIndex( 0 ).fromAttribute( "name" ); 266 forPattern( "toplevel/element/element" ).createObject().ofType( NamedBean.class ); 267 } 268 269 }).newDigester(); 270 271 // Parse our test input 272 // an exception will be thrown if the method can't be found 273 NamedBean root1 = digester.parse( getInputStream( "Test8.xml" ) ); 274 275 276 // if the CallMethodRule were to incorrectly invoke the method call 277 // on the second-created NamedBean instance, then the root one would 278 // have a null name. If it works correctly, the target element will 279 // be the first-created (root) one, despite the fact that a second 280 // object instance was created between the firing of the 281 // CallMethodRule and its associated CallParamRule. 282 assertEquals( "Wrong method call order", "C", root1.getName() ); 283 } 284 285 /** 286 * Test nested CallMethod rules. 287 * <p> 288 * The current implementation of CallMethodRule, in which the method is invoked in its end() method, causes 289 * behaviour which some users find non-intuitive. In this test it can be seen to "reverse" the order of data 290 * processed. However this is the way CallMethodRule has always behaved, and it is expected that apps out there rely 291 * on this call order so this test is present to ensure that no-one changes this behaviour. 292 */ 293 @Test 294 public void testOrderNestedPartB() 295 throws Exception 296 { 297 Digester digester = newLoader( new AbstractRulesModule() 298 { 299 300 @Override 301 protected void configure() 302 { 303 forPattern( "*/element" ).callMethod( "append" ).withParamCount( 1 ) 304 .then() 305 .callParam().ofIndex( 0 ).fromAttribute( "name" ); 306 } 307 308 }).newDigester(); 309 310 // Configure the digester as required 311 StringBuilder word = new StringBuilder(); 312 digester.push( word ); 313 314 // Parse our test input 315 Object root1 = null; 316 try 317 { 318 // an exception will be thrown if the method can't be found 319 root1 = digester.parse( getInputStream( "Test8.xml" ) ); 320 assertNotNull( root1 ); 321 } 322 catch ( Throwable t ) 323 { 324 // this means that the method can't be found and so the test fails 325 fail( "Digester threw Exception: " + t ); 326 } 327 328 assertEquals( "Wrong method call order", "CBA", word.toString() ); 329 } 330 331 @Test 332 public void testPrimitiveReading() 333 throws Exception 334 { 335 StringReader reader = 336 new StringReader( "<?xml version='1.0' ?><root><bean good='true'/><bean good='false'/><bean/>" 337 + "<beanie bad='Fee Fie Foe Fum' good='true'/><beanie bad='Fee Fie Foe Fum' good='false'/>" 338 + "<beanie bad='Fee Fie Foe Fum'/></root>" ); 339 340 Digester digester = new Digester(); 341 342 // SimpleLog log = new SimpleLog("[testPrimitiveReading:Digester]"); 343 // log.setLevel(SimpleLog.LOG_LEVEL_TRACE); 344 // digester.setLogger(log); 345 346 digester.addObjectCreate( "root/bean", PrimitiveBean.class ); 347 digester.addSetNext( "root/bean", "add" ); 348 Class<?>[] params = { Boolean.TYPE }; 349 digester.addCallMethod( "root/bean", "setBoolean", 1, params ); 350 digester.addCallParam( "root/bean", 0, "good" ); 351 352 digester.addObjectCreate( "root/beanie", PrimitiveBean.class ); 353 digester.addSetNext( "root/beanie", "add" ); 354 Class<?>[] beanieParams = { String.class, Boolean.TYPE }; 355 digester.addCallMethod( "root/beanie", "testSetBoolean", 2, beanieParams ); 356 digester.addCallParam( "root/beanie", 0, "bad" ); 357 digester.addCallParam( "root/beanie", 1, "good" ); 358 359 ArrayList<PrimitiveBean> list = new ArrayList<PrimitiveBean>(); 360 digester.push( list ); 361 digester.parse( reader ); 362 363 assertEquals( "Wrong number of beans in list", 6, list.size() ); 364 PrimitiveBean bean = list.get( 0 ); 365 assertTrue( "Bean 0 property not called", bean.getSetBooleanCalled() ); 366 assertEquals( "Bean 0 property incorrect", true, bean.getBoolean() ); 367 bean = list.get( 1 ); 368 assertTrue( "Bean 1 property not called", bean.getSetBooleanCalled() ); 369 assertEquals( "Bean 1 property incorrect", false, bean.getBoolean() ); 370 bean = list.get( 2 ); 371 // no attibute, no call is what's expected 372 assertTrue( "Bean 2 property called", !bean.getSetBooleanCalled() ); 373 bean = list.get( 3 ); 374 assertTrue( "Bean 3 property not called", bean.getSetBooleanCalled() ); 375 assertEquals( "Bean 3 property incorrect", true, bean.getBoolean() ); 376 bean = list.get( 4 ); 377 assertTrue( "Bean 4 property not called", bean.getSetBooleanCalled() ); 378 assertEquals( "Bean 4 property incorrect", false, bean.getBoolean() ); 379 bean = list.get( 5 ); 380 assertTrue( "Bean 5 property not called", bean.getSetBooleanCalled() ); 381 assertEquals( "Bean 5 property incorrect", false, bean.getBoolean() ); 382 } 383 384 @Test 385 public void testFromStack() 386 throws Exception 387 { 388 389 StringReader reader = 390 new StringReader( "<?xml version='1.0' ?><root><one/><two/><three/><four/><five/></root>" ); 391 392 Digester digester = new Digester(); 393 394 Class<?>[] params = { String.class }; 395 396 digester.addObjectCreate( "root/one", NamedBean.class ); 397 digester.addSetNext( "root/one", "add" ); 398 digester.addCallMethod( "root/one", "setName", 1, params ); 399 digester.addCallParam( "root/one", 0, 2 ); 400 401 digester.addObjectCreate( "root/two", NamedBean.class ); 402 digester.addSetNext( "root/two", "add" ); 403 digester.addCallMethod( "root/two", "setName", 1, params ); 404 digester.addCallParam( "root/two", 0, 3 ); 405 406 digester.addObjectCreate( "root/three", NamedBean.class ); 407 digester.addSetNext( "root/three", "add" ); 408 digester.addCallMethod( "root/three", "setName", 1, params ); 409 digester.addCallParam( "root/three", 0, 4 ); 410 411 digester.addObjectCreate( "root/four", NamedBean.class ); 412 digester.addSetNext( "root/four", "add" ); 413 digester.addCallMethod( "root/four", "setName", 1, params ); 414 digester.addCallParam( "root/four", 0, 5 ); 415 416 digester.addObjectCreate( "root/five", NamedBean.class ); 417 digester.addSetNext( "root/five", "add" ); 418 Class<?>[] newParams = { String.class, String.class }; 419 digester.addCallMethod( "root/five", "test", 2, newParams ); 420 digester.addCallParam( "root/five", 0, 10 ); 421 digester.addCallParam( "root/five", 1, 3 ); 422 423 // prepare stack 424 digester.push( "That lamb was sure to go." ); 425 digester.push( "And everywhere that Mary went," ); 426 digester.push( "It's fleece was white as snow." ); 427 digester.push( "Mary had a little lamb," ); 428 429 ArrayList<NamedBean> list = new ArrayList<NamedBean>(); 430 digester.push( list ); 431 digester.parse( reader ); 432 433 assertEquals( "Wrong number of beans in list", 5, list.size() ); 434 NamedBean bean = list.get( 0 ); 435 assertEquals( "Parameter not set from stack (1)", "Mary had a little lamb,", bean.getName() ); 436 bean = list.get( 1 ); 437 assertEquals( "Parameter not set from stack (2)", "It's fleece was white as snow.", bean.getName() ); 438 bean = list.get( 2 ); 439 assertEquals( "Parameter not set from stack (3)", "And everywhere that Mary went,", bean.getName() ); 440 bean = list.get( 3 ); 441 assertEquals( "Parameter not set from stack (4)", "That lamb was sure to go.", bean.getName() ); 442 bean = list.get( 4 ); 443 assertEquals( "Out of stack not set to null", null, bean.getName() ); 444 } 445 446 @Test 447 public void testTwoCalls() 448 throws Exception 449 { 450 451 StringReader reader = 452 new StringReader( "<?xml version='1.0' ?><root>" + "<param class='int' coolness='true'>25</param>" 453 + "<param class='long'>50</param>" + "<param class='float' coolness='false'>90</param></root>" ); 454 455 Digester digester = new Digester(); 456 // SimpleLog log = new SimpleLog("{testTwoCalls:Digester]"); 457 // log.setLevel(SimpleLog.LOG_LEVEL_TRACE); 458 // digester.setLogger(log); 459 460 digester.addObjectCreate( "root/param", ParamBean.class ); 461 digester.addSetNext( "root/param", "add" ); 462 digester.addCallMethod( "root/param", "setThisAndThat", 2 ); 463 digester.addCallParam( "root/param", 0, "class" ); 464 digester.addCallParam( "root/param", 1 ); 465 digester.addCallMethod( "root/param", "setCool", 1, new Class[] { boolean.class } ); 466 digester.addCallParam( "root/param", 0, "coolness" ); 467 468 ArrayList<ParamBean> list = new ArrayList<ParamBean>(); 469 digester.push( list ); 470 digester.parse( reader ); 471 472 assertEquals( "Wrong number of objects created", 3, list.size() ); 473 ParamBean bean = list.get( 0 ); 474 assertEquals( "Coolness wrong (1)", true, bean.isCool() ); 475 assertEquals( "This wrong (1)", "int", bean.getThis() ); 476 assertEquals( "That wrong (1)", "25", bean.getThat() ); 477 bean = list.get( 1 ); 478 assertEquals( "Coolness wrong (2)", false, bean.isCool() ); 479 assertEquals( "This wrong (2)", "long", bean.getThis() ); 480 assertEquals( "That wrong (2)", "50", bean.getThat() ); 481 bean = list.get( 2 ); 482 assertEquals( "Coolness wrong (3)", false, bean.isCool() ); 483 assertEquals( "This wrong (3)", "float", bean.getThis() ); 484 assertEquals( "That wrong (3)", "90", bean.getThat() ); 485 } 486 487 @Test 488 public void testNestedBody() 489 throws Exception 490 { 491 492 StringReader reader = 493 new StringReader( "<?xml version='1.0' ?><root>" + "<spam>Simple</spam>" 494 + "<spam>Complex<spam>Deep<spam>Deeper<spam>Deepest</spam></spam></spam></spam>" + "</root>" ); 495 496 Digester digester = new Digester(); 497 498 // SimpleLog log = new SimpleLog("[testPrimitiveReading:Digester]"); 499 // log.setLevel(SimpleLog.LOG_LEVEL_TRACE); 500 // digester.setLogger(log); 501 502 digester.addObjectCreate( "root/spam", NamedBean.class ); 503 digester.addSetRoot( "root/spam", "add" ); 504 digester.addCallMethod( "root/spam", "setName", 1 ); 505 digester.addCallParam( "root/spam", 0 ); 506 507 digester.addObjectCreate( "root/spam/spam", NamedBean.class ); 508 digester.addSetRoot( "root/spam/spam", "add" ); 509 digester.addCallMethod( "root/spam/spam", "setName", 1 ); 510 digester.addCallParam( "root/spam/spam", 0 ); 511 512 digester.addObjectCreate( "root/spam/spam/spam", NamedBean.class ); 513 digester.addSetRoot( "root/spam/spam/spam", "add" ); 514 digester.addCallMethod( "root/spam/spam/spam", "setName", 1 ); 515 digester.addCallParam( "root/spam/spam/spam", 0 ); 516 517 digester.addObjectCreate( "root/spam/spam/spam/spam", NamedBean.class ); 518 digester.addSetRoot( "root/spam/spam/spam/spam", "add" ); 519 digester.addCallMethod( "root/spam/spam/spam/spam", "setName", 1 ); 520 digester.addCallParam( "root/spam/spam/spam/spam", 0 ); 521 522 ArrayList<NamedBean> list = new ArrayList<NamedBean>(); 523 digester.push( list ); 524 digester.parse( reader ); 525 526 NamedBean bean = list.get( 0 ); 527 assertEquals( "Wrong name (1)", "Simple", bean.getName() ); 528 // these are added in deepest first order by the addRootRule 529 bean = list.get( 4 ); 530 assertEquals( "Wrong name (2)", "Complex", bean.getName() ); 531 bean = list.get( 3 ); 532 assertEquals( "Wrong name (3)", "Deep", bean.getName() ); 533 bean = list.get( 2 ); 534 assertEquals( "Wrong name (4)", "Deeper", bean.getName() ); 535 bean = list.get( 1 ); 536 assertEquals( "Wrong name (5)", "Deepest", bean.getName() ); 537 } 538 539 @Test 540 public void testProcessingHook() 541 throws Exception 542 { 543 544 class TestCallMethodRule 545 extends CallMethodRule 546 { 547 Object result; 548 549 TestCallMethodRule( String methodName, int paramCount ) 550 { 551 super( methodName, paramCount ); 552 } 553 554 @Override 555 protected void processMethodCallResult( Object result ) 556 { 557 this.result = result; 558 } 559 } 560 561 StringReader reader = 562 new StringReader( "<?xml version='1.0' ?><root>" 563 + "<param class='float' coolness='false'>90</param></root>" ); 564 565 Digester digester = new Digester(); 566 // SimpleLog log = new SimpleLog("{testTwoCalls:Digester]"); 567 // log.setLevel(SimpleLog.LOG_LEVEL_TRACE); 568 // digester.setLogger(log); 569 570 digester.addObjectCreate( "root/param", ParamBean.class ); 571 digester.addSetNext( "root/param", "add" ); 572 TestCallMethodRule rule = new TestCallMethodRule( "setThisAndThat", 2 ); 573 digester.addRule( "root/param", rule ); 574 digester.addCallParam( "root/param", 0, "class" ); 575 digester.addCallParam( "root/param", 1, "coolness" ); 576 577 ArrayList<ParamBean> list = new ArrayList<ParamBean>(); 578 digester.push( list ); 579 digester.parse( reader ); 580 581 assertEquals( "Wrong number of objects created", 1, list.size() ); 582 assertEquals( "Result not passed into hook", "The Other", rule.result ); 583 } 584 585 /** Test for the PathCallParamRule */ 586 @Test 587 public void testPathCallParam() 588 throws Exception 589 { 590 String xml = 591 "<?xml version='1.0'?><main>" + "<alpha><beta>Ignore this</beta></alpha>" 592 + "<beta><epsilon><gamma>Ignore that</gamma></epsilon></beta>" + "</main>"; 593 594 SimpleTestBean bean = new SimpleTestBean(); 595 bean.setAlphaBeta( "[UNSET]", "[UNSET]" ); 596 597 StringReader in = new StringReader( xml ); 598 Digester digester = new Digester(); 599 digester.setRules( new ExtendedBaseRules() ); 600 digester.addCallParamPath( "*/alpha/?", 0 ); 601 digester.addCallParamPath( "*/epsilon/?", 1 ); 602 digester.addCallMethod( "main", "setAlphaBeta", 2 ); 603 604 digester.push( bean ); 605 606 digester.parse( in ); 607 608 assertEquals( "Test alpha property setting", "main/alpha/beta", bean.getAlpha() ); 609 assertEquals( "Test beta property setting", "main/beta/epsilon/gamma", bean.getBeta() ); 610 } 611 612 /** 613 * Test invoking an object which does not exist on the stack. 614 */ 615 @Test 616 public void testCallInvalidTarget() 617 throws Exception 618 { 619 620 Digester digester = new Digester(); 621 digester.addObjectCreate( "employee", HashMap.class ); 622 623 // there should be only one object on the stack (index zero), 624 // so selecting a target object with index 1 on the object stack 625 // should result in an exception. 626 CallMethodRule r = new CallMethodRule( 1, "put", 0 ); 627 digester.addRule( "employee", r ); 628 629 try 630 { 631 digester.parse( getInputStream( "Test5.xml" ) ); 632 fail( "Exception should be thrown for invalid target offset" ); 633 } 634 catch ( SAXException e ) 635 { 636 // ok, exception expected 637 } 638 } 639 640 /** 641 * Test invoking an object which is at top-1 on the stack, like SetNextRule does... 642 */ 643 @Test 644 public void testCallNext() 645 throws Exception 646 { 647 648 Digester digester = new Digester(); 649 digester.addObjectCreate( "employee", HashMap.class ); 650 651 digester.addObjectCreate( "employee/address", Address.class ); 652 digester.addSetNestedProperties( "employee/address" ); 653 CallMethodRule r = new CallMethodRule( 1, "put", 2 ); 654 digester.addRule( "employee/address", r ); 655 digester.addCallParam( "employee/address/type", 0 ); 656 digester.addCallParam( "employee/address", 1, 0 ); 657 658 HashMap<String, Address> map = digester.parse( getInputStream( "Test5.xml" ) ); 659 660 assertNotNull( map ); 661 Set<String> keys = map.keySet(); 662 assertEquals( 2, keys.size() ); 663 Address home = map.get( "home" ); 664 assertNotNull( home ); 665 assertEquals( "HmZip", home.getZipCode() ); 666 Address office = map.get( "office" ); 667 assertNotNull( office ); 668 assertEquals( "OfZip", office.getZipCode() ); 669 } 670 671 /** 672 * Test invoking an object which is at the root of the stack, like SetRoot does... 673 */ 674 @Test 675 public void testCallRoot() 676 throws Exception 677 { 678 679 Digester digester = new Digester(); 680 digester.addObjectCreate( "employee", HashMap.class ); 681 682 digester.addObjectCreate( "employee/address", Address.class ); 683 digester.addSetNestedProperties( "employee/address" ); 684 CallMethodRule r = new CallMethodRule( -1, "put", 2 ); 685 digester.addRule( "employee/address", r ); 686 digester.addCallParam( "employee/address/type", 0 ); 687 digester.addCallParam( "employee/address", 1, 0 ); 688 689 HashMap<String, Address> map = digester.parse( getInputStream( "Test5.xml" ) ); 690 691 assertNotNull( map ); 692 Set<String> keys = map.keySet(); 693 assertEquals( 2, keys.size() ); 694 Address home = map.get( "home" ); 695 assertNotNull( home ); 696 assertEquals( "HmZip", home.getZipCode() ); 697 Address office = map.get( "office" ); 698 assertNotNull( office ); 699 assertEquals( "OfZip", office.getZipCode() ); 700 } 701 702 // ------------------------------------------------ Utility Support Methods 703 704 /** 705 * Return an appropriate InputStream for the specified test file (which must be inside our current package. 706 * 707 * @param name Name of the test file we want 708 * @exception IOException if an input/output error occurs 709 */ 710 protected InputStream getInputStream( String name ) 711 throws IOException 712 { 713 714 return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) ); 715 716 } 717 718}