You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/08/30 13:52:19 UTC

[commons-digester] branch master updated: We don't use author tags

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-digester.git


The following commit(s) were added to refs/heads/master by this push:
     new 9b66903f We don't use author tags
9b66903f is described below

commit 9b66903faa3eb334b0b65398bb17aa2ad84fc8f2
Author: Gary Gregory <gg...@rocketsoftware.com>
AuthorDate: Tue Aug 30 09:52:14 2022 -0400

    We don't use author tags
---
 .../commons/digester3/RecordedInvocation.java      |  294 ++--
 .../commons/digester3/CallMethodRuleTestCase.java  | 1434 ++++++++++----------
 .../commons/digester3/DTDValidationTestCase.java   |  274 ++--
 .../apache/commons/digester3/DigesterTestCase.java | 1422 ++++++++++---------
 .../digester3/ExtendedBaseRulesTestCase.java       |  822 ++++++-----
 .../org/apache/commons/digester3/NamedBean.java    |  116 +-
 .../commons/digester3/NodeCreateRuleTestCase.java  | 1084 ++++++++-------
 .../digester3/ObjectCreationFactoryTestImpl.java   |   86 +-
 .../commons/digester3/ObjectParamRuleTestCase.java |  184 ++-
 .../digester3/OtherTestObjectCreationFactory.java  |   58 +-
 .../org/apache/commons/digester3/ParamBean.java    |  128 +-
 .../apache/commons/digester3/PrimitiveBean.java    |  112 +-
 .../commons/digester3/RegexRulesTestCase.java      |  408 +++---
 .../org/apache/commons/digester3/RuleTestCase.java | 1135 ++++++++--------
 .../commons/digester3/RulesBaseTestCase.java       |  544 ++++----
 .../org/apache/commons/digester3/TestBean.java     |  632 +++++----
 .../commons/digester3/TestEntityResolution.java    |  112 +-
 .../commons/digester3/TestFactoryCreate.java       |  452 +++---
 .../org/apache/commons/digester3/TestRule.java     |  380 +++---
 .../org/apache/commons/digester3/TestRuleSet.java  |  198 ++-
 .../WithDefaultsRulesWrapperTestCase.java          |  206 ++-
 .../apache/commons/digester3/plugins/Utils.java    |   84 +-
 .../substitution/VariableExpansionTestCase.java    |  652 +++++----
 .../digester3/xmlrules/CallParamTestObject.java    |  164 ++-
 .../xmlrules/DigesterRulesSourceTestImpl.java      |   93 +-
 .../commons/digester3/xmlrules/ObjectTestImpl.java |  224 ++-
 .../xmlrules/ThrowExceptionCreationFactory.java    |   78 +-
 27 files changed, 5660 insertions(+), 5716 deletions(-)

diff --git a/core/src/main/java/org/apache/commons/digester3/RecordedInvocation.java b/core/src/main/java/org/apache/commons/digester3/RecordedInvocation.java
index 0b56a985..9a3d7392 100644
--- a/core/src/main/java/org/apache/commons/digester3/RecordedInvocation.java
+++ b/core/src/main/java/org/apache/commons/digester3/RecordedInvocation.java
@@ -1,147 +1,147 @@
-package org.apache.commons.digester3;
-
-/*
- * 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.
- */
-
-import java.lang.reflect.Method;
-
-/**
- * Detached representation of a method invocation.
- * From Commons [proxy] v2 branch.
- * @author James Carman
- * @since 3.2
- */
-final class RecordedInvocation
-{
-
-    //******************************************************************************************************************
-    // Fields
-    //******************************************************************************************************************
-
-    private final Method invokedMethod;
-
-    private final Object[] arguments;
-
-    //******************************************************************************************************************
-    // Constructors
-    //******************************************************************************************************************
-
-    /**
-     * Create a new RecordedInvocation instance.
-     *
-     * @param invokedMethod
-     * @param arguments
-     */
-    public RecordedInvocation( final Method invokedMethod, final Object[] arguments )
-    {
-        this.invokedMethod = invokedMethod;
-        this.arguments = arguments;
-    }
-
-    //******************************************************************************************************************
-    // Canonical Methods
-    //******************************************************************************************************************
-
-    /**
-     * Get the invokedMethod.
-     *
-     * @return Method
-     */
-    public Method getInvokedMethod()
-    {
-        return invokedMethod;
-    }
-
-    /**
-     * Get the arguments.
-     *
-     * @return Object[]
-     */
-    public Object[] getArguments()
-    {
-        return arguments;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String toString()
-    {
-        final StringBuilder buffer = new StringBuilder();
-        buffer.append( invokedMethod.getDeclaringClass().getName() );
-        buffer.append( "." );
-        buffer.append( invokedMethod.getName() );
-        buffer.append( "(" );
-        final int count = arguments.length;
-        for ( int i = 0; i < count; i++ )
-        {
-            final Object arg = arguments[i];
-            if ( i > 0 )
-            {
-                buffer.append( ", " );
-            }
-            convert( buffer, arg );
-        }
-        buffer.append( ")" );
-        return buffer.toString();
-    }
-
-    /**
-     * Add a string representation of {@code input} to {@code buffer}.
-     *
-     * @param buffer the buffer to append the string representation of the input object.
-     * @param input the input object has to be serialized to string.
-     */
-    protected void convert( final StringBuilder buffer, final Object input )
-    {
-        if ( input == null )
-        {
-            buffer.append( "<null>" );
-            return;
-        }
-
-        // Primitive types, and non-object arrays
-        // use toString().
-        if ( !( input instanceof Object[] ) )
-        {
-            buffer.append( input.toString() );
-        }
-        else
-        {
-            buffer.append( "(" );
-            buffer.append( input.getClass().getSimpleName() );
-            buffer.append( "){" );
-            final Object[] array = (Object[]) input;
-            final int count = array.length;
-            for ( int i = 0; i < count; i++ )
-            {
-                if ( i > 0 )
-                {
-                    buffer.append( ", " );
-                }
-                // We use convert() again, because it could be a multi-dimensional array
-                // where each element must be converted.
-                convert( buffer, array[i] );
-            }
-            buffer.append( "}" );
-        }
-    }
-
-}
+package org.apache.commons.digester3;
+
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+
+/**
+ * Detached representation of a method invocation.
+ * From Commons [proxy] v2 branch.
+ *
+ * @since 3.2
+ */
+final class RecordedInvocation
+{
+
+    //******************************************************************************************************************
+    // Fields
+    //******************************************************************************************************************
+
+    private final Method invokedMethod;
+
+    private final Object[] arguments;
+
+    //******************************************************************************************************************
+    // Constructors
+    //******************************************************************************************************************
+
+    /**
+     * Create a new RecordedInvocation instance.
+     *
+     * @param invokedMethod
+     * @param arguments
+     */
+    public RecordedInvocation( final Method invokedMethod, final Object[] arguments )
+    {
+        this.invokedMethod = invokedMethod;
+        this.arguments = arguments;
+    }
+
+    //******************************************************************************************************************
+    // Canonical Methods
+    //******************************************************************************************************************
+
+    /**
+     * Get the invokedMethod.
+     *
+     * @return Method
+     */
+    public Method getInvokedMethod()
+    {
+        return invokedMethod;
+    }
+
+    /**
+     * Get the arguments.
+     *
+     * @return Object[]
+     */
+    public Object[] getArguments()
+    {
+        return arguments;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString()
+    {
+        final StringBuilder buffer = new StringBuilder();
+        buffer.append( invokedMethod.getDeclaringClass().getName() );
+        buffer.append( "." );
+        buffer.append( invokedMethod.getName() );
+        buffer.append( "(" );
+        final int count = arguments.length;
+        for ( int i = 0; i < count; i++ )
+        {
+            final Object arg = arguments[i];
+            if ( i > 0 )
+            {
+                buffer.append( ", " );
+            }
+            convert( buffer, arg );
+        }
+        buffer.append( ")" );
+        return buffer.toString();
+    }
+
+    /**
+     * Add a string representation of {@code input} to {@code buffer}.
+     *
+     * @param buffer the buffer to append the string representation of the input object.
+     * @param input the input object has to be serialized to string.
+     */
+    protected void convert( final StringBuilder buffer, final Object input )
+    {
+        if ( input == null )
+        {
+            buffer.append( "<null>" );
+            return;
+        }
+
+        // Primitive types, and non-object arrays
+        // use toString().
+        if ( !( input instanceof Object[] ) )
+        {
+            buffer.append( input.toString() );
+        }
+        else
+        {
+            buffer.append( "(" );
+            buffer.append( input.getClass().getSimpleName() );
+            buffer.append( "){" );
+            final Object[] array = (Object[]) input;
+            final int count = array.length;
+            for ( int i = 0; i < count; i++ )
+            {
+                if ( i > 0 )
+                {
+                    buffer.append( ", " );
+                }
+                // We use convert() again, because it could be a multi-dimensional array
+                // where each element must be converted.
+                convert( buffer, array[i] );
+            }
+            buffer.append( "}" );
+        }
+    }
+
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/CallMethodRuleTestCase.java b/core/src/test/java/org/apache/commons/digester3/CallMethodRuleTestCase.java
index fd31a015..3fdd1ce4 100644
--- a/core/src/test/java/org/apache/commons/digester3/CallMethodRuleTestCase.java
+++ b/core/src/test/java/org/apache/commons/digester3/CallMethodRuleTestCase.java
@@ -1,718 +1,716 @@
-/* $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.digester3;
-
-import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Set;
-
-import org.apache.commons.digester3.binder.AbstractRulesModule;
-import org.junit.Test;
-import org.xml.sax.SAXException;
-
-//import org.apache.commons.logging.impl.SimpleLog;
-
-/**
- * <p>
- * Tests for the {@code CallMethodRule} and associated {@code CallParamRule}.
- *
- * @author Christopher Lenz
- */
-public class CallMethodRuleTestCase
-{
-
-    /**
-     * Test method calls with the CallMethodRule rule. It should be possible to call a method with no arguments using
-     * several rule syntaxes.
-     */
-    @Test
-    public void testBasic()
-        throws SAXException, IOException
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "employee" ).createObject().ofType( Employee.class );
-                // try all syntax permutations
-                forPattern( "employee" ).callMethod( "toString" ).withParamCount( 0 ).withParamTypes( (Class[]) null )
-                    .then()
-                    .callMethod( "toString" ).withParamCount( 0 ).withParamTypes( (String[]) null )
-                    .then()
-                    .callMethod( "toString" ).withParamCount( 0 ).withParamTypes( new Class[] {} )
-                    .then()
-                    .callMethod( "toString" ).withParamCount( 0 ).withParamTypes( new String[] {} )
-                    .then()
-                    .callMethod( "toString" );
-            }
-
-        }).newDigester();
-
-        // Parse our test input.
-        // An exception will be thrown if the method can't be found
-        final Employee root1 = digester.parse( getInputStream( "Test5.xml" ) );
-        assertNotNull( root1 );
-    }
-
-    /**
-     * Test method calls with the CallMethodRule reading from the element body, with no CallParamMethod rules added.
-     */
-    @Test
-    public void testCallMethodOnly()
-        throws Exception
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "employee" ).createObject().ofType( Employee.class );
-                forPattern( "employee/firstName" ).callMethod( "setFirstName" ).usingElementBodyAsArgument();
-                forPattern( "employee/lastName" ).callMethod( "setLastName" ).usingElementBodyAsArgument();
-            }
-
-        }).newDigester();
-
-        // Parse our test input
-        final 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.
-     */
-    @Test
-    public void testSettingProperties()
-        throws SAXException, IOException
-    {
-        Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "employee" ).createObject().ofType( Employee.class )
-                    .then()
-                    .callMethod( "setLastName" ).withParamTypes( "java.lang.String" );
-                forPattern( "employee/lastName" ).callParam().ofIndex( 0 );
-            }
-
-        }).newDigester();
-
-        // Parse our test input
-
-        // an exception will be thrown if the method can't be found
-        Employee employee = digester.parse( getInputStream( "Test5.xml" ) );
-        assertEquals( "Failed to call Employee.setLastName", "Last Name", employee.getLastName() );
-
-        digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "employee" ).createObject().ofType( Employee.class )
-                    .then()
-                    .callMethod( "setAge" ).withParamTypes( int.class );
-                forPattern( "employee/age" ).callParam();
-            }
-
-        }).newDigester();
-
-        // Parse our test input
-        // an exception will be thrown if the method can't be found
-        employee = digester.parse( getInputStream( "Test5.xml" ) );
-        assertEquals( "Failed to call Employee.setAge", 21, employee.getAge() );
-
-        digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "employee" ).createObject().ofType( Employee.class )
-                    .then()
-                    .callMethod( "setActive" ).withParamTypes( boolean.class );
-                forPattern( "employee/active" ).callParam();
-            }
-
-        }).newDigester();
-
-        // Parse our test input
-        // an exception will be thrown if the method can't be found
-        employee = digester.parse( getInputStream( "Test5.xml" ) );
-        assertEquals( "Failed to call Employee.setActive", true, employee.isActive() );
-
-        digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "employee" ).createObject().ofType( Employee.class )
-                    .then()
-                    .callMethod( "setSalary" ).withParamTypes( float.class );
-                forPattern( "employee/salary" ).callParam();
-            }
-
-        }).newDigester();
-
-        // Parse our test input
-        // an exception will be thrown if the method can't be found
-        employee = digester.parse( getInputStream( "Test5.xml" ) );
-        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.
-     */
-    @Test
-    public void testParamsFromStack()
-        throws SAXException, IOException
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "map" ).createObject().ofType( HashMap.class )
-                    .then()
-                    .callMethod( "put" ).withParamCount( 2 );
-                forPattern( "map/key" ).createObject().ofType( AlphaBean.class )
-                    .then()
-                    .setProperties()
-                    .then()
-                    .callParam().fromStack( true );
-                forPattern( "map/value" ).createObject().ofType( BetaBean.class )
-                    .then()
-                    .setProperties()
-                    .then()
-                    .callParam().ofIndex( 1 ).fromStack( true );
-            }
-
-        }).newDigester();
-
-        final StringBuilder xml =
-            new StringBuilder().append( "<?xml version='1.0'?>" ).append( "<map>" ).append( "  <key name='The key'/>" ).append( "  <value name='The value'/>" ).append( "</map>" );
-
-        final HashMap<AlphaBean, BetaBean> map = digester.parse( new StringReader( xml.toString() ) );
-
-        assertNotNull( map );
-        assertEquals( 1, map.size() );
-        assertEquals( "The key", map.keySet().iterator().next().getName() );
-        assertEquals( "The value", 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.
-     */
-    @Test
-    public void testOrderNestedPartA()
-        throws Exception
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                // 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.
-                forPattern( "toplevel/element" ).createObject().ofType( NamedBean.class )
-                    .then()
-                    .callMethod( "setName" ).withParamCount( 1 );
-                forPattern( "toplevel/element/element/element" ).callParam().ofIndex( 0 ).fromAttribute( "name" );
-                forPattern( "toplevel/element/element" ).createObject().ofType( NamedBean.class );
-            }
-
-        }).newDigester();
-
-       // Parse our test input
-       // an exception will be thrown if the method can't be found
-        final NamedBean root1 = digester.parse( getInputStream( "Test8.xml" ) );
-
-
-        // 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
-     * behavior 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 behavior.
-     */
-    @Test
-    public void testOrderNestedPartB()
-        throws Exception
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "*/element" ).callMethod( "append" ).withParamCount( 1 )
-                    .then()
-                    .callParam().ofIndex( 0 ).fromAttribute( "name" );
-            }
-
-        }).newDigester();
-
-        // Configure the digester as required
-        final StringBuilder word = new StringBuilder();
-        digester.push( word );
-
-        // 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 ( final 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() );
-    }
-
-    @Test
-    public void testPrimitiveReading()
-        throws Exception
-    {
-        final 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>" );
-
-        final 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" );
-        final 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" );
-        final 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" );
-
-        final ArrayList<PrimitiveBean> list = new ArrayList<PrimitiveBean>();
-        digester.push( list );
-        digester.parse( reader );
-
-        assertEquals( "Wrong number of beans in list", 6, list.size() );
-        PrimitiveBean bean = list.get( 0 );
-        assertTrue( "Bean 0 property not called", bean.getSetBooleanCalled() );
-        assertEquals( "Bean 0 property incorrect", true, bean.getBoolean() );
-        bean = list.get( 1 );
-        assertTrue( "Bean 1 property not called", bean.getSetBooleanCalled() );
-        assertEquals( "Bean 1 property incorrect", false, bean.getBoolean() );
-        bean = list.get( 2 );
-        // no attibute, no call is what's expected
-        assertTrue( "Bean 2 property called", !bean.getSetBooleanCalled() );
-        bean = list.get( 3 );
-        assertTrue( "Bean 3 property not called", bean.getSetBooleanCalled() );
-        assertEquals( "Bean 3 property incorrect", true, bean.getBoolean() );
-        bean = list.get( 4 );
-        assertTrue( "Bean 4 property not called", bean.getSetBooleanCalled() );
-        assertEquals( "Bean 4 property incorrect", false, bean.getBoolean() );
-        bean = list.get( 5 );
-        assertTrue( "Bean 5 property not called", bean.getSetBooleanCalled() );
-        assertEquals( "Bean 5 property incorrect", false, bean.getBoolean() );
-    }
-
-    @Test
-    public void testFromStack()
-        throws Exception
-    {
-
-        final StringReader reader =
-            new StringReader( "<?xml version='1.0' ?><root><one/><two/><three/><four/><five/></root>" );
-
-        final Digester digester = new Digester();
-
-        final 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" );
-        final 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," );
-
-        final ArrayList<NamedBean> list = new ArrayList<NamedBean>();
-        digester.push( list );
-        digester.parse( reader );
-
-        assertEquals( "Wrong number of beans in list", 5, list.size() );
-        NamedBean bean = list.get( 0 );
-        assertEquals( "Parameter not set from stack (1)", "Mary had a little lamb,", bean.getName() );
-        bean = list.get( 1 );
-        assertEquals( "Parameter not set from stack (2)", "It's fleece was white as snow.", bean.getName() );
-        bean = list.get( 2 );
-        assertEquals( "Parameter not set from stack (3)", "And everywhere that Mary went,", bean.getName() );
-        bean = list.get( 3 );
-        assertEquals( "Parameter not set from stack (4)", "That lamb was sure to go.", bean.getName() );
-        bean = list.get( 4 );
-        assertEquals( "Out of stack not set to null", null, bean.getName() );
-    }
-
-    @Test
-    public void testTwoCalls()
-        throws Exception
-    {
-
-        final 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>" );
-
-        final 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" );
-
-        final ArrayList<ParamBean> list = new ArrayList<ParamBean>();
-        digester.push( list );
-        digester.parse( reader );
-
-        assertEquals( "Wrong number of objects created", 3, list.size() );
-        ParamBean bean = 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 = 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 = list.get( 2 );
-        assertEquals( "Coolness wrong (3)", false, bean.isCool() );
-        assertEquals( "This wrong (3)", "float", bean.getThis() );
-        assertEquals( "That wrong (3)", "90", bean.getThat() );
-    }
-
-    @Test
-    public void testNestedBody()
-        throws Exception
-    {
-
-        final 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>" );
-
-        final 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 );
-
-        final ArrayList<NamedBean> list = new ArrayList<NamedBean>();
-        digester.push( list );
-        digester.parse( reader );
-
-        NamedBean bean = list.get( 0 );
-        assertEquals( "Wrong name (1)", "Simple", bean.getName() );
-        // these are added in deepest first order by the addRootRule
-        bean = list.get( 4 );
-        assertEquals( "Wrong name (2)", "Complex", bean.getName() );
-        bean = list.get( 3 );
-        assertEquals( "Wrong name (3)", "Deep", bean.getName() );
-        bean = list.get( 2 );
-        assertEquals( "Wrong name (4)", "Deeper", bean.getName() );
-        bean = list.get( 1 );
-        assertEquals( "Wrong name (5)", "Deepest", bean.getName() );
-    }
-
-    @Test
-    public void testProcessingHook()
-        throws Exception
-    {
-
-        class TestCallMethodRule
-            extends CallMethodRule
-        {
-            Object result;
-
-            TestCallMethodRule( final String methodName, final int paramCount )
-            {
-                super( methodName, paramCount );
-            }
-
-            @Override
-            protected void processMethodCallResult( final Object result )
-            {
-                this.result = result;
-            }
-        }
-
-        final StringReader reader =
-            new StringReader( "<?xml version='1.0' ?><root>"
-                + "<param class='float' coolness='false'>90</param></root>" );
-
-        final 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" );
-        final TestCallMethodRule rule = new TestCallMethodRule( "setThisAndThat", 2 );
-        digester.addRule( "root/param", rule );
-        digester.addCallParam( "root/param", 0, "class" );
-        digester.addCallParam( "root/param", 1, "coolness" );
-
-        final ArrayList<ParamBean> list = new ArrayList<ParamBean>();
-        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 */
-    @Test
-    public void testPathCallParam()
-        throws Exception
-    {
-        final String xml =
-            "<?xml version='1.0'?><main>" + "<alpha><beta>Ignore this</beta></alpha>"
-                + "<beta><epsilon><gamma>Ignore that</gamma></epsilon></beta>" + "</main>";
-
-        final SimpleTestBean bean = new SimpleTestBean();
-        bean.setAlphaBeta( "[UNSET]", "[UNSET]" );
-
-        final StringReader in = new StringReader( xml );
-        final 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.
-     */
-    @Test
-    public void testCallInvalidTarget()
-        throws Exception
-    {
-
-        final 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.
-        final 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 ( final SAXException e )
-        {
-            // ok, exception expected
-        }
-    }
-
-    /**
-     * Test invoking an object which is at top-1 on the stack, like SetNextRule does...
-     */
-    @Test
-    public void testCallNext()
-        throws Exception
-    {
-
-        final Digester digester = new Digester();
-        digester.addObjectCreate( "employee", HashMap.class );
-
-        digester.addObjectCreate( "employee/address", Address.class );
-        digester.addSetNestedProperties( "employee/address" );
-        final CallMethodRule r = new CallMethodRule( 1, "put", 2 );
-        digester.addRule( "employee/address", r );
-        digester.addCallParam( "employee/address/type", 0 );
-        digester.addCallParam( "employee/address", 1, 0 );
-
-        final HashMap<String, Address> map = digester.parse( getInputStream( "Test5.xml" ) );
-
-        assertNotNull( map );
-        final Set<String> keys = map.keySet();
-        assertEquals( 2, keys.size() );
-        final Address home = map.get( "home" );
-        assertNotNull( home );
-        assertEquals( "HmZip", home.getZipCode() );
-        final Address office = map.get( "office" );
-        assertNotNull( office );
-        assertEquals( "OfZip", office.getZipCode() );
-    }
-
-    /**
-     * Test invoking an object which is at the root of the stack, like SetRoot does...
-     */
-    @Test
-    public void testCallRoot()
-        throws Exception
-    {
-
-        final Digester digester = new Digester();
-        digester.addObjectCreate( "employee", HashMap.class );
-
-        digester.addObjectCreate( "employee/address", Address.class );
-        digester.addSetNestedProperties( "employee/address" );
-        final CallMethodRule r = new CallMethodRule( -1, "put", 2 );
-        digester.addRule( "employee/address", r );
-        digester.addCallParam( "employee/address/type", 0 );
-        digester.addCallParam( "employee/address", 1, 0 );
-
-        final HashMap<String, Address> map = digester.parse( getInputStream( "Test5.xml" ) );
-
-        assertNotNull( map );
-        final Set<String> keys = map.keySet();
-        assertEquals( 2, keys.size() );
-        final Address home = map.get( "home" );
-        assertNotNull( home );
-        assertEquals( "HmZip", home.getZipCode() );
-        final Address office = 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
-     * @throws IOException if an input/output error occurs
-     */
-    protected InputStream getInputStream( final String name )
-        throws IOException
-    {
-
-        return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) );
-
-    }
-
-}
+/* $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.digester3;
+
+import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Set;
+
+import org.apache.commons.digester3.binder.AbstractRulesModule;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+//import org.apache.commons.logging.impl.SimpleLog;
+
+/**
+ * <p>
+ * Tests for the {@code CallMethodRule} and associated {@code CallParamRule}.
+ */
+public class CallMethodRuleTestCase
+{
+
+    /**
+     * Test method calls with the CallMethodRule rule. It should be possible to call a method with no arguments using
+     * several rule syntaxes.
+     */
+    @Test
+    public void testBasic()
+        throws SAXException, IOException
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "employee" ).createObject().ofType( Employee.class );
+                // try all syntax permutations
+                forPattern( "employee" ).callMethod( "toString" ).withParamCount( 0 ).withParamTypes( (Class[]) null )
+                    .then()
+                    .callMethod( "toString" ).withParamCount( 0 ).withParamTypes( (String[]) null )
+                    .then()
+                    .callMethod( "toString" ).withParamCount( 0 ).withParamTypes( new Class[] {} )
+                    .then()
+                    .callMethod( "toString" ).withParamCount( 0 ).withParamTypes( new String[] {} )
+                    .then()
+                    .callMethod( "toString" );
+            }
+
+        }).newDigester();
+
+        // Parse our test input.
+        // An exception will be thrown if the method can't be found
+        final Employee root1 = digester.parse( getInputStream( "Test5.xml" ) );
+        assertNotNull( root1 );
+    }
+
+    /**
+     * Test method calls with the CallMethodRule reading from the element body, with no CallParamMethod rules added.
+     */
+    @Test
+    public void testCallMethodOnly()
+        throws Exception
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "employee" ).createObject().ofType( Employee.class );
+                forPattern( "employee/firstName" ).callMethod( "setFirstName" ).usingElementBodyAsArgument();
+                forPattern( "employee/lastName" ).callMethod( "setLastName" ).usingElementBodyAsArgument();
+            }
+
+        }).newDigester();
+
+        // Parse our test input
+        final 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.
+     */
+    @Test
+    public void testSettingProperties()
+        throws SAXException, IOException
+    {
+        Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "employee" ).createObject().ofType( Employee.class )
+                    .then()
+                    .callMethod( "setLastName" ).withParamTypes( "java.lang.String" );
+                forPattern( "employee/lastName" ).callParam().ofIndex( 0 );
+            }
+
+        }).newDigester();
+
+        // Parse our test input
+
+        // an exception will be thrown if the method can't be found
+        Employee employee = digester.parse( getInputStream( "Test5.xml" ) );
+        assertEquals( "Failed to call Employee.setLastName", "Last Name", employee.getLastName() );
+
+        digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "employee" ).createObject().ofType( Employee.class )
+                    .then()
+                    .callMethod( "setAge" ).withParamTypes( int.class );
+                forPattern( "employee/age" ).callParam();
+            }
+
+        }).newDigester();
+
+        // Parse our test input
+        // an exception will be thrown if the method can't be found
+        employee = digester.parse( getInputStream( "Test5.xml" ) );
+        assertEquals( "Failed to call Employee.setAge", 21, employee.getAge() );
+
+        digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "employee" ).createObject().ofType( Employee.class )
+                    .then()
+                    .callMethod( "setActive" ).withParamTypes( boolean.class );
+                forPattern( "employee/active" ).callParam();
+            }
+
+        }).newDigester();
+
+        // Parse our test input
+        // an exception will be thrown if the method can't be found
+        employee = digester.parse( getInputStream( "Test5.xml" ) );
+        assertEquals( "Failed to call Employee.setActive", true, employee.isActive() );
+
+        digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "employee" ).createObject().ofType( Employee.class )
+                    .then()
+                    .callMethod( "setSalary" ).withParamTypes( float.class );
+                forPattern( "employee/salary" ).callParam();
+            }
+
+        }).newDigester();
+
+        // Parse our test input
+        // an exception will be thrown if the method can't be found
+        employee = digester.parse( getInputStream( "Test5.xml" ) );
+        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.
+     */
+    @Test
+    public void testParamsFromStack()
+        throws SAXException, IOException
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "map" ).createObject().ofType( HashMap.class )
+                    .then()
+                    .callMethod( "put" ).withParamCount( 2 );
+                forPattern( "map/key" ).createObject().ofType( AlphaBean.class )
+                    .then()
+                    .setProperties()
+                    .then()
+                    .callParam().fromStack( true );
+                forPattern( "map/value" ).createObject().ofType( BetaBean.class )
+                    .then()
+                    .setProperties()
+                    .then()
+                    .callParam().ofIndex( 1 ).fromStack( true );
+            }
+
+        }).newDigester();
+
+        final StringBuilder xml =
+            new StringBuilder().append( "<?xml version='1.0'?>" ).append( "<map>" ).append( "  <key name='The key'/>" ).append( "  <value name='The value'/>" ).append( "</map>" );
+
+        final HashMap<AlphaBean, BetaBean> map = digester.parse( new StringReader( xml.toString() ) );
+
+        assertNotNull( map );
+        assertEquals( 1, map.size() );
+        assertEquals( "The key", map.keySet().iterator().next().getName() );
+        assertEquals( "The value", 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.
+     */
+    @Test
+    public void testOrderNestedPartA()
+        throws Exception
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                // 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.
+                forPattern( "toplevel/element" ).createObject().ofType( NamedBean.class )
+                    .then()
+                    .callMethod( "setName" ).withParamCount( 1 );
+                forPattern( "toplevel/element/element/element" ).callParam().ofIndex( 0 ).fromAttribute( "name" );
+                forPattern( "toplevel/element/element" ).createObject().ofType( NamedBean.class );
+            }
+
+        }).newDigester();
+
+       // Parse our test input
+       // an exception will be thrown if the method can't be found
+        final NamedBean root1 = digester.parse( getInputStream( "Test8.xml" ) );
+
+
+        // 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
+     * behavior 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 behavior.
+     */
+    @Test
+    public void testOrderNestedPartB()
+        throws Exception
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "*/element" ).callMethod( "append" ).withParamCount( 1 )
+                    .then()
+                    .callParam().ofIndex( 0 ).fromAttribute( "name" );
+            }
+
+        }).newDigester();
+
+        // Configure the digester as required
+        final StringBuilder word = new StringBuilder();
+        digester.push( word );
+
+        // 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 ( final 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() );
+    }
+
+    @Test
+    public void testPrimitiveReading()
+        throws Exception
+    {
+        final 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>" );
+
+        final 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" );
+        final 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" );
+        final 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" );
+
+        final ArrayList<PrimitiveBean> list = new ArrayList<PrimitiveBean>();
+        digester.push( list );
+        digester.parse( reader );
+
+        assertEquals( "Wrong number of beans in list", 6, list.size() );
+        PrimitiveBean bean = list.get( 0 );
+        assertTrue( "Bean 0 property not called", bean.getSetBooleanCalled() );
+        assertEquals( "Bean 0 property incorrect", true, bean.getBoolean() );
+        bean = list.get( 1 );
+        assertTrue( "Bean 1 property not called", bean.getSetBooleanCalled() );
+        assertEquals( "Bean 1 property incorrect", false, bean.getBoolean() );
+        bean = list.get( 2 );
+        // no attibute, no call is what's expected
+        assertTrue( "Bean 2 property called", !bean.getSetBooleanCalled() );
+        bean = list.get( 3 );
+        assertTrue( "Bean 3 property not called", bean.getSetBooleanCalled() );
+        assertEquals( "Bean 3 property incorrect", true, bean.getBoolean() );
+        bean = list.get( 4 );
+        assertTrue( "Bean 4 property not called", bean.getSetBooleanCalled() );
+        assertEquals( "Bean 4 property incorrect", false, bean.getBoolean() );
+        bean = list.get( 5 );
+        assertTrue( "Bean 5 property not called", bean.getSetBooleanCalled() );
+        assertEquals( "Bean 5 property incorrect", false, bean.getBoolean() );
+    }
+
+    @Test
+    public void testFromStack()
+        throws Exception
+    {
+
+        final StringReader reader =
+            new StringReader( "<?xml version='1.0' ?><root><one/><two/><three/><four/><five/></root>" );
+
+        final Digester digester = new Digester();
+
+        final 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" );
+        final 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," );
+
+        final ArrayList<NamedBean> list = new ArrayList<NamedBean>();
+        digester.push( list );
+        digester.parse( reader );
+
+        assertEquals( "Wrong number of beans in list", 5, list.size() );
+        NamedBean bean = list.get( 0 );
+        assertEquals( "Parameter not set from stack (1)", "Mary had a little lamb,", bean.getName() );
+        bean = list.get( 1 );
+        assertEquals( "Parameter not set from stack (2)", "It's fleece was white as snow.", bean.getName() );
+        bean = list.get( 2 );
+        assertEquals( "Parameter not set from stack (3)", "And everywhere that Mary went,", bean.getName() );
+        bean = list.get( 3 );
+        assertEquals( "Parameter not set from stack (4)", "That lamb was sure to go.", bean.getName() );
+        bean = list.get( 4 );
+        assertEquals( "Out of stack not set to null", null, bean.getName() );
+    }
+
+    @Test
+    public void testTwoCalls()
+        throws Exception
+    {
+
+        final 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>" );
+
+        final 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" );
+
+        final ArrayList<ParamBean> list = new ArrayList<ParamBean>();
+        digester.push( list );
+        digester.parse( reader );
+
+        assertEquals( "Wrong number of objects created", 3, list.size() );
+        ParamBean bean = 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 = 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 = list.get( 2 );
+        assertEquals( "Coolness wrong (3)", false, bean.isCool() );
+        assertEquals( "This wrong (3)", "float", bean.getThis() );
+        assertEquals( "That wrong (3)", "90", bean.getThat() );
+    }
+
+    @Test
+    public void testNestedBody()
+        throws Exception
+    {
+
+        final 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>" );
+
+        final 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 );
+
+        final ArrayList<NamedBean> list = new ArrayList<NamedBean>();
+        digester.push( list );
+        digester.parse( reader );
+
+        NamedBean bean = list.get( 0 );
+        assertEquals( "Wrong name (1)", "Simple", bean.getName() );
+        // these are added in deepest first order by the addRootRule
+        bean = list.get( 4 );
+        assertEquals( "Wrong name (2)", "Complex", bean.getName() );
+        bean = list.get( 3 );
+        assertEquals( "Wrong name (3)", "Deep", bean.getName() );
+        bean = list.get( 2 );
+        assertEquals( "Wrong name (4)", "Deeper", bean.getName() );
+        bean = list.get( 1 );
+        assertEquals( "Wrong name (5)", "Deepest", bean.getName() );
+    }
+
+    @Test
+    public void testProcessingHook()
+        throws Exception
+    {
+
+        class TestCallMethodRule
+            extends CallMethodRule
+        {
+            Object result;
+
+            TestCallMethodRule( final String methodName, final int paramCount )
+            {
+                super( methodName, paramCount );
+            }
+
+            @Override
+            protected void processMethodCallResult( final Object result )
+            {
+                this.result = result;
+            }
+        }
+
+        final StringReader reader =
+            new StringReader( "<?xml version='1.0' ?><root>"
+                + "<param class='float' coolness='false'>90</param></root>" );
+
+        final 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" );
+        final TestCallMethodRule rule = new TestCallMethodRule( "setThisAndThat", 2 );
+        digester.addRule( "root/param", rule );
+        digester.addCallParam( "root/param", 0, "class" );
+        digester.addCallParam( "root/param", 1, "coolness" );
+
+        final ArrayList<ParamBean> list = new ArrayList<ParamBean>();
+        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 */
+    @Test
+    public void testPathCallParam()
+        throws Exception
+    {
+        final String xml =
+            "<?xml version='1.0'?><main>" + "<alpha><beta>Ignore this</beta></alpha>"
+                + "<beta><epsilon><gamma>Ignore that</gamma></epsilon></beta>" + "</main>";
+
+        final SimpleTestBean bean = new SimpleTestBean();
+        bean.setAlphaBeta( "[UNSET]", "[UNSET]" );
+
+        final StringReader in = new StringReader( xml );
+        final 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.
+     */
+    @Test
+    public void testCallInvalidTarget()
+        throws Exception
+    {
+
+        final 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.
+        final 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 ( final SAXException e )
+        {
+            // ok, exception expected
+        }
+    }
+
+    /**
+     * Test invoking an object which is at top-1 on the stack, like SetNextRule does...
+     */
+    @Test
+    public void testCallNext()
+        throws Exception
+    {
+
+        final Digester digester = new Digester();
+        digester.addObjectCreate( "employee", HashMap.class );
+
+        digester.addObjectCreate( "employee/address", Address.class );
+        digester.addSetNestedProperties( "employee/address" );
+        final CallMethodRule r = new CallMethodRule( 1, "put", 2 );
+        digester.addRule( "employee/address", r );
+        digester.addCallParam( "employee/address/type", 0 );
+        digester.addCallParam( "employee/address", 1, 0 );
+
+        final HashMap<String, Address> map = digester.parse( getInputStream( "Test5.xml" ) );
+
+        assertNotNull( map );
+        final Set<String> keys = map.keySet();
+        assertEquals( 2, keys.size() );
+        final Address home = map.get( "home" );
+        assertNotNull( home );
+        assertEquals( "HmZip", home.getZipCode() );
+        final Address office = map.get( "office" );
+        assertNotNull( office );
+        assertEquals( "OfZip", office.getZipCode() );
+    }
+
+    /**
+     * Test invoking an object which is at the root of the stack, like SetRoot does...
+     */
+    @Test
+    public void testCallRoot()
+        throws Exception
+    {
+
+        final Digester digester = new Digester();
+        digester.addObjectCreate( "employee", HashMap.class );
+
+        digester.addObjectCreate( "employee/address", Address.class );
+        digester.addSetNestedProperties( "employee/address" );
+        final CallMethodRule r = new CallMethodRule( -1, "put", 2 );
+        digester.addRule( "employee/address", r );
+        digester.addCallParam( "employee/address/type", 0 );
+        digester.addCallParam( "employee/address", 1, 0 );
+
+        final HashMap<String, Address> map = digester.parse( getInputStream( "Test5.xml" ) );
+
+        assertNotNull( map );
+        final Set<String> keys = map.keySet();
+        assertEquals( 2, keys.size() );
+        final Address home = map.get( "home" );
+        assertNotNull( home );
+        assertEquals( "HmZip", home.getZipCode() );
+        final Address office = 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
+     * @throws IOException if an input/output error occurs
+     */
+    protected InputStream getInputStream( final String name )
+        throws IOException
+    {
+
+        return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) );
+
+    }
+
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/DTDValidationTestCase.java b/core/src/test/java/org/apache/commons/digester3/DTDValidationTestCase.java
index be3d3e74..baeba3c3 100644
--- a/core/src/test/java/org/apache/commons/digester3/DTDValidationTestCase.java
+++ b/core/src/test/java/org/apache/commons/digester3/DTDValidationTestCase.java
@@ -1,138 +1,136 @@
-/*
- * 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.digester3;
-
-import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;
-
-import java.io.File;
-
-import org.apache.commons.digester3.binder.AbstractRulesModule;
-import org.junit.Test;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-/**
- * Tests for entity resolution and dtd validation
- *
- * @author <a href='http://commons.apache.org/'>Apache Commons Team</a>
- */
-public class DTDValidationTestCase
-{
-
-    @Test( expected = SAXParseException.class )
-    public void testDigesterDTDError()
-        throws Exception
-    {
-        newLoader( new AbstractRulesModule() {
-
-            @Override
-            protected void configure()
-            {
-                // do nothing
-            }
-
-        } )
-        .setValidating( true )
-        .setErrorHandler( new ErrorHandler()
-        {
-
-            @Override
-            public void warning( final SAXParseException e )
-                throws SAXException
-            {
-                throw e;
-            }
-
-            @Override
-            public void fatalError( final SAXParseException e )
-                throws SAXException
-            {
-                throw e;
-            }
-
-            @Override
-            public void error( final SAXParseException e )
-                throws SAXException
-            {
-                throw e;
-            }
-
-        } )
-        .newDigester()
-        .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd-error.xml" ) );
-    }
-
-    @Test
-    public void testDigesterNoDTDValidation()
-        throws Exception
-    {
-        newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                // do nothing
-            }
-
-        } )
-        .setValidating( false )
-        .newDigester()
-        .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd-error.xml" ) );
-    }
-
-    @Test
-    public void testDigesterValidation()
-        throws Exception
-    {
-        newLoader( new AbstractRulesModule()
-        {
-            @Override
-            protected void configure()
-            {
-                // do nothing
-            }
-        } )
-        .setValidating( true )
-        .newDigester()
-        .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd.xml" ) );
-    }
-
-    @Test
-    public void testDigesterLoaderFeatureDisabled()
-        throws Exception
-    {
-       newLoader( new AbstractRulesModule()
-        {
-
-           @Override
-            protected void configure()
-            {
-                // do nothing
-            }
-
-        } )
-        .setFeature("http://xml.org/sax/features/validation", false)
-        .setFeature("http://xml.org/sax/features/external-parameter-entities", false)
-        .setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false)
-        .setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
-        .newDigester()
-        .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd-error.xml" ) );
-    }
-
-}
+/*
+ * 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.digester3;
+
+import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;
+
+import java.io.File;
+
+import org.apache.commons.digester3.binder.AbstractRulesModule;
+import org.junit.Test;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Tests for entity resolution and dtd validation
+ */
+public class DTDValidationTestCase
+{
+
+    @Test( expected = SAXParseException.class )
+    public void testDigesterDTDError()
+        throws Exception
+    {
+        newLoader( new AbstractRulesModule() {
+
+            @Override
+            protected void configure()
+            {
+                // do nothing
+            }
+
+        } )
+        .setValidating( true )
+        .setErrorHandler( new ErrorHandler()
+        {
+
+            @Override
+            public void warning( final SAXParseException e )
+                throws SAXException
+            {
+                throw e;
+            }
+
+            @Override
+            public void fatalError( final SAXParseException e )
+                throws SAXException
+            {
+                throw e;
+            }
+
+            @Override
+            public void error( final SAXParseException e )
+                throws SAXException
+            {
+                throw e;
+            }
+
+        } )
+        .newDigester()
+        .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd-error.xml" ) );
+    }
+
+    @Test
+    public void testDigesterNoDTDValidation()
+        throws Exception
+    {
+        newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                // do nothing
+            }
+
+        } )
+        .setValidating( false )
+        .newDigester()
+        .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd-error.xml" ) );
+    }
+
+    @Test
+    public void testDigesterValidation()
+        throws Exception
+    {
+        newLoader( new AbstractRulesModule()
+        {
+            @Override
+            protected void configure()
+            {
+                // do nothing
+            }
+        } )
+        .setValidating( true )
+        .newDigester()
+        .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd.xml" ) );
+    }
+
+    @Test
+    public void testDigesterLoaderFeatureDisabled()
+        throws Exception
+    {
+       newLoader( new AbstractRulesModule()
+        {
+
+           @Override
+            protected void configure()
+            {
+                // do nothing
+            }
+
+        } )
+        .setFeature("http://xml.org/sax/features/validation", false)
+        .setFeature("http://xml.org/sax/features/external-parameter-entities", false)
+        .setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false)
+        .setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
+        .newDigester()
+        .parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd-error.xml" ) );
+    }
+
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/DigesterTestCase.java b/core/src/test/java/org/apache/commons/digester3/DigesterTestCase.java
index e865d796..aef786b2 100644
--- a/core/src/test/java/org/apache/commons/digester3/DigesterTestCase.java
+++ b/core/src/test/java/org/apache/commons/digester3/DigesterTestCase.java
@@ -1,712 +1,710 @@
-/* $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.digester3;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.File;
-import java.io.InputStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.math.BigDecimal;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.EmptyStackException;
-import java.util.Map;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.xml.sax.Attributes;
-import org.xml.sax.InputSource;
-import org.xml.sax.helpers.AttributesImpl;
-
-/**
- * <p>
- * Test Case for the Digester class. These tests exercise the individual methods of a Digester, but do not attempt to
- * process complete documents.
- * </p>
- *
- * @author Craig R. McClanahan
- */
-public class DigesterTestCase
-{
-
-    // ----------------------------------------------------- Instance Variables
-
-    /**
-     * The digester instance we will be processing.
-     */
-    protected Digester digester = null;
-
-    /**
-     * The set of public identifiers, and corresponding resource names, for the versions of the DTDs that we know about.
-     * There <strong>MUST</strong> be an even number of Strings in this array.
-     */
-    protected static final String registrations[] = { "-//Netscape Communications//DTD RSS 0.9//EN",
-        "/org/apache/commons/digester3/rss/rss-0.9.dtd", "-//Netscape Communications//DTD RSS 0.91//EN",
-        "/org/apache/commons/digester3/rss/rss-0.91.dtd", };
-
-    // -------------------------------------------------- Overall Test Methods
-
-    /**
-     * Set up instance variables required by this test case.
-     */
-    @Before
-    public void setUp()
-    {
-
-        digester = new Digester();
-        digester.setRules( new RulesBase() );
-
-    }
-
-    /**
-     * Tear down instance variables required by this test case.
-     */
-    @After
-    public void tearDown()
-    {
-
-        digester = null;
-
-    }
-
-    // ------------------------------------------------ Individual Test Methods
-
-    /**
-     * Test {@code null} parsing. (should lead to {@code IllegalArgumentException}s)
-     */
-    @Test
-    public void testNullFileParse()
-        throws Exception
-    {
-
-        try
-        {
-            digester.parse( (File) null );
-            fail( "Expected IllegalArgumentException with null argument" );
-        }
-        catch ( final IllegalArgumentException e )
-        {
-            // expected
-        }
-
-    }
-
-    @Test
-    public void testNullInputSourceParse()
-        throws Exception
-    {
-
-        try
-        {
-            digester.parse( (InputSource) null );
-            fail( "Expected IllegalArgumentException with null argument" );
-        }
-        catch ( final IllegalArgumentException e )
-        {
-            // expected
-        }
-
-    }
-
-    @Test
-    public void testNullInputStreamParse()
-        throws Exception
-    {
-
-        try
-        {
-            digester.parse( (InputStream) null );
-            fail( "Expected IllegalArgumentException with null argument" );
-        }
-        catch ( final IllegalArgumentException e )
-        {
-            // expected
-        }
-
-    }
-
-    @Test
-    public void testNullReaderParse()
-        throws Exception
-    {
-
-        try
-        {
-            digester.parse( (Reader) null );
-            fail( "Expected IllegalArgumentException with null argument" );
-        }
-        catch ( final IllegalArgumentException e )
-        {
-            // expected
-        }
-
-    }
-
-    @Test
-    public void testNullStringParse()
-        throws Exception
-    {
-
-        try
-        {
-            digester.parse( (String) null );
-            fail( "Expected IllegalArgumentException with null argument" );
-        }
-        catch ( final IllegalArgumentException e )
-        {
-            // expected
-        }
-
-    }
-
-    @Test
-    public void testNullURLParse()
-        throws Exception
-    {
-
-        try
-        {
-            digester.parse( (URL) null );
-            fail( "Expected IllegalArgumentException with null argument" );
-        }
-        catch ( final IllegalArgumentException e )
-        {
-            // expected
-        }
-
-    }
-
-    /**
-     * Test the basic property getters and setters.
-     */
-    @Test
-    public void testProperties()
-    {
-
-        assertNull( "Initial error handler is null", digester.getErrorHandler() );
-        digester.setErrorHandler( digester );
-        assertTrue( "Set error handler is digester", digester.getErrorHandler() == digester );
-        digester.setErrorHandler( null );
-        assertNull( "Reset error handler is null", digester.getErrorHandler() );
-
-        assertTrue( "Initial namespace aware is false", !digester.getNamespaceAware() );
-        digester.setNamespaceAware( true );
-        assertTrue( "Set namespace aware is true", digester.getNamespaceAware() );
-        digester.setNamespaceAware( false );
-        assertTrue( "Reset namespace aware is false", !digester.getNamespaceAware() );
-
-        assertTrue( "Initial validating is false", !digester.getValidating() );
-        digester.setValidating( true );
-        assertTrue( "Set validating is true", digester.getValidating() );
-        digester.setValidating( false );
-        assertTrue( "Reset validating is false", !digester.getValidating() );
-
-    }
-
-    /**
-     * Test registration of URLs for specified public identifiers.
-     */
-    @Test
-    public void testRegistrations()
-    {
-
-        Map<String, URL> map = digester.getRegistrations();
-        assertEquals( "Initially zero registrations", 0, map.size() );
-        int n = 0;
-        for ( int i = 0; i < registrations.length; i += 2 )
-        {
-            final URL url = this.getClass().getResource( registrations[i + 1] );
-            if ( url != null )
-            {
-                digester.register( registrations[i], url );
-                n++;
-            }
-        }
-        map = digester.getRegistrations();
-        assertEquals( "Registered two URLs", n, map.size() );
-
-        final int count[] = new int[n];
-        for ( int i = 0; i < n; i++ ) {
-            count[i] = 0;
-        }
-        for ( final String key : map.keySet() )
-        {
-            for ( int i = 0; i < n; i++ )
-            {
-                if ( key.equals( registrations[i * 2] ) )
-                {
-                    count[i]++;
-                    break;
-                }
-            }
-        }
-        for ( int i = 0; i < n; i++ ) {
-            assertEquals( "Count for key " + registrations[i * 2], 1, count[i] );
-        }
-
-    }
-
-    /**
-     * Basic test for rule creation and matching.
-     */
-    @Test
-    public void testRules()
-    {
-
-        assertEquals( "Initial rules list is empty", 0, digester.getRules().match( null, "a", null, null ).size() );
-        digester.addSetProperties( "a" );
-        assertEquals( "Add a matching rule", 1, digester.getRules().match( null, "a", null, null ).size() );
-        digester.addSetProperties( "b" );
-        assertEquals( "Add a non-matching rule", 1, digester.getRules().match( null, "a", null, null ).size() );
-        digester.addSetProperties( "a/b" );
-        assertEquals( "Add a non-matching nested rule", 1, digester.getRules().match( null, "a", null, null ).size() );
-        digester.addSetProperties( "a/b" );
-        assertEquals( "Add a second matching rule", 2, digester.getRules().match( null, "a/b", null, null ).size() );
-
-    }
-
-    /**
-     * <p>
-     * Test matching rules in {@link RulesBase}.
-     * </p>
-     * <p>
-     * Tests:
-     * </p>
-     * <ul>
-     * <li>exact match</li>
-     * <li>tail match</li>
-     * <li>longest pattern rule</li>
-     * </ul>
-     */
-    @Test
-    public void testRulesBase()
-    {
-
-        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
-
-        // We're going to set up
-        digester.addRule( "a/b/c/d", new TestRule( "a/b/c/d" ) );
-        digester.addRule( "*/d", new TestRule( "*/d" ) );
-        digester.addRule( "*/c/d", new TestRule( "*/c/d" ) );
-
-        // Test exact match
-        assertEquals( "Exact match takes precedence 1", 1, digester.getRules().match( null, "a/b/c/d", null, null ).size() );
-        assertEquals( "Exact match takes precedence 2", "a/b/c/d",
-                      ( (TestRule) digester.getRules().match( null, "a/b/c/d", null, null ).iterator().next() ).getIdentifier() );
-
-        // Test wildcard tail matching
-        assertEquals( "Wildcard tail matching rule 1", 1, digester.getRules().match( null, "a/b/d", null, null ).size() );
-        assertEquals( "Wildcard tail matching rule 2", "*/d",
-                      ( (TestRule) digester.getRules().match( null, "a/b/d", null, null ).iterator().next() ).getIdentifier() );
-
-        // Test the longest matching pattern rule
-        assertEquals( "Longest tail rule 1", 1, digester.getRules().match( null, "x/c/d", null, null ).size() );
-        assertEquals( "Longest tail rule 2", "*/c/d",
-                      ( (TestRule) digester.getRules().match( null, "x/c/d", null, null ).iterator().next() ).getIdentifier() );
-
-    }
-
-    /**
-     * Test the basic stack mechanisms.
-     */
-    @Test
-    public void testStackMethods()
-    {
-
-        Object value;
-
-        // New stack must be empty
-        assertEquals( "New stack is empty", 0, digester.getCount() );
-        value = digester.peek();
-        assertNull( "New stack peek() returns null", value );
-        value = digester.pop();
-        assertNull( "New stack pop() returns null", value );
-
-        // Test pushing and popping activities
-        digester.push( "First Item" );
-        assertEquals( "Pushed one item size", 1, digester.getCount() );
-        value = digester.peek();
-        assertNotNull( "Peeked first item is not null", value );
-        assertEquals( "Peeked first item value", "First Item", value );
-
-        digester.push( "Second Item" );
-        assertEquals( "Pushed two items size", 2, digester.getCount() );
-        value = digester.peek();
-        assertNotNull( "Peeked second item is not null", value );
-        assertEquals( "Peeked second item value", "Second Item", value );
-
-        value = digester.pop();
-        assertEquals( "Popped stack size", 1, digester.getCount() );
-        assertNotNull( "Popped second item is not null", value );
-        assertEquals( "Popped second item value", "Second Item", value );
-        value = digester.peek();
-        assertNotNull( "Remaining item is not null", value );
-        assertEquals( "Remaining item value", "First Item", value );
-        assertEquals( "Remaining stack size", 1, digester.getCount() );
-
-        // Cleared stack is empty
-        digester.push( "Dummy Item" );
-        digester.clear();
-        assertEquals( "Cleared stack is empty", 0, digester.getCount() );
-        value = digester.peek();
-        assertNull( "Cleared stack peek() returns null", value );
-        value = digester.pop();
-        assertNull( "Cleared stack pop() returns null", value );
-
-    }
-
-    @Test
-    public void testOnceAndOnceOnly()
-        throws Exception
-    {
-
-        class TestConfigureDigester
-            extends Digester
-        {
-            public int called = 0;
-
-            public TestConfigureDigester()
-            {
-            }
-
-            @Override
-            protected void initialize()
-            {
-                called++;
-            }
-        }
-
-        final TestConfigureDigester digester = new TestConfigureDigester();
-
-        final String xml = "<?xml version='1.0'?><document/>";
-        digester.parse( new StringReader( xml ) );
-
-        assertEquals( "Initialize should be called once and only once", 1, digester.called );
-    }
-
-    @Test
-    public void testBasicSubstitution()
-        throws Exception
-    {
-        class TestSubRule
-            extends Rule
-        {
-            public String body;
-
-            public Attributes attributes;
-
-            @Override
-            public void begin( final String namespace, final String name, final Attributes attributes )
-            {
-                this.attributes = new AttributesImpl( attributes );
-            }
-
-            @Override
-            public void body( final String namespace, final String name, final String text )
-            {
-                this.body = text;
-            }
-        }
-
-        final TestSubRule tsr = new TestSubRule();
-        final Digester digester = new Digester();
-        digester.addRule( "alpha/beta", tsr );
-
-        // it's not easy to transform dirty harry into the mighty circus - but let's give it a try
-        final String xml =
-            "<?xml version='1.0'?><alpha><beta forname='Dirty' surname='Harry'>Do you feel luck punk?</beta></alpha>";
-        InputSource in = new InputSource( new StringReader( xml ) );
-
-        digester.parse( in );
-
-        assertEquals( "Unsubstituted body text", "Do you feel luck punk?", tsr.body );
-        assertEquals( "Unsubstituted number of attributes", 2, tsr.attributes.getLength() );
-        assertEquals( "Unsubstituted forname attribute value", "Dirty", tsr.attributes.getValue( "forname" ) );
-        assertEquals( "Unsubstituted surname attribute value", "Harry", tsr.attributes.getValue( "surname" ) );
-
-        digester.setSubstitutor( new Substitutor()
-        {
-            @Override
-            public Attributes substitute( final Attributes attributes )
-            {
-                final AttributesImpl results = new AttributesImpl();
-                results.addAttribute( "", "python", "python", "CDATA", "Cleese" );
-                return results;
-            }
-
-            @Override
-            public String substitute( final String bodyText )
-            {
-                return "And now for something completely different...";
-            }
-        } );
-
-        // now transform into the full monty
-        in = new InputSource( new StringReader( xml ) );
-        digester.parse( in );
-
-        assertEquals( "Substituted body text", "And now for something completely different...", tsr.body );
-        assertEquals( "Substituted number of attributes", 1, tsr.attributes.getLength() );
-        assertEquals( "Substituted python attribute value", "Cleese", tsr.attributes.getValue( "", "python" ) );
-    }
-
-    /** Tests the push-peek-pop cycle for a named stack */
-    @Test
-    public void testNamedStackPushPeekPop()
-        throws Exception
-    {
-        final BigDecimal archimedesAveragePi = new BigDecimal( "3.1418" );
-        final String testStackName = "org.apache.commons.digester3.tests.testNamedStackPushPeekPop";
-        final Digester digester = new Digester();
-        assertTrue( "Stack starts empty:", digester.isEmpty( testStackName ) );
-        digester.push( testStackName, archimedesAveragePi );
-        assertEquals( "Peeked value:", archimedesAveragePi, digester.peek( testStackName ) );
-        assertEquals( "Popped value:", archimedesAveragePi, digester.pop( testStackName ) );
-        assertTrue( "Stack ends empty:", digester.isEmpty( testStackName ) );
-
-        digester.push( testStackName, "1" );
-        digester.push( testStackName, "2" );
-        digester.push( testStackName, "3" );
-
-        assertEquals( "Peek#1", "1", digester.peek( testStackName, 2 ) );
-        assertEquals( "Peek#2", "2", digester.peek( testStackName, 1 ) );
-        assertEquals( "Peek#3", "3", digester.peek( testStackName, 0 ) );
-        assertEquals( "Peek#3a", "3", digester.peek( testStackName ) );
-
-        try
-        {
-            // peek beyond stack
-            digester.peek( testStackName, 3 );
-            fail( "Peek#4 failed to throw an exception." );
-        }
-        catch ( final EmptyStackException ex )
-        {
-            // ok, expected
-        }
-
-        try
-        {
-            // peek a nonexistent named stack
-            digester.peek( "no.such.stack", 0 );
-            fail( "Peeking a non-existent stack failed to throw an exception." );
-        }
-        catch ( final EmptyStackException ex )
-        {
-            // ok, expected
-        }
-    }
-
-    /** Tests that values are stored independently */
-    @Test
-    public void testNamedIndependence()
-    {
-        final String testStackOneName = "org.apache.commons.digester3.tests.testNamedIndependenceOne";
-        final String testStackTwoName = "org.apache.commons.digester3.tests.testNamedIndependenceTwo";
-        final Digester digester = new Digester();
-        digester.push( testStackOneName, "Tweedledum" );
-        digester.push( testStackTwoName, "Tweedledee" );
-        assertEquals( "Popped value one:", "Tweedledum", digester.pop( testStackOneName ) );
-        assertEquals( "Popped value two:", "Tweedledee", digester.pop( testStackTwoName ) );
-    }
-
-    /** Tests popping named stack not yet pushed */
-    @Test
-    public void testPopNamedStackNotPushed()
-    {
-        final String testStackName = "org.apache.commons.digester3.tests.testPopNamedStackNotPushed";
-        final Digester digester = new Digester();
-        try
-        {
-
-            digester.pop( testStackName );
-            fail( "Expected an EmptyStackException" );
-
-        }
-        catch ( final EmptyStackException e )
-        {
-            // expected
-        }
-
-        try
-        {
-
-            digester.peek( testStackName );
-            fail( "Expected an EmptyStackException" );
-
-        }
-        catch ( final EmptyStackException e )
-        {
-            // expected
-        }
-    }
-
-    /** Tests for isEmpty */
-    @Test
-    public void testNamedStackIsEmpty()
-    {
-        final String testStackName = "org.apache.commons.digester3.tests.testNamedStackIsEmpty";
-        final Digester digester = new Digester();
-        assertTrue( "A named stack that has no object pushed onto it yet should be empty",
-                    digester.isEmpty( testStackName ) );
-
-        digester.push( testStackName, "Some test value" );
-        assertFalse( "A named stack that has an object pushed onto it should be not empty",
-                     digester.isEmpty( testStackName ) );
-
-        digester.peek( testStackName );
-        assertFalse( "Peek should not effect whether the stack is empty", digester.isEmpty( testStackName ) );
-
-        digester.pop( testStackName );
-        assertTrue( "A named stack that has it's last object popped is empty", digester.isEmpty( testStackName ) );
-    }
-
-    /**
-     * Test the Digester.getRoot method.
-     */
-    @Test
-    public void testGetRoot()
-        throws Exception
-    {
-        final Digester digester = new Digester();
-        digester.addRule( "root", new ObjectCreateRule( TestBean.class ) );
-
-        final String xml = "<root/>";
-        final InputSource in = new InputSource( new StringReader( xml ) );
-
-        digester.parse( in );
-
-        final Object root = digester.getRoot();
-        assertNotNull( "root object not retrieved", root );
-        assertTrue( "root object not a TestRule instance", ( root instanceof TestBean ) );
-    }
-
-    /** Utility class for method testStackAction */
-    private static class TrackingStackAction
-        implements StackAction
-    {
-        public ArrayList<String> events = new ArrayList<String>();
-
-        @Override
-        public Object onPush( final Digester d, final String stackName, final Object o )
-        {
-            final String msg = "push:" + stackName + ":" + o.toString();
-            events.add( msg );
-
-            final String str = o.toString();
-            if ( str.startsWith( "replpush" ) )
-            {
-                return new String( str );
-            }
-            return o;
-        }
-
-        @Override
-        public Object onPop( final Digester d, final String stackName, final Object o )
-        {
-            final String msg = "pop:" + stackName + ":" + o.toString();
-            events.add( msg );
-            final String str = o.toString();
-            if ( str.startsWith( "replpop" ) )
-            {
-                return new String( str );
-            }
-            return o;
-        }
-    }
-
-    /**
-     * Test custom StackAction subclasses.
-     */
-    @Test
-    public void testStackAction()
-    {
-        final TrackingStackAction action = new TrackingStackAction();
-
-        final Object obj1 = "obj1";
-        final Object obj2 = "obj2";
-        final Object obj3 = "replpop.obj3";
-        final Object obj4 = "replpush.obj4";
-
-        final Object obj8 = "obj8";
-        final Object obj9 = "obj9";
-
-        final Digester d = new Digester();
-        d.setStackAction( action );
-
-        assertEquals( 0, action.events.size() );
-        d.push( obj1 );
-        d.push( obj2 );
-        d.push( obj3 );
-        d.push( obj4 );
-
-        assertNotNull( d.peek( 0 ) );
-        // for obj4, a copy should have been pushed
-        assertNotSame( obj4, d.peek( 0 ) );
-        assertEquals( obj4, d.peek( 0 ) );
-        // for obj3, replacement only occurs on pop
-        assertSame( obj3, d.peek( 1 ) );
-        assertSame( obj2, d.peek( 2 ) );
-        assertSame( obj1, d.peek( 3 ) );
-
-        final Object obj4a = d.pop();
-        final Object obj3a = d.pop();
-        final Object obj2a = d.pop();
-        final Object obj1a = d.pop();
-
-        assertFalse( obj4 == obj4a );
-        assertEquals( obj4, obj4a );
-        assertFalse( obj3 == obj4a );
-        assertEquals( obj3, obj3a );
-        assertSame( obj2, obj2a );
-        assertSame( obj1, obj1a );
-
-        d.push( "stack1", obj8 );
-        d.push( "stack1", obj9 );
-        final Object obj9a = d.pop( "stack1" );
-        final Object obj8a = d.pop( "stack1" );
-
-        assertSame( obj8, obj8a );
-        assertSame( obj9, obj9a );
-
-        assertEquals( 12, action.events.size() );
-        assertEquals( "push:null:obj1", action.events.get( 0 ) );
-        assertEquals( "push:null:obj2", action.events.get( 1 ) );
-        assertEquals( "push:null:replpop.obj3", action.events.get( 2 ) );
-        assertEquals( "push:null:replpush.obj4", action.events.get( 3 ) );
-        assertEquals( "pop:null:replpush.obj4", action.events.get( 4 ) );
-        assertEquals( "pop:null:replpop.obj3", action.events.get( 5 ) );
-        assertEquals( "pop:null:obj2", action.events.get( 6 ) );
-        assertEquals( "pop:null:obj1", action.events.get( 7 ) );
-
-        assertEquals( "push:stack1:obj8", action.events.get( 8 ) );
-        assertEquals( "push:stack1:obj9", action.events.get( 9 ) );
-        assertEquals( "pop:stack1:obj9", action.events.get( 10 ) );
-        assertEquals( "pop:stack1:obj8", action.events.get( 11 ) );
-    }
-}
+/* $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.digester3;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.EmptyStackException;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * <p>
+ * Test Case for the Digester class. These tests exercise the individual methods of a Digester, but do not attempt to
+ * process complete documents.
+ * </p>
+ */
+public class DigesterTestCase
+{
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The digester instance we will be processing.
+     */
+    protected Digester digester = null;
+
+    /**
+     * The set of public identifiers, and corresponding resource names, for the versions of the DTDs that we know about.
+     * There <strong>MUST</strong> be an even number of Strings in this array.
+     */
+    protected static final String registrations[] = { "-//Netscape Communications//DTD RSS 0.9//EN",
+        "/org/apache/commons/digester3/rss/rss-0.9.dtd", "-//Netscape Communications//DTD RSS 0.91//EN",
+        "/org/apache/commons/digester3/rss/rss-0.91.dtd", };
+
+    // -------------------------------------------------- Overall Test Methods
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    @Before
+    public void setUp()
+    {
+
+        digester = new Digester();
+        digester.setRules( new RulesBase() );
+
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    @After
+    public void tearDown()
+    {
+
+        digester = null;
+
+    }
+
+    // ------------------------------------------------ Individual Test Methods
+
+    /**
+     * Test {@code null} parsing. (should lead to {@code IllegalArgumentException}s)
+     */
+    @Test
+    public void testNullFileParse()
+        throws Exception
+    {
+
+        try
+        {
+            digester.parse( (File) null );
+            fail( "Expected IllegalArgumentException with null argument" );
+        }
+        catch ( final IllegalArgumentException e )
+        {
+            // expected
+        }
+
+    }
+
+    @Test
+    public void testNullInputSourceParse()
+        throws Exception
+    {
+
+        try
+        {
+            digester.parse( (InputSource) null );
+            fail( "Expected IllegalArgumentException with null argument" );
+        }
+        catch ( final IllegalArgumentException e )
+        {
+            // expected
+        }
+
+    }
+
+    @Test
+    public void testNullInputStreamParse()
+        throws Exception
+    {
+
+        try
+        {
+            digester.parse( (InputStream) null );
+            fail( "Expected IllegalArgumentException with null argument" );
+        }
+        catch ( final IllegalArgumentException e )
+        {
+            // expected
+        }
+
+    }
+
+    @Test
+    public void testNullReaderParse()
+        throws Exception
+    {
+
+        try
+        {
+            digester.parse( (Reader) null );
+            fail( "Expected IllegalArgumentException with null argument" );
+        }
+        catch ( final IllegalArgumentException e )
+        {
+            // expected
+        }
+
+    }
+
+    @Test
+    public void testNullStringParse()
+        throws Exception
+    {
+
+        try
+        {
+            digester.parse( (String) null );
+            fail( "Expected IllegalArgumentException with null argument" );
+        }
+        catch ( final IllegalArgumentException e )
+        {
+            // expected
+        }
+
+    }
+
+    @Test
+    public void testNullURLParse()
+        throws Exception
+    {
+
+        try
+        {
+            digester.parse( (URL) null );
+            fail( "Expected IllegalArgumentException with null argument" );
+        }
+        catch ( final IllegalArgumentException e )
+        {
+            // expected
+        }
+
+    }
+
+    /**
+     * Test the basic property getters and setters.
+     */
+    @Test
+    public void testProperties()
+    {
+
+        assertNull( "Initial error handler is null", digester.getErrorHandler() );
+        digester.setErrorHandler( digester );
+        assertTrue( "Set error handler is digester", digester.getErrorHandler() == digester );
+        digester.setErrorHandler( null );
+        assertNull( "Reset error handler is null", digester.getErrorHandler() );
+
+        assertTrue( "Initial namespace aware is false", !digester.getNamespaceAware() );
+        digester.setNamespaceAware( true );
+        assertTrue( "Set namespace aware is true", digester.getNamespaceAware() );
+        digester.setNamespaceAware( false );
+        assertTrue( "Reset namespace aware is false", !digester.getNamespaceAware() );
+
+        assertTrue( "Initial validating is false", !digester.getValidating() );
+        digester.setValidating( true );
+        assertTrue( "Set validating is true", digester.getValidating() );
+        digester.setValidating( false );
+        assertTrue( "Reset validating is false", !digester.getValidating() );
+
+    }
+
+    /**
+     * Test registration of URLs for specified public identifiers.
+     */
+    @Test
+    public void testRegistrations()
+    {
+
+        Map<String, URL> map = digester.getRegistrations();
+        assertEquals( "Initially zero registrations", 0, map.size() );
+        int n = 0;
+        for ( int i = 0; i < registrations.length; i += 2 )
+        {
+            final URL url = this.getClass().getResource( registrations[i + 1] );
+            if ( url != null )
+            {
+                digester.register( registrations[i], url );
+                n++;
+            }
+        }
+        map = digester.getRegistrations();
+        assertEquals( "Registered two URLs", n, map.size() );
+
+        final int count[] = new int[n];
+        for ( int i = 0; i < n; i++ ) {
+            count[i] = 0;
+        }
+        for ( final String key : map.keySet() )
+        {
+            for ( int i = 0; i < n; i++ )
+            {
+                if ( key.equals( registrations[i * 2] ) )
+                {
+                    count[i]++;
+                    break;
+                }
+            }
+        }
+        for ( int i = 0; i < n; i++ ) {
+            assertEquals( "Count for key " + registrations[i * 2], 1, count[i] );
+        }
+
+    }
+
+    /**
+     * Basic test for rule creation and matching.
+     */
+    @Test
+    public void testRules()
+    {
+
+        assertEquals( "Initial rules list is empty", 0, digester.getRules().match( null, "a", null, null ).size() );
+        digester.addSetProperties( "a" );
+        assertEquals( "Add a matching rule", 1, digester.getRules().match( null, "a", null, null ).size() );
+        digester.addSetProperties( "b" );
+        assertEquals( "Add a non-matching rule", 1, digester.getRules().match( null, "a", null, null ).size() );
+        digester.addSetProperties( "a/b" );
+        assertEquals( "Add a non-matching nested rule", 1, digester.getRules().match( null, "a", null, null ).size() );
+        digester.addSetProperties( "a/b" );
+        assertEquals( "Add a second matching rule", 2, digester.getRules().match( null, "a/b", null, null ).size() );
+
+    }
+
+    /**
+     * <p>
+     * Test matching rules in {@link RulesBase}.
+     * </p>
+     * <p>
+     * Tests:
+     * </p>
+     * <ul>
+     * <li>exact match</li>
+     * <li>tail match</li>
+     * <li>longest pattern rule</li>
+     * </ul>
+     */
+    @Test
+    public void testRulesBase()
+    {
+
+        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
+
+        // We're going to set up
+        digester.addRule( "a/b/c/d", new TestRule( "a/b/c/d" ) );
+        digester.addRule( "*/d", new TestRule( "*/d" ) );
+        digester.addRule( "*/c/d", new TestRule( "*/c/d" ) );
+
+        // Test exact match
+        assertEquals( "Exact match takes precedence 1", 1, digester.getRules().match( null, "a/b/c/d", null, null ).size() );
+        assertEquals( "Exact match takes precedence 2", "a/b/c/d",
+                      ( (TestRule) digester.getRules().match( null, "a/b/c/d", null, null ).iterator().next() ).getIdentifier() );
+
+        // Test wildcard tail matching
+        assertEquals( "Wildcard tail matching rule 1", 1, digester.getRules().match( null, "a/b/d", null, null ).size() );
+        assertEquals( "Wildcard tail matching rule 2", "*/d",
+                      ( (TestRule) digester.getRules().match( null, "a/b/d", null, null ).iterator().next() ).getIdentifier() );
+
+        // Test the longest matching pattern rule
+        assertEquals( "Longest tail rule 1", 1, digester.getRules().match( null, "x/c/d", null, null ).size() );
+        assertEquals( "Longest tail rule 2", "*/c/d",
+                      ( (TestRule) digester.getRules().match( null, "x/c/d", null, null ).iterator().next() ).getIdentifier() );
+
+    }
+
+    /**
+     * Test the basic stack mechanisms.
+     */
+    @Test
+    public void testStackMethods()
+    {
+
+        Object value;
+
+        // New stack must be empty
+        assertEquals( "New stack is empty", 0, digester.getCount() );
+        value = digester.peek();
+        assertNull( "New stack peek() returns null", value );
+        value = digester.pop();
+        assertNull( "New stack pop() returns null", value );
+
+        // Test pushing and popping activities
+        digester.push( "First Item" );
+        assertEquals( "Pushed one item size", 1, digester.getCount() );
+        value = digester.peek();
+        assertNotNull( "Peeked first item is not null", value );
+        assertEquals( "Peeked first item value", "First Item", value );
+
+        digester.push( "Second Item" );
+        assertEquals( "Pushed two items size", 2, digester.getCount() );
+        value = digester.peek();
+        assertNotNull( "Peeked second item is not null", value );
+        assertEquals( "Peeked second item value", "Second Item", value );
+
+        value = digester.pop();
+        assertEquals( "Popped stack size", 1, digester.getCount() );
+        assertNotNull( "Popped second item is not null", value );
+        assertEquals( "Popped second item value", "Second Item", value );
+        value = digester.peek();
+        assertNotNull( "Remaining item is not null", value );
+        assertEquals( "Remaining item value", "First Item", value );
+        assertEquals( "Remaining stack size", 1, digester.getCount() );
+
+        // Cleared stack is empty
+        digester.push( "Dummy Item" );
+        digester.clear();
+        assertEquals( "Cleared stack is empty", 0, digester.getCount() );
+        value = digester.peek();
+        assertNull( "Cleared stack peek() returns null", value );
+        value = digester.pop();
+        assertNull( "Cleared stack pop() returns null", value );
+
+    }
+
+    @Test
+    public void testOnceAndOnceOnly()
+        throws Exception
+    {
+
+        class TestConfigureDigester
+            extends Digester
+        {
+            public int called = 0;
+
+            public TestConfigureDigester()
+            {
+            }
+
+            @Override
+            protected void initialize()
+            {
+                called++;
+            }
+        }
+
+        final TestConfigureDigester digester = new TestConfigureDigester();
+
+        final String xml = "<?xml version='1.0'?><document/>";
+        digester.parse( new StringReader( xml ) );
+
+        assertEquals( "Initialize should be called once and only once", 1, digester.called );
+    }
+
+    @Test
+    public void testBasicSubstitution()
+        throws Exception
+    {
+        class TestSubRule
+            extends Rule
+        {
+            public String body;
+
+            public Attributes attributes;
+
+            @Override
+            public void begin( final String namespace, final String name, final Attributes attributes )
+            {
+                this.attributes = new AttributesImpl( attributes );
+            }
+
+            @Override
+            public void body( final String namespace, final String name, final String text )
+            {
+                this.body = text;
+            }
+        }
+
+        final TestSubRule tsr = new TestSubRule();
+        final Digester digester = new Digester();
+        digester.addRule( "alpha/beta", tsr );
+
+        // it's not easy to transform dirty harry into the mighty circus - but let's give it a try
+        final String xml =
+            "<?xml version='1.0'?><alpha><beta forname='Dirty' surname='Harry'>Do you feel luck punk?</beta></alpha>";
+        InputSource in = new InputSource( new StringReader( xml ) );
+
+        digester.parse( in );
+
+        assertEquals( "Unsubstituted body text", "Do you feel luck punk?", tsr.body );
+        assertEquals( "Unsubstituted number of attributes", 2, tsr.attributes.getLength() );
+        assertEquals( "Unsubstituted forname attribute value", "Dirty", tsr.attributes.getValue( "forname" ) );
+        assertEquals( "Unsubstituted surname attribute value", "Harry", tsr.attributes.getValue( "surname" ) );
+
+        digester.setSubstitutor( new Substitutor()
+        {
+            @Override
+            public Attributes substitute( final Attributes attributes )
+            {
+                final AttributesImpl results = new AttributesImpl();
+                results.addAttribute( "", "python", "python", "CDATA", "Cleese" );
+                return results;
+            }
+
+            @Override
+            public String substitute( final String bodyText )
+            {
+                return "And now for something completely different...";
+            }
+        } );
+
+        // now transform into the full monty
+        in = new InputSource( new StringReader( xml ) );
+        digester.parse( in );
+
+        assertEquals( "Substituted body text", "And now for something completely different...", tsr.body );
+        assertEquals( "Substituted number of attributes", 1, tsr.attributes.getLength() );
+        assertEquals( "Substituted python attribute value", "Cleese", tsr.attributes.getValue( "", "python" ) );
+    }
+
+    /** Tests the push-peek-pop cycle for a named stack */
+    @Test
+    public void testNamedStackPushPeekPop()
+        throws Exception
+    {
+        final BigDecimal archimedesAveragePi = new BigDecimal( "3.1418" );
+        final String testStackName = "org.apache.commons.digester3.tests.testNamedStackPushPeekPop";
+        final Digester digester = new Digester();
+        assertTrue( "Stack starts empty:", digester.isEmpty( testStackName ) );
+        digester.push( testStackName, archimedesAveragePi );
+        assertEquals( "Peeked value:", archimedesAveragePi, digester.peek( testStackName ) );
+        assertEquals( "Popped value:", archimedesAveragePi, digester.pop( testStackName ) );
+        assertTrue( "Stack ends empty:", digester.isEmpty( testStackName ) );
+
+        digester.push( testStackName, "1" );
+        digester.push( testStackName, "2" );
+        digester.push( testStackName, "3" );
+
+        assertEquals( "Peek#1", "1", digester.peek( testStackName, 2 ) );
+        assertEquals( "Peek#2", "2", digester.peek( testStackName, 1 ) );
+        assertEquals( "Peek#3", "3", digester.peek( testStackName, 0 ) );
+        assertEquals( "Peek#3a", "3", digester.peek( testStackName ) );
+
+        try
+        {
+            // peek beyond stack
+            digester.peek( testStackName, 3 );
+            fail( "Peek#4 failed to throw an exception." );
+        }
+        catch ( final EmptyStackException ex )
+        {
+            // ok, expected
+        }
+
+        try
+        {
+            // peek a nonexistent named stack
+            digester.peek( "no.such.stack", 0 );
+            fail( "Peeking a non-existent stack failed to throw an exception." );
+        }
+        catch ( final EmptyStackException ex )
+        {
+            // ok, expected
+        }
+    }
+
+    /** Tests that values are stored independently */
+    @Test
+    public void testNamedIndependence()
+    {
+        final String testStackOneName = "org.apache.commons.digester3.tests.testNamedIndependenceOne";
+        final String testStackTwoName = "org.apache.commons.digester3.tests.testNamedIndependenceTwo";
+        final Digester digester = new Digester();
+        digester.push( testStackOneName, "Tweedledum" );
+        digester.push( testStackTwoName, "Tweedledee" );
+        assertEquals( "Popped value one:", "Tweedledum", digester.pop( testStackOneName ) );
+        assertEquals( "Popped value two:", "Tweedledee", digester.pop( testStackTwoName ) );
+    }
+
+    /** Tests popping named stack not yet pushed */
+    @Test
+    public void testPopNamedStackNotPushed()
+    {
+        final String testStackName = "org.apache.commons.digester3.tests.testPopNamedStackNotPushed";
+        final Digester digester = new Digester();
+        try
+        {
+
+            digester.pop( testStackName );
+            fail( "Expected an EmptyStackException" );
+
+        }
+        catch ( final EmptyStackException e )
+        {
+            // expected
+        }
+
+        try
+        {
+
+            digester.peek( testStackName );
+            fail( "Expected an EmptyStackException" );
+
+        }
+        catch ( final EmptyStackException e )
+        {
+            // expected
+        }
+    }
+
+    /** Tests for isEmpty */
+    @Test
+    public void testNamedStackIsEmpty()
+    {
+        final String testStackName = "org.apache.commons.digester3.tests.testNamedStackIsEmpty";
+        final Digester digester = new Digester();
+        assertTrue( "A named stack that has no object pushed onto it yet should be empty",
+                    digester.isEmpty( testStackName ) );
+
+        digester.push( testStackName, "Some test value" );
+        assertFalse( "A named stack that has an object pushed onto it should be not empty",
+                     digester.isEmpty( testStackName ) );
+
+        digester.peek( testStackName );
+        assertFalse( "Peek should not effect whether the stack is empty", digester.isEmpty( testStackName ) );
+
+        digester.pop( testStackName );
+        assertTrue( "A named stack that has it's last object popped is empty", digester.isEmpty( testStackName ) );
+    }
+
+    /**
+     * Test the Digester.getRoot method.
+     */
+    @Test
+    public void testGetRoot()
+        throws Exception
+    {
+        final Digester digester = new Digester();
+        digester.addRule( "root", new ObjectCreateRule( TestBean.class ) );
+
+        final String xml = "<root/>";
+        final InputSource in = new InputSource( new StringReader( xml ) );
+
+        digester.parse( in );
+
+        final Object root = digester.getRoot();
+        assertNotNull( "root object not retrieved", root );
+        assertTrue( "root object not a TestRule instance", ( root instanceof TestBean ) );
+    }
+
+    /** Utility class for method testStackAction */
+    private static class TrackingStackAction
+        implements StackAction
+    {
+        public ArrayList<String> events = new ArrayList<String>();
+
+        @Override
+        public Object onPush( final Digester d, final String stackName, final Object o )
+        {
+            final String msg = "push:" + stackName + ":" + o.toString();
+            events.add( msg );
+
+            final String str = o.toString();
+            if ( str.startsWith( "replpush" ) )
+            {
+                return new String( str );
+            }
+            return o;
+        }
+
+        @Override
+        public Object onPop( final Digester d, final String stackName, final Object o )
+        {
+            final String msg = "pop:" + stackName + ":" + o.toString();
+            events.add( msg );
+            final String str = o.toString();
+            if ( str.startsWith( "replpop" ) )
+            {
+                return new String( str );
+            }
+            return o;
+        }
+    }
+
+    /**
+     * Test custom StackAction subclasses.
+     */
+    @Test
+    public void testStackAction()
+    {
+        final TrackingStackAction action = new TrackingStackAction();
+
+        final Object obj1 = "obj1";
+        final Object obj2 = "obj2";
+        final Object obj3 = "replpop.obj3";
+        final Object obj4 = "replpush.obj4";
+
+        final Object obj8 = "obj8";
+        final Object obj9 = "obj9";
+
+        final Digester d = new Digester();
+        d.setStackAction( action );
+
+        assertEquals( 0, action.events.size() );
+        d.push( obj1 );
+        d.push( obj2 );
+        d.push( obj3 );
+        d.push( obj4 );
+
+        assertNotNull( d.peek( 0 ) );
+        // for obj4, a copy should have been pushed
+        assertNotSame( obj4, d.peek( 0 ) );
+        assertEquals( obj4, d.peek( 0 ) );
+        // for obj3, replacement only occurs on pop
+        assertSame( obj3, d.peek( 1 ) );
+        assertSame( obj2, d.peek( 2 ) );
+        assertSame( obj1, d.peek( 3 ) );
+
+        final Object obj4a = d.pop();
+        final Object obj3a = d.pop();
+        final Object obj2a = d.pop();
+        final Object obj1a = d.pop();
+
+        assertFalse( obj4 == obj4a );
+        assertEquals( obj4, obj4a );
+        assertFalse( obj3 == obj4a );
+        assertEquals( obj3, obj3a );
+        assertSame( obj2, obj2a );
+        assertSame( obj1, obj1a );
+
+        d.push( "stack1", obj8 );
+        d.push( "stack1", obj9 );
+        final Object obj9a = d.pop( "stack1" );
+        final Object obj8a = d.pop( "stack1" );
+
+        assertSame( obj8, obj8a );
+        assertSame( obj9, obj9a );
+
+        assertEquals( 12, action.events.size() );
+        assertEquals( "push:null:obj1", action.events.get( 0 ) );
+        assertEquals( "push:null:obj2", action.events.get( 1 ) );
+        assertEquals( "push:null:replpop.obj3", action.events.get( 2 ) );
+        assertEquals( "push:null:replpush.obj4", action.events.get( 3 ) );
+        assertEquals( "pop:null:replpush.obj4", action.events.get( 4 ) );
+        assertEquals( "pop:null:replpop.obj3", action.events.get( 5 ) );
+        assertEquals( "pop:null:obj2", action.events.get( 6 ) );
+        assertEquals( "pop:null:obj1", action.events.get( 7 ) );
+
+        assertEquals( "push:stack1:obj8", action.events.get( 8 ) );
+        assertEquals( "push:stack1:obj9", action.events.get( 9 ) );
+        assertEquals( "pop:stack1:obj9", action.events.get( 10 ) );
+        assertEquals( "pop:stack1:obj8", action.events.get( 11 ) );
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/ExtendedBaseRulesTestCase.java b/core/src/test/java/org/apache/commons/digester3/ExtendedBaseRulesTestCase.java
index c9ce9501..6ced0218 100644
--- a/core/src/test/java/org/apache/commons/digester3/ExtendedBaseRulesTestCase.java
+++ b/core/src/test/java/org/apache/commons/digester3/ExtendedBaseRulesTestCase.java
@@ -1,412 +1,410 @@
-/* $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.digester3;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.Iterator;
-import java.util.List;
-
-import org.junit.Test;
-
-/**
- * <p>
- * Runs standard tests for RulesBase as well as tests of extensions.
- *
- * @author Robert Burrell Donkin <ro...@mac.com>
- */
-public class ExtendedBaseRulesTestCase
-    extends RulesBaseTestCase
-{
-
-    // -------------------------------------------------- Overall Test Methods
-
-    /**
-     * <p>
-     * This should be overriden by subclasses.
-     *
-     * @return the matching rules to be tested.
-     */
-    @Override
-    protected Rules createMatchingRulesForTest()
-    {
-
-        return new ExtendedBaseRules();
-    }
-
-    /**
-     * Basic test of parent matching rules. A parent match matches any child of a particular kind of parent. A wild
-     * parent has a wildcard prefix. This method tests non-universal wildcards.
-     */
-    @Test
-    public void testBasicParentMatch()
-    {
-
-        // clear any existing rules
-        digester.getRules().clear();
-
-        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
-
-        // Set up rules
-        // since these are all NON-UNIVERSAL matches
-        // only expect one match at each stage
-        digester.addRule( "alpha/beta/gamma/delta", new TestRule( "exact" ) );
-        digester.addRule( "*/beta/gamma/epsilon", new TestRule( "wild_child" ) );
-        digester.addRule( "alpha/beta/gamma/?", new TestRule( "exact_parent" ) );
-        digester.addRule( "*/beta/gamma/?", new TestRule( "wild_parent" ) );
-
-        // this should match just the exact since this has presidence
-        List<Rule> list = digester.getRules().match( null, "alpha/beta/gamma/delta", null, null );
-
-        // all three rules should match
-        assertEquals( "Testing basic parent mismatch (A)", 1, list.size() );
-
-        Iterator<Rule> it = list.iterator();
-        assertEquals( "Testing basic parent mismatch (B)", "exact", ( (TestRule) it.next() ).getIdentifier() );
-
-        // we don't have an exact match for this child so we should get the exact parent
-        list = digester.getRules().match( null, "alpha/beta/gamma/epsilon", null, null );
-
-        // all three rules should match
-        assertEquals( "Testing basic parent mismatch (C)", 1, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Testing basic parent mismatch (D)", "exact_parent", ( (TestRule) it.next() ).getIdentifier() );
-
-        // wild child overrides wild parent
-        list = digester.getRules().match( null, "alpha/omega/beta/gamma/epsilon", null, null );
-
-        // all three rules should match
-        assertEquals( "Testing basic parent mismatch (E)", 1, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Testing basic parent mismatch (F)", "wild_child", ( (TestRule) it.next() ).getIdentifier() );
-
-        // nothing else matches so return wild parent
-        list = digester.getRules().match( null, "alpha/omega/beta/gamma/zeta", null, null );
-
-        // all three rules should match
-        assertEquals( "Testing basic parent mismatch (G)", 1, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Testing basic parent mismatch (H)", "wild_parent", ( (TestRule) it.next() ).getIdentifier() );
-
-        // clean up
-        digester.getRules().clear();
-
-    }
-
-    /**
-     * Basic test of universal matching rules. Universal rules act independent.
-     */
-    @Test
-    public void testBasicUniversal()
-    {
-
-        // clear any existing rules
-        digester.getRules().clear();
-
-        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
-
-        // Set up rules
-        // set up universal matches against non-universal ones
-        digester.addRule( "alpha/beta/gamma", new TestRule( "exact" ) );
-        digester.addRule( "*/beta/gamma", new TestRule( "non_wild_head" ) );
-        digester.addRule( "!*/beta/gamma", new TestRule( "universal_wild_head" ) );
-        digester.addRule( "!alpha/beta/gamma/?", new TestRule( "universal_wild_child" ) );
-        digester.addRule( "alpha/beta/gamma/?", new TestRule( "non_wild_child" ) );
-        digester.addRule( "alpha/beta/gamma/epsilon", new TestRule( "exact2" ) );
-        digester.addRule( "alpha/epsilon/beta/gamma/zeta", new TestRule( "exact3" ) );
-        digester.addRule( "*/gamma/?", new TestRule( "non_wildhead_child" ) );
-        digester.addRule( "!*/epsilon/beta/gamma/?", new TestRule( "universal_wildhead_child" ) );
-
-        List<Rule> list;
-        Iterator<Rule> it;
-
-        // test universal wild head
-        list = digester.getRules().match( null, "alpha/beta/gamma", null, null );
-
-        assertEquals( "Testing universal wildcard mismatch (A)", 2, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Testing universal wildcard mismatch (B)", "exact", ( (TestRule) it.next() ).getIdentifier() );
-        assertEquals( "Testing universal wildcard mismatch (C)", "universal_wild_head",
-                      ( (TestRule) it.next() ).getIdentifier() );
-
-        // test universal parent
-        list = digester.getRules().match( null, "alpha/beta/gamma/epsilon", null, null );
-
-        assertEquals( "Testing universal wildcard mismatch (D)", 2, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Testing universal wildcard mismatch (E)", "universal_wild_child",
-                      ( (TestRule) it.next() ).getIdentifier() );
-        assertEquals( "Testing universal wildcard mismatch (F)", "exact2", ( (TestRule) it.next() ).getIdentifier() );
-
-        // test universal parent
-        list = digester.getRules().match( null, "alpha/beta/gamma/zeta", null, null );
-
-        assertEquals( "Testing universal wildcard mismatch (G)", 2, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Testing universal wildcard mismatch (H)", "universal_wild_child",
-                      ( (TestRule) it.next() ).getIdentifier() );
-        assertEquals( "Testing universal wildcard mismatch (I)", "non_wild_child",
-                      ( (TestRule) it.next() ).getIdentifier() );
-
-        // test wildcard universal parent
-        list = digester.getRules().match( null, "alpha/epsilon/beta/gamma/alpha", null, null );
-
-        assertEquals( "Testing universal wildcard mismatch (J)", 2, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Testing universal wildcard mismatch (K)", "non_wildhead_child",
-                      ( (TestRule) it.next() ).getIdentifier() );
-        assertEquals( "Testing universal wildcard mismatch (L)", "universal_wildhead_child",
-                      ( (TestRule) it.next() ).getIdentifier() );
-
-        // test wildcard universal parent
-        list = digester.getRules().match( null, "alpha/epsilon/beta/gamma/zeta", null, null );
-
-        assertEquals( "Testing universal wildcard mismatch (M)", 2, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Testing universal wildcard mismatch (M)", "exact3", ( (TestRule) it.next() ).getIdentifier() );
-        assertEquals( "Testing universal wildcard mismatch (O)", "universal_wildhead_child",
-                      ( (TestRule) it.next() ).getIdentifier() );
-
-        // clean up
-        digester.getRules().clear();
-
-    }
-
-    /**
-     * Basic test of wild matches. A universal will match matches anything! A non-universal will match matches anything
-     * not matched by something else. This method tests non-universal and universal wild matches.
-     */
-    @Test
-    public void testWildMatch()
-    {
-
-        // clear any existing rules
-        digester.getRules().clear();
-
-        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
-
-        // Set up rules
-        // The combinations a little large to test everything but we'll pick a couple and try them.
-        digester.addRule( "*", new TestRule( "basic_wild" ) );
-        digester.addRule( "!*", new TestRule( "universal_wild" ) );
-        digester.addRule( "alpha/beta/gamma/delta", new TestRule( "exact" ) );
-        digester.addRule( "*/beta/gamma/?", new TestRule( "wild_parent" ) );
-
-        List<Rule> list;
-        Iterator<Rule> it;
-
-        // The universal wild will always match whatever else does
-        list = digester.getRules().match( null, "alpha/beta/gamma/delta", null, null );
-
-        // all three rules should match
-        assertEquals( "Testing wild mismatch (A)", 2, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Testing wild mismatch (B)", "universal_wild", ( (TestRule) it.next() ).getIdentifier() );
-        assertEquals( "Testing wild mismatch (C)", "exact", ( (TestRule) it.next() ).getIdentifier() );
-
-        // The universal wild will always match whatever else does
-        list = digester.getRules().match( null, "alpha/beta/gamma/epsilon", null, null );
-
-        assertEquals( "Testing wild mismatch (D)", 2, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Testing wild mismatch (E)", "universal_wild", ( (TestRule) it.next() ).getIdentifier() );
-        assertEquals( "Testing wild mismatch (F)", "wild_parent", ( (TestRule) it.next() ).getIdentifier() );
-
-        // The universal wild will always match whatever else does
-        // we have no other non-universal matching so this will match the non-universal wild as well
-        list = digester.getRules().match( null, "alpha/gamma", null, null );
-
-        assertEquals( "Testing wild mismatch (G)", 2, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Testing wild mismatch (H)", "basic_wild", ( (TestRule) it.next() ).getIdentifier() );
-        assertEquals( "Testing wild mismatch (I)", "universal_wild", ( (TestRule) it.next() ).getIdentifier() );
-
-        // clean up
-        digester.getRules().clear();
-
-    }
-
-    /**
-     * Basic test of wild matches. A universal will match matches anything! A non-universal will match matches anything
-     * not matched by something else. This method tests non-universal and universal wild matches.
-     */
-    @Test
-    public void testRootTailMatch()
-    {
-
-        // clear any existing rules
-        digester.getRules().clear();
-
-        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
-
-        // Set up rules
-        // The combinations a little large to test everything but we'll pick a couple and try them.
-        digester.addRule( "*/a", new TestRule( "a_tail" ) );
-
-        List<Rule> list;
-
-        list = digester.getRules().match( null, "a", null, null );
-
-        assertEquals( "Testing tail wrong size (A)", 1, list.size() );
-        assertEquals( "Testing tail mismatch (B)", "a_tail", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-
-        list = digester.getRules().match( null, "beta/a", null, null );
-
-        assertEquals( "Testing tail wrong size (C)", 1, list.size() );
-        assertEquals( "Testing tail mismatch (D)", "a_tail", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-
-        list = digester.getRules().match( null, "be/aaa", null, null );
-
-        assertEquals( "Testing tail no matches (E)", 0, list.size() );
-
-        list = digester.getRules().match( null, "aaa", null, null );
-
-        assertEquals( "Testing tail no matches (F)", 0, list.size() );
-
-        list = digester.getRules().match( null, "a/beta", null, null );
-
-        assertEquals( "Testing tail no matches (G)", 0, list.size() );
-
-        // clean up
-        digester.getRules().clear();
-
-    }
-
-    @Test
-    public void testAncesterMatch()
-        throws Exception
-    {
-        // test fixed root ancester
-        digester.getRules().clear();
-
-        digester.addRule( "!a/b/*", new TestRule( "uni-a-b-star" ) );
-        digester.addRule( "a/b/*", new TestRule( "a-b-star" ) );
-        digester.addRule( "a/b/c", new TestRule( "a-b-c" ) );
-        digester.addRule( "a/b/?", new TestRule( "a-b-child" ) );
-
-        List<Rule> list = digester.getRules().match( null, "a/b/c", null, null );
-
-        assertEquals( "Simple ancester matches (1)", 2, list.size() );
-        assertEquals( "Univeral ancester mismatch (1)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-        assertEquals( "Parent precedence failure", "a-b-c", ( (TestRule) list.get( 1 ) ).getIdentifier() );
-
-        list = digester.getRules().match( null, "a/b/b", null, null );
-        assertEquals( "Simple ancester matches (2)", 2, list.size() );
-        assertEquals( "Univeral ancester mismatch (2)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-        assertEquals( "Child precedence failure", "a-b-child", ( (TestRule) list.get( 1 ) ).getIdentifier() );
-
-        list = digester.getRules().match( null, "a/b/d", null, null );
-        assertEquals( "Simple ancester matches (3)", 2, list.size() );
-        assertEquals( "Univeral ancester mismatch (3)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-        assertEquals( "Ancester mismatch (1)", "a-b-child", ( (TestRule) list.get( 1 ) ).getIdentifier() );
-
-        list = digester.getRules().match( null, "a/b/d/e/f", null, null );
-        assertEquals( "Simple ancester matches (4)", 2, list.size() );
-        assertEquals( "Univeral ancester mismatch (4)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-        assertEquals( "Ancester mismatch (2)", "a-b-star", ( (TestRule) list.get( 1 ) ).getIdentifier() );
-
-        // test wild root ancester
-        digester.getRules().clear();
-
-        digester.addRule( "!*/a/b/*", new TestRule( "uni-star-a-b-star" ) );
-        digester.addRule( "*/b/c/*", new TestRule( "star-b-c-star" ) );
-        digester.addRule( "*/b/c/d", new TestRule( "star-b-c-d" ) );
-        digester.addRule( "a/b/c", new TestRule( "a-b-c" ) );
-
-        list = digester.getRules().match( null, "a/b/c", null, null );
-        assertEquals( "Wild ancester match (1)", 2, list.size() );
-        assertEquals( "Univeral ancester mismatch (5)", "uni-star-a-b-star",
-                      ( (TestRule) list.get( 0 ) ).getIdentifier() );
-        assertEquals( "Match missed (1)", "a-b-c", ( (TestRule) list.get( 1 ) ).getIdentifier() );
-
-        list = digester.getRules().match( null, "b/c", null, null );
-        assertEquals( "Wild ancester match (2)", 1, list.size() );
-        assertEquals( "Match missed (2)", "star-b-c-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-
-        list = digester.getRules().match( null, "a/b/c/d", null, null );
-        assertEquals( "Wild ancester match (3)", 2, list.size() );
-        assertEquals( "Match missed (3)", "uni-star-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-        assertEquals( "Match missed (4)", "star-b-c-d", ( (TestRule) list.get( 1 ) ).getIdentifier() );
-
-        list = digester.getRules().match( null, "b/b/c/e/d", null, null );
-        assertEquals( "Wild ancester match (2)", 1, list.size() );
-        assertEquals( "Match missed (5)", "star-b-c-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-    }
-
-    @Test
-    public void testLongMatch()
-    {
-
-        digester.getRules().clear();
-
-        digester.addRule( "a/b/c/d/*", new TestRule( "a-b-c-d-star" ) );
-
-        List<Rule> list = digester.getRules().match( null, "a/b/c/d/e", null, null );
-        assertEquals( "Long match (1)", 1, list.size() );
-        assertEquals( "Match missed (1)", "a-b-c-d-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-
-        list = digester.getRules().match( null, "a/b/c/d/e/f", null, null );
-        assertEquals( "Long match (2)", 1, list.size() );
-        assertEquals( "Match missed (2)", "a-b-c-d-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-
-        list = digester.getRules().match( null, "a/b/c/d/e/f/g", null, null );
-        assertEquals( "Long match (3)", 1, list.size() );
-        assertEquals( "Match missed (3)", "a-b-c-d-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-
-        list = digester.getRules().match( null, "a/b/c/d", null, null );
-        assertEquals( "Long match (4)", 0, list.size() );
-    }
-
-    @Test
-    public void testInstructors()
-    {
-        digester.getRules().clear();
-
-        digester.addRule( "!instructors/*", new TestRule( "instructors" ) );
-        digester.addRule( "!instructor/*", new TestRule( "instructor" ) );
-
-        final List<Rule> list = digester.getRules().match( null, "instructors", null, null );
-        assertEquals( "Only expect to match instructors", 1, list.size() );
-        assertEquals( "Instructors expected", "instructors", ( (TestRule) list.get( 0 ) ).getIdentifier() );
-
-    }
-
-    @Test
-    public void testMiddleInstructors()
-    {
-        digester.getRules().clear();
-
-        digester.addRule( "!instructors/*", new TestRule( "instructors" ) );
-
-        final List<Rule> list = digester.getRules().match( null, "/tosh/instructors/fiddlesticks", null, null );
-        assertEquals( "No matches expected", 0, list.size() );
-
-    }
-}
+/* $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.digester3;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * <p>
+ * Runs standard tests for RulesBase as well as tests of extensions.
+ */
+public class ExtendedBaseRulesTestCase
+    extends RulesBaseTestCase
+{
+
+    // -------------------------------------------------- Overall Test Methods
+
+    /**
+     * <p>
+     * This should be overriden by subclasses.
+     *
+     * @return the matching rules to be tested.
+     */
+    @Override
+    protected Rules createMatchingRulesForTest()
+    {
+
+        return new ExtendedBaseRules();
+    }
+
+    /**
+     * Basic test of parent matching rules. A parent match matches any child of a particular kind of parent. A wild
+     * parent has a wildcard prefix. This method tests non-universal wildcards.
+     */
+    @Test
+    public void testBasicParentMatch()
+    {
+
+        // clear any existing rules
+        digester.getRules().clear();
+
+        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
+
+        // Set up rules
+        // since these are all NON-UNIVERSAL matches
+        // only expect one match at each stage
+        digester.addRule( "alpha/beta/gamma/delta", new TestRule( "exact" ) );
+        digester.addRule( "*/beta/gamma/epsilon", new TestRule( "wild_child" ) );
+        digester.addRule( "alpha/beta/gamma/?", new TestRule( "exact_parent" ) );
+        digester.addRule( "*/beta/gamma/?", new TestRule( "wild_parent" ) );
+
+        // this should match just the exact since this has presidence
+        List<Rule> list = digester.getRules().match( null, "alpha/beta/gamma/delta", null, null );
+
+        // all three rules should match
+        assertEquals( "Testing basic parent mismatch (A)", 1, list.size() );
+
+        Iterator<Rule> it = list.iterator();
+        assertEquals( "Testing basic parent mismatch (B)", "exact", ( (TestRule) it.next() ).getIdentifier() );
+
+        // we don't have an exact match for this child so we should get the exact parent
+        list = digester.getRules().match( null, "alpha/beta/gamma/epsilon", null, null );
+
+        // all three rules should match
+        assertEquals( "Testing basic parent mismatch (C)", 1, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Testing basic parent mismatch (D)", "exact_parent", ( (TestRule) it.next() ).getIdentifier() );
+
+        // wild child overrides wild parent
+        list = digester.getRules().match( null, "alpha/omega/beta/gamma/epsilon", null, null );
+
+        // all three rules should match
+        assertEquals( "Testing basic parent mismatch (E)", 1, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Testing basic parent mismatch (F)", "wild_child", ( (TestRule) it.next() ).getIdentifier() );
+
+        // nothing else matches so return wild parent
+        list = digester.getRules().match( null, "alpha/omega/beta/gamma/zeta", null, null );
+
+        // all three rules should match
+        assertEquals( "Testing basic parent mismatch (G)", 1, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Testing basic parent mismatch (H)", "wild_parent", ( (TestRule) it.next() ).getIdentifier() );
+
+        // clean up
+        digester.getRules().clear();
+
+    }
+
+    /**
+     * Basic test of universal matching rules. Universal rules act independent.
+     */
+    @Test
+    public void testBasicUniversal()
+    {
+
+        // clear any existing rules
+        digester.getRules().clear();
+
+        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
+
+        // Set up rules
+        // set up universal matches against non-universal ones
+        digester.addRule( "alpha/beta/gamma", new TestRule( "exact" ) );
+        digester.addRule( "*/beta/gamma", new TestRule( "non_wild_head" ) );
+        digester.addRule( "!*/beta/gamma", new TestRule( "universal_wild_head" ) );
+        digester.addRule( "!alpha/beta/gamma/?", new TestRule( "universal_wild_child" ) );
+        digester.addRule( "alpha/beta/gamma/?", new TestRule( "non_wild_child" ) );
+        digester.addRule( "alpha/beta/gamma/epsilon", new TestRule( "exact2" ) );
+        digester.addRule( "alpha/epsilon/beta/gamma/zeta", new TestRule( "exact3" ) );
+        digester.addRule( "*/gamma/?", new TestRule( "non_wildhead_child" ) );
+        digester.addRule( "!*/epsilon/beta/gamma/?", new TestRule( "universal_wildhead_child" ) );
+
+        List<Rule> list;
+        Iterator<Rule> it;
+
+        // test universal wild head
+        list = digester.getRules().match( null, "alpha/beta/gamma", null, null );
+
+        assertEquals( "Testing universal wildcard mismatch (A)", 2, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Testing universal wildcard mismatch (B)", "exact", ( (TestRule) it.next() ).getIdentifier() );
+        assertEquals( "Testing universal wildcard mismatch (C)", "universal_wild_head",
+                      ( (TestRule) it.next() ).getIdentifier() );
+
+        // test universal parent
+        list = digester.getRules().match( null, "alpha/beta/gamma/epsilon", null, null );
+
+        assertEquals( "Testing universal wildcard mismatch (D)", 2, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Testing universal wildcard mismatch (E)", "universal_wild_child",
+                      ( (TestRule) it.next() ).getIdentifier() );
+        assertEquals( "Testing universal wildcard mismatch (F)", "exact2", ( (TestRule) it.next() ).getIdentifier() );
+
+        // test universal parent
+        list = digester.getRules().match( null, "alpha/beta/gamma/zeta", null, null );
+
+        assertEquals( "Testing universal wildcard mismatch (G)", 2, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Testing universal wildcard mismatch (H)", "universal_wild_child",
+                      ( (TestRule) it.next() ).getIdentifier() );
+        assertEquals( "Testing universal wildcard mismatch (I)", "non_wild_child",
+                      ( (TestRule) it.next() ).getIdentifier() );
+
+        // test wildcard universal parent
+        list = digester.getRules().match( null, "alpha/epsilon/beta/gamma/alpha", null, null );
+
+        assertEquals( "Testing universal wildcard mismatch (J)", 2, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Testing universal wildcard mismatch (K)", "non_wildhead_child",
+                      ( (TestRule) it.next() ).getIdentifier() );
+        assertEquals( "Testing universal wildcard mismatch (L)", "universal_wildhead_child",
+                      ( (TestRule) it.next() ).getIdentifier() );
+
+        // test wildcard universal parent
+        list = digester.getRules().match( null, "alpha/epsilon/beta/gamma/zeta", null, null );
+
+        assertEquals( "Testing universal wildcard mismatch (M)", 2, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Testing universal wildcard mismatch (M)", "exact3", ( (TestRule) it.next() ).getIdentifier() );
+        assertEquals( "Testing universal wildcard mismatch (O)", "universal_wildhead_child",
+                      ( (TestRule) it.next() ).getIdentifier() );
+
+        // clean up
+        digester.getRules().clear();
+
+    }
+
+    /**
+     * Basic test of wild matches. A universal will match matches anything! A non-universal will match matches anything
+     * not matched by something else. This method tests non-universal and universal wild matches.
+     */
+    @Test
+    public void testWildMatch()
+    {
+
+        // clear any existing rules
+        digester.getRules().clear();
+
+        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
+
+        // Set up rules
+        // The combinations a little large to test everything but we'll pick a couple and try them.
+        digester.addRule( "*", new TestRule( "basic_wild" ) );
+        digester.addRule( "!*", new TestRule( "universal_wild" ) );
+        digester.addRule( "alpha/beta/gamma/delta", new TestRule( "exact" ) );
+        digester.addRule( "*/beta/gamma/?", new TestRule( "wild_parent" ) );
+
+        List<Rule> list;
+        Iterator<Rule> it;
+
+        // The universal wild will always match whatever else does
+        list = digester.getRules().match( null, "alpha/beta/gamma/delta", null, null );
+
+        // all three rules should match
+        assertEquals( "Testing wild mismatch (A)", 2, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Testing wild mismatch (B)", "universal_wild", ( (TestRule) it.next() ).getIdentifier() );
+        assertEquals( "Testing wild mismatch (C)", "exact", ( (TestRule) it.next() ).getIdentifier() );
+
+        // The universal wild will always match whatever else does
+        list = digester.getRules().match( null, "alpha/beta/gamma/epsilon", null, null );
+
+        assertEquals( "Testing wild mismatch (D)", 2, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Testing wild mismatch (E)", "universal_wild", ( (TestRule) it.next() ).getIdentifier() );
+        assertEquals( "Testing wild mismatch (F)", "wild_parent", ( (TestRule) it.next() ).getIdentifier() );
+
+        // The universal wild will always match whatever else does
+        // we have no other non-universal matching so this will match the non-universal wild as well
+        list = digester.getRules().match( null, "alpha/gamma", null, null );
+
+        assertEquals( "Testing wild mismatch (G)", 2, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Testing wild mismatch (H)", "basic_wild", ( (TestRule) it.next() ).getIdentifier() );
+        assertEquals( "Testing wild mismatch (I)", "universal_wild", ( (TestRule) it.next() ).getIdentifier() );
+
+        // clean up
+        digester.getRules().clear();
+
+    }
+
+    /**
+     * Basic test of wild matches. A universal will match matches anything! A non-universal will match matches anything
+     * not matched by something else. This method tests non-universal and universal wild matches.
+     */
+    @Test
+    public void testRootTailMatch()
+    {
+
+        // clear any existing rules
+        digester.getRules().clear();
+
+        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
+
+        // Set up rules
+        // The combinations a little large to test everything but we'll pick a couple and try them.
+        digester.addRule( "*/a", new TestRule( "a_tail" ) );
+
+        List<Rule> list;
+
+        list = digester.getRules().match( null, "a", null, null );
+
+        assertEquals( "Testing tail wrong size (A)", 1, list.size() );
+        assertEquals( "Testing tail mismatch (B)", "a_tail", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+
+        list = digester.getRules().match( null, "beta/a", null, null );
+
+        assertEquals( "Testing tail wrong size (C)", 1, list.size() );
+        assertEquals( "Testing tail mismatch (D)", "a_tail", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+
+        list = digester.getRules().match( null, "be/aaa", null, null );
+
+        assertEquals( "Testing tail no matches (E)", 0, list.size() );
+
+        list = digester.getRules().match( null, "aaa", null, null );
+
+        assertEquals( "Testing tail no matches (F)", 0, list.size() );
+
+        list = digester.getRules().match( null, "a/beta", null, null );
+
+        assertEquals( "Testing tail no matches (G)", 0, list.size() );
+
+        // clean up
+        digester.getRules().clear();
+
+    }
+
+    @Test
+    public void testAncesterMatch()
+        throws Exception
+    {
+        // test fixed root ancester
+        digester.getRules().clear();
+
+        digester.addRule( "!a/b/*", new TestRule( "uni-a-b-star" ) );
+        digester.addRule( "a/b/*", new TestRule( "a-b-star" ) );
+        digester.addRule( "a/b/c", new TestRule( "a-b-c" ) );
+        digester.addRule( "a/b/?", new TestRule( "a-b-child" ) );
+
+        List<Rule> list = digester.getRules().match( null, "a/b/c", null, null );
+
+        assertEquals( "Simple ancester matches (1)", 2, list.size() );
+        assertEquals( "Univeral ancester mismatch (1)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+        assertEquals( "Parent precedence failure", "a-b-c", ( (TestRule) list.get( 1 ) ).getIdentifier() );
+
+        list = digester.getRules().match( null, "a/b/b", null, null );
+        assertEquals( "Simple ancester matches (2)", 2, list.size() );
+        assertEquals( "Univeral ancester mismatch (2)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+        assertEquals( "Child precedence failure", "a-b-child", ( (TestRule) list.get( 1 ) ).getIdentifier() );
+
+        list = digester.getRules().match( null, "a/b/d", null, null );
+        assertEquals( "Simple ancester matches (3)", 2, list.size() );
+        assertEquals( "Univeral ancester mismatch (3)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+        assertEquals( "Ancester mismatch (1)", "a-b-child", ( (TestRule) list.get( 1 ) ).getIdentifier() );
+
+        list = digester.getRules().match( null, "a/b/d/e/f", null, null );
+        assertEquals( "Simple ancester matches (4)", 2, list.size() );
+        assertEquals( "Univeral ancester mismatch (4)", "uni-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+        assertEquals( "Ancester mismatch (2)", "a-b-star", ( (TestRule) list.get( 1 ) ).getIdentifier() );
+
+        // test wild root ancester
+        digester.getRules().clear();
+
+        digester.addRule( "!*/a/b/*", new TestRule( "uni-star-a-b-star" ) );
+        digester.addRule( "*/b/c/*", new TestRule( "star-b-c-star" ) );
+        digester.addRule( "*/b/c/d", new TestRule( "star-b-c-d" ) );
+        digester.addRule( "a/b/c", new TestRule( "a-b-c" ) );
+
+        list = digester.getRules().match( null, "a/b/c", null, null );
+        assertEquals( "Wild ancester match (1)", 2, list.size() );
+        assertEquals( "Univeral ancester mismatch (5)", "uni-star-a-b-star",
+                      ( (TestRule) list.get( 0 ) ).getIdentifier() );
+        assertEquals( "Match missed (1)", "a-b-c", ( (TestRule) list.get( 1 ) ).getIdentifier() );
+
+        list = digester.getRules().match( null, "b/c", null, null );
+        assertEquals( "Wild ancester match (2)", 1, list.size() );
+        assertEquals( "Match missed (2)", "star-b-c-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+
+        list = digester.getRules().match( null, "a/b/c/d", null, null );
+        assertEquals( "Wild ancester match (3)", 2, list.size() );
+        assertEquals( "Match missed (3)", "uni-star-a-b-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+        assertEquals( "Match missed (4)", "star-b-c-d", ( (TestRule) list.get( 1 ) ).getIdentifier() );
+
+        list = digester.getRules().match( null, "b/b/c/e/d", null, null );
+        assertEquals( "Wild ancester match (2)", 1, list.size() );
+        assertEquals( "Match missed (5)", "star-b-c-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+    }
+
+    @Test
+    public void testLongMatch()
+    {
+
+        digester.getRules().clear();
+
+        digester.addRule( "a/b/c/d/*", new TestRule( "a-b-c-d-star" ) );
+
+        List<Rule> list = digester.getRules().match( null, "a/b/c/d/e", null, null );
+        assertEquals( "Long match (1)", 1, list.size() );
+        assertEquals( "Match missed (1)", "a-b-c-d-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+
+        list = digester.getRules().match( null, "a/b/c/d/e/f", null, null );
+        assertEquals( "Long match (2)", 1, list.size() );
+        assertEquals( "Match missed (2)", "a-b-c-d-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+
+        list = digester.getRules().match( null, "a/b/c/d/e/f/g", null, null );
+        assertEquals( "Long match (3)", 1, list.size() );
+        assertEquals( "Match missed (3)", "a-b-c-d-star", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+
+        list = digester.getRules().match( null, "a/b/c/d", null, null );
+        assertEquals( "Long match (4)", 0, list.size() );
+    }
+
+    @Test
+    public void testInstructors()
+    {
+        digester.getRules().clear();
+
+        digester.addRule( "!instructors/*", new TestRule( "instructors" ) );
+        digester.addRule( "!instructor/*", new TestRule( "instructor" ) );
+
+        final List<Rule> list = digester.getRules().match( null, "instructors", null, null );
+        assertEquals( "Only expect to match instructors", 1, list.size() );
+        assertEquals( "Instructors expected", "instructors", ( (TestRule) list.get( 0 ) ).getIdentifier() );
+
+    }
+
+    @Test
+    public void testMiddleInstructors()
+    {
+        digester.getRules().clear();
+
+        digester.addRule( "!instructors/*", new TestRule( "instructors" ) );
+
+        final List<Rule> list = digester.getRules().match( null, "/tosh/instructors/fiddlesticks", null, null );
+        assertEquals( "No matches expected", 0, list.size() );
+
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/NamedBean.java b/core/src/test/java/org/apache/commons/digester3/NamedBean.java
index db1189ae..6b0a2151 100644
--- a/core/src/test/java/org/apache/commons/digester3/NamedBean.java
+++ b/core/src/test/java/org/apache/commons/digester3/NamedBean.java
@@ -1,59 +1,57 @@
-/* $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.digester3;
-
-/**
- * Very simple test bean
- *
- * @author Robert Burrell Donkin
- */
-public class NamedBean
-{
-
-    private String name = "**UNSET**";
-
-    public NamedBean()
-    {
-    }
-
-    public NamedBean( final String name )
-    {
-    }
-
-    public String getName()
-    {
-        return name;
-    }
-
-    public void setName( final String name )
-    {
-        this.name = name;
-    }
-
-    public void test( final String name, final String ignored )
-    {
-        setName( name );
-    }
-
-    @Override
-    public String toString()
-    {
-        return "NamedBean[" + getName() + "]";
-    }
-}
+/* $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.digester3;
+
+/**
+ * Very simple test bean
+ */
+public class NamedBean
+{
+
+    private String name = "**UNSET**";
+
+    public NamedBean()
+    {
+    }
+
+    public NamedBean( final String name )
+    {
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName( final String name )
+    {
+        this.name = name;
+    }
+
+    public void test( final String name, final String ignored )
+    {
+        setName( name );
+    }
+
+    @Override
+    public String toString()
+    {
+        return "NamedBean[" + getName() + "]";
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/NodeCreateRuleTestCase.java b/core/src/test/java/org/apache/commons/digester3/NodeCreateRuleTestCase.java
index d89a045b..73e0e6ae 100644
--- a/core/src/test/java/org/apache/commons/digester3/NodeCreateRuleTestCase.java
+++ b/core/src/test/java/org/apache/commons/digester3/NodeCreateRuleTestCase.java
@@ -1,543 +1,541 @@
-/* $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.digester3;
-
-import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.apache.commons.digester3.binder.AbstractRulesModule;
-import org.apache.commons.digester3.binder.NodeCreateRuleProvider.NodeType;
-import org.junit.Test;
-import org.w3c.dom.Document;
-import org.w3c.dom.DocumentFragment;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.xml.sax.SAXException;
-
-/**
- * <p>
- * Test case for the {@code NodeCreateRule}.
- *
- * @author Christopher Lenz
- */
-public class NodeCreateRuleTestCase
-{
-
-    // ----------------------------------------------------- Instance Variables
-
-    /**
-     * Simple test xml document used in the tests.
-     */
-    protected final static String TEST_XML = "<?xml version='1.0'?><root>ROOT BODY<alpha>ALPHA BODY</alpha>"
-        + "<beta>BETA BODY</beta><gamma>GAMMA BODY</gamma></root>";
-
-    // ------------------------------------------------ Individual Test Methods
-
-    /**
-     * Tests simple element construction, using the {@link #TEST_XML} XML input data.
-     */
-    @Test
-    public void testInvalidNodeTypes()
-        throws Exception
-    {
-
-        try
-        {
-            final Rule rule = new NodeCreateRule( Node.ATTRIBUTE_NODE );
-            fail( "IllegalArgumentException expected for type ATTRIBUTE_NODE" );
-            assertNotNull( rule ); // just to prevent compiler warning on unused var
-        }
-        catch ( final IllegalArgumentException iae )
-        {
-            // expected
-        }
-        try
-        {
-            final Rule rule = new NodeCreateRule( Node.CDATA_SECTION_NODE );
-            fail( "IllegalArgumentException expected for type " + "CDATA_SECTION_NODE" );
-            assertNotNull( rule ); // just to prevent compiler warning on unused var
-        }
-        catch ( final IllegalArgumentException iae )
-        {
-            // expected
-        }
-        try
-        {
-            final Rule rule = new NodeCreateRule( Node.COMMENT_NODE );
-            fail( "IllegalArgumentException expected for type COMMENT_NODE" );
-            assertNotNull( rule ); // just to prevent compiler warning on unused var
-        }
-        catch ( final IllegalArgumentException iae )
-        {
-            // expected
-        }
-        try
-        {
-            final Rule rule = new NodeCreateRule( Node.DOCUMENT_NODE );
-            fail( "IllegalArgumentException expected for type DOCUMENT_NODE" );
-            assertNotNull( rule ); // just to prevent compiler warning on unused var
-        }
-        catch ( final IllegalArgumentException iae )
-        {
-            // expected
-        }
-        try
-        {
-            final Rule rule = new NodeCreateRule( Node.DOCUMENT_TYPE_NODE );
-            fail( "IllegalArgumentException expected for type " + "DOCUMENT_TYPE_NODE" );
-            assertNotNull( rule ); // just to prevent compiler warning on unused var
-        }
-        catch ( final IllegalArgumentException iae )
-        {
-            // expected
-        }
-        try
-        {
-            final Rule rule = new NodeCreateRule( Node.ENTITY_NODE );
-            fail( "IllegalArgumentException expected for type ENTITY_NODE" );
-            assertNotNull( rule ); // just to prevent compiler warning on unused var
-        }
-        catch ( final IllegalArgumentException iae )
-        {
-            // expected
-        }
-        try
-        {
-            final Rule rule = new NodeCreateRule( Node.ENTITY_REFERENCE_NODE );
-            fail( "IllegalArgumentException expected for type " + "ENTITY_REFERENCE_NODE" );
-            assertNotNull( rule ); // just to prevent compiler warning on unused var
-        }
-        catch ( final IllegalArgumentException iae )
-        {
-            // expected
-        }
-        try
-        {
-            final Rule rule = new NodeCreateRule( Node.NOTATION_NODE );
-            fail( "IllegalArgumentException expected for type NOTATION_NODE" );
-            assertNotNull( rule ); // just to prevent compiler warning on unused var
-        }
-        catch ( final IllegalArgumentException iae )
-        {
-            // expected
-        }
-        try
-        {
-            final Rule rule = new NodeCreateRule( Node.PROCESSING_INSTRUCTION_NODE );
-            fail( "IllegalArgumentException expected for type " + "PROCESSING_INSTRUCTION_NODE" );
-            assertNotNull( rule ); // just to prevent compiler warning on unused var
-        }
-        catch ( final IllegalArgumentException iae )
-        {
-            // expected
-        }
-        try
-        {
-            final Rule rule = new NodeCreateRule( Node.TEXT_NODE );
-            fail( "IllegalArgumentException expected for type TEXT_NODE" );
-            assertNotNull( rule ); // just to prevent compiler warning on unused var
-        }
-        catch ( final IllegalArgumentException iae )
-        {
-            // expected
-        }
-
-    }
-
-    /**
-     * Tests simple element construction, using the {@link #TEST_XML} XML input data.
-     */
-    @Test
-    public void testElement()
-        throws SAXException, IOException
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "root/alpha" ).createNode();
-            }
-
-        }).newDigester();
-
-        final Element element = digester.parse( new StringReader( TEST_XML ) );
-
-        assertNotNull( element );
-        assertEquals( "alpha", element.getNodeName() );
-        assertNull( element.getLocalName() );
-        assertNull( element.getNamespaceURI() );
-        assertEquals( 1, element.getChildNodes().getLength() );
-        assertEquals( "ALPHA BODY", element.getFirstChild().getNodeValue() );
-
-    }
-
-    /**
-     * Tests simple fragment construction, using the {@link #TEST_XML} XML input data.
-     */
-    @Test
-    public void testDocumentFragment()
-        throws SAXException, IOException
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "root" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT );
-            }
-
-        }).newDigester();
-
-        final DocumentFragment fragment = digester.parse( new StringReader( TEST_XML ) );
-
-        assertNotNull( fragment );
-        assertEquals( 4, fragment.getChildNodes().getLength() );
-
-        final Node rootBody = fragment.getFirstChild();
-        assertEquals( Node.TEXT_NODE, rootBody.getNodeType() );
-        assertEquals( "ROOT BODY", rootBody.getNodeValue() );
-
-        final Node alpha = fragment.getChildNodes().item( 1 );
-        assertEquals( Node.ELEMENT_NODE, alpha.getNodeType() );
-        assertEquals( "alpha", alpha.getNodeName() );
-        assertNull( ( (Element) alpha ).getLocalName() );
-        assertNull( ( (Element) alpha ).getNamespaceURI() );
-        assertEquals( 1, alpha.getChildNodes().getLength() );
-        assertEquals( "ALPHA BODY", alpha.getFirstChild().getNodeValue() );
-
-        final Node beta = fragment.getChildNodes().item( 2 );
-        assertEquals( Node.ELEMENT_NODE, beta.getNodeType() );
-        assertEquals( "beta", beta.getNodeName() );
-        assertNull( ( (Element) beta ).getLocalName() );
-        assertNull( ( (Element) beta ).getNamespaceURI() );
-        assertEquals( 1, beta.getChildNodes().getLength() );
-        assertEquals( "BETA BODY", beta.getFirstChild().getNodeValue() );
-
-        final Node gamma = fragment.getChildNodes().item( 3 );
-        assertEquals( Node.ELEMENT_NODE, gamma.getNodeType() );
-        assertEquals( "gamma", gamma.getNodeName() );
-        assertNull( ( (Element) gamma ).getLocalName() );
-        assertNull( ( (Element) gamma ).getNamespaceURI() );
-        assertEquals( 1, gamma.getChildNodes().getLength() );
-        assertEquals( "GAMMA BODY", gamma.getFirstChild().getNodeValue() );
-
-    }
-
-    /**
-     * Tests whether control is returned to digester after fragment construction.
-     */
-    @Test
-    public void testNested()
-        throws SAXException, IOException
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "root" ).createObject().ofType( ArrayList.class );
-                forPattern( "root/a/b" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT )
-                    .then()
-                    .setRoot( "add" );
-                forPattern( "root/b" ).createObject().ofType( String.class )
-                    .then()
-                    .setRoot( "add" );
-            }
-
-        }).newDigester();
-
-        final List<?> list = digester.parse( getInputStream( "Test4.xml" ) );
-
-        assertNotNull( list );
-        assertEquals( 2, list.size() );
-
-        assertTrue( list.get( 0 ) instanceof DocumentFragment );
-        final DocumentFragment fragment = (DocumentFragment) list.get( 0 );
-
-        assertEquals( Node.ELEMENT_NODE, fragment.getFirstChild().getNodeType() );
-        final Element a = (Element) fragment.getFirstChild();
-        assertEquals( "a", a.getNodeName() );
-        assertEquals( 1, a.getAttributes().getLength() );
-        assertEquals( "THREE", a.getAttribute( "name" ) );
-
-        assertTrue( list.get( 1 ) instanceof String );
-
-    }
-
-    /**
-     * Tests whether attributes are correctly imported into the fragment, using the example in the Test1 XML file.
-     */
-    @Test
-    public void testAttributes()
-        throws SAXException, IOException
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "employee" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT );
-            }
-
-        }).newDigester();
-
-        final DocumentFragment fragment = digester.parse( getInputStream( "Test1.xml" ) );
-
-        assertNotNull( fragment );
-        assertEquals( 2, fragment.getChildNodes().getLength() );
-
-        assertEquals( Node.ELEMENT_NODE, fragment.getFirstChild().getNodeType() );
-        final Element address1 = (Element) fragment.getFirstChild();
-        assertEquals( "address", address1.getNodeName() );
-        assertEquals( 5, address1.getAttributes().getLength() );
-        assertEquals( "home", address1.getAttribute( "type" ) );
-        assertEquals( "Home Street", address1.getAttribute( "street" ) );
-        assertEquals( "Home City", address1.getAttribute( "city" ) );
-        assertEquals( "HS", address1.getAttribute( "state" ) );
-        assertEquals( "HmZip", address1.getAttribute( "zipCode" ) );
-
-        assertEquals( Node.ELEMENT_NODE, fragment.getLastChild().getNodeType() );
-        final Element address2 = (Element) fragment.getLastChild();
-        assertEquals( "address", address2.getNodeName() );
-        assertEquals( 5, address2.getAttributes().getLength() );
-        assertEquals( "office", address2.getAttribute( "type" ) );
-        assertEquals( "Office Street", address2.getAttribute( "street" ) );
-        assertEquals( "Office City", address2.getAttribute( "city" ) );
-        assertEquals( "OS", address2.getAttribute( "state" ) );
-        assertEquals( "OfZip", address2.getAttribute( "zipCode" ) );
-
-    }
-
-    /**
-     * Tests whether namespaces are handled correctly, using the example from the file Test3 XML file.
-     */
-    @Test
-    public void testNamespaces()
-        throws SAXException, IOException
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "employee" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT );
-            }
-
-        })
-        .setNamespaceAware( true )
-        .newDigester();
-
-        final DocumentFragment fragment = digester.parse( getInputStream( "Test3.xml" ) );
-
-        assertNotNull( fragment );
-        assertEquals( 2, fragment.getChildNodes().getLength() );
-
-        assertEquals( Node.ELEMENT_NODE, fragment.getFirstChild().getNodeType() );
-        final Element address1 = (Element) fragment.getFirstChild();
-        assertEquals( "address", address1.getNodeName() );
-        assertEquals( "http://commons.apache.org/digester/Bar", address1.getNamespaceURI() );
-        assertEquals( "address", address1.getLocalName() );
-        assertEquals( 5, address1.getAttributes().getLength() );
-        assertEquals( "home", address1.getAttribute( "type" ) );
-        assertEquals( "Home Street", address1.getAttribute( "street" ) );
-        assertEquals( "Home City", address1.getAttribute( "city" ) );
-        assertEquals( "HS", address1.getAttribute( "state" ) );
-        assertEquals( "HmZip", address1.getAttribute( "zipCode" ) );
-
-        assertEquals( Node.ELEMENT_NODE, fragment.getLastChild().getNodeType() );
-        final Element address2 = (Element) fragment.getLastChild();
-        assertEquals( "address", address2.getNodeName() );
-        assertEquals( "http://commons.apache.org/digester/Bar", address2.getNamespaceURI() );
-        assertEquals( "address", address2.getLocalName() );
-        assertEquals( 5, address2.getAttributes().getLength() );
-        assertEquals( "office", address2.getAttribute( "type" ) );
-        assertEquals( "Office Street", address2.getAttribute( "street" ) );
-        assertEquals( "Office City", address2.getAttribute( "city" ) );
-        assertEquals( "OS", address2.getAttribute( "state" ) );
-        assertEquals( "OfZip", address2.getAttribute( "zipCode" ) );
-
-    }
-
-    /**
-     * Tests whether namespaced attributes are handled correctly, using the example from the file Test10 XML file.
-     */
-    @Test
-    public void testNamespacedAttribute()
-        throws SAXException, IOException
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "employee" ).createNode().ofType( NodeType.ELEMENT );
-            }
-
-        })
-        .setNamespaceAware( true )
-        .newDigester();
-
-        final Element element = digester.parse( getInputStream( "Test10.xml" ) );
-
-        assertNotNull( element );
-
-        assertNotNull( element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ) );
-        assertEquals( "MyTestAttribute",
-                      element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getNodeValue() );
-        assertEquals( "test",
-                      element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getLocalName() );
-        assertEquals( "bar", element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getPrefix() );
-        assertEquals( "bar:test",
-                      element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getName() );
-
-    }
-
-    /**
-     * Tests whether non-namespaced attributes are handled correctly, using the example from the file Test11 XML file.
-     */
-    @Test
-    public void testNonNamespacedAttribute()
-        throws SAXException, IOException
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "employee" ).createNode().ofType( NodeType.ELEMENT );
-            }
-
-        })
-        .setNamespaceAware( true )
-        .newDigester();
-
-        final Element element = digester.parse( getInputStream( "Test10.xml" ) );
-
-        assertNotNull( element );
-
-        assertNotNull( element.getAttributeNode( "firstName" ) );
-        assertEquals( "First Name", element.getAttributeNode( "firstName" ).getNodeValue() );
-        assertEquals( "firstName", element.getAttributeNode( "firstName" ).getLocalName() );
-        assertEquals( null, element.getAttributeNode( "firstName" ).getPrefix() );
-        assertEquals( "firstName", element.getAttributeNode( "firstName" ).getName() );
-
-    }
-
-    /**
-     * Tests whether the created fragment can be imported into an existing document.
-     */
-    @Test
-    public void testImport()
-        throws SAXException, ParserConfigurationException, IOException
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "root" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT );
-            }
-
-        })
-        .newDigester();
-
-        final DocumentFragment fragment = digester.parse( new StringReader( TEST_XML ) );
-
-        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-        final DocumentBuilder builder = factory.newDocumentBuilder();
-        final Document doc = builder.newDocument();
-        final Node importedFragment = doc.importNode( fragment, true );
-        doc.appendChild( doc.createElement( "root" ) );
-        doc.getFirstChild().appendChild( importedFragment );
-
-    }
-
-    /**
-     * This unit test checks that text nodes are correctly created when xml entities are used. In particular, this
-     * usually causes the xml parser to make multiple invocations of the characters(..) sax callback, rather than just
-     * one.
-     */
-    @Test
-    public void testEntityText()
-        throws Exception
-    {
-        final String TEST_XML2 = "<?xml version='1.0'?><root><alpha>&#65; &#65;</alpha></root>";
-
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "root/alpha" ).createNode();
-            }
-
-        })
-        .newDigester();
-
-        final Element element = digester.parse( new StringReader( TEST_XML2 ) );
-
-        assertNotNull( element );
-        assertEquals( "alpha", element.getNodeName() );
-        assertNull( element.getLocalName() );
-        assertNull( element.getNamespaceURI() );
-        assertEquals( 1, element.getChildNodes().getLength() );
-        assertEquals( "A A", element.getFirstChild().getNodeValue() );
-    }
-
-    // ------------------------------------------------ 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
-     * @throws IOException if an input/output error occurs
-     */
-    protected InputStream getInputStream( final String name )
-        throws IOException
-    {
-
-        return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) );
-
-    }
-
-}
+/* $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.digester3;
+
+import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.commons.digester3.binder.AbstractRulesModule;
+import org.apache.commons.digester3.binder.NodeCreateRuleProvider.NodeType;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+/**
+ * <p>
+ * Test case for the {@code NodeCreateRule}.
+ */
+public class NodeCreateRuleTestCase
+{
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Simple test xml document used in the tests.
+     */
+    protected final static String TEST_XML = "<?xml version='1.0'?><root>ROOT BODY<alpha>ALPHA BODY</alpha>"
+        + "<beta>BETA BODY</beta><gamma>GAMMA BODY</gamma></root>";
+
+    // ------------------------------------------------ Individual Test Methods
+
+    /**
+     * Tests simple element construction, using the {@link #TEST_XML} XML input data.
+     */
+    @Test
+    public void testInvalidNodeTypes()
+        throws Exception
+    {
+
+        try
+        {
+            final Rule rule = new NodeCreateRule( Node.ATTRIBUTE_NODE );
+            fail( "IllegalArgumentException expected for type ATTRIBUTE_NODE" );
+            assertNotNull( rule ); // just to prevent compiler warning on unused var
+        }
+        catch ( final IllegalArgumentException iae )
+        {
+            // expected
+        }
+        try
+        {
+            final Rule rule = new NodeCreateRule( Node.CDATA_SECTION_NODE );
+            fail( "IllegalArgumentException expected for type " + "CDATA_SECTION_NODE" );
+            assertNotNull( rule ); // just to prevent compiler warning on unused var
+        }
+        catch ( final IllegalArgumentException iae )
+        {
+            // expected
+        }
+        try
+        {
+            final Rule rule = new NodeCreateRule( Node.COMMENT_NODE );
+            fail( "IllegalArgumentException expected for type COMMENT_NODE" );
+            assertNotNull( rule ); // just to prevent compiler warning on unused var
+        }
+        catch ( final IllegalArgumentException iae )
+        {
+            // expected
+        }
+        try
+        {
+            final Rule rule = new NodeCreateRule( Node.DOCUMENT_NODE );
+            fail( "IllegalArgumentException expected for type DOCUMENT_NODE" );
+            assertNotNull( rule ); // just to prevent compiler warning on unused var
+        }
+        catch ( final IllegalArgumentException iae )
+        {
+            // expected
+        }
+        try
+        {
+            final Rule rule = new NodeCreateRule( Node.DOCUMENT_TYPE_NODE );
+            fail( "IllegalArgumentException expected for type " + "DOCUMENT_TYPE_NODE" );
+            assertNotNull( rule ); // just to prevent compiler warning on unused var
+        }
+        catch ( final IllegalArgumentException iae )
+        {
+            // expected
+        }
+        try
+        {
+            final Rule rule = new NodeCreateRule( Node.ENTITY_NODE );
+            fail( "IllegalArgumentException expected for type ENTITY_NODE" );
+            assertNotNull( rule ); // just to prevent compiler warning on unused var
+        }
+        catch ( final IllegalArgumentException iae )
+        {
+            // expected
+        }
+        try
+        {
+            final Rule rule = new NodeCreateRule( Node.ENTITY_REFERENCE_NODE );
+            fail( "IllegalArgumentException expected for type " + "ENTITY_REFERENCE_NODE" );
+            assertNotNull( rule ); // just to prevent compiler warning on unused var
+        }
+        catch ( final IllegalArgumentException iae )
+        {
+            // expected
+        }
+        try
+        {
+            final Rule rule = new NodeCreateRule( Node.NOTATION_NODE );
+            fail( "IllegalArgumentException expected for type NOTATION_NODE" );
+            assertNotNull( rule ); // just to prevent compiler warning on unused var
+        }
+        catch ( final IllegalArgumentException iae )
+        {
+            // expected
+        }
+        try
+        {
+            final Rule rule = new NodeCreateRule( Node.PROCESSING_INSTRUCTION_NODE );
+            fail( "IllegalArgumentException expected for type " + "PROCESSING_INSTRUCTION_NODE" );
+            assertNotNull( rule ); // just to prevent compiler warning on unused var
+        }
+        catch ( final IllegalArgumentException iae )
+        {
+            // expected
+        }
+        try
+        {
+            final Rule rule = new NodeCreateRule( Node.TEXT_NODE );
+            fail( "IllegalArgumentException expected for type TEXT_NODE" );
+            assertNotNull( rule ); // just to prevent compiler warning on unused var
+        }
+        catch ( final IllegalArgumentException iae )
+        {
+            // expected
+        }
+
+    }
+
+    /**
+     * Tests simple element construction, using the {@link #TEST_XML} XML input data.
+     */
+    @Test
+    public void testElement()
+        throws SAXException, IOException
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "root/alpha" ).createNode();
+            }
+
+        }).newDigester();
+
+        final Element element = digester.parse( new StringReader( TEST_XML ) );
+
+        assertNotNull( element );
+        assertEquals( "alpha", element.getNodeName() );
+        assertNull( element.getLocalName() );
+        assertNull( element.getNamespaceURI() );
+        assertEquals( 1, element.getChildNodes().getLength() );
+        assertEquals( "ALPHA BODY", element.getFirstChild().getNodeValue() );
+
+    }
+
+    /**
+     * Tests simple fragment construction, using the {@link #TEST_XML} XML input data.
+     */
+    @Test
+    public void testDocumentFragment()
+        throws SAXException, IOException
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "root" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT );
+            }
+
+        }).newDigester();
+
+        final DocumentFragment fragment = digester.parse( new StringReader( TEST_XML ) );
+
+        assertNotNull( fragment );
+        assertEquals( 4, fragment.getChildNodes().getLength() );
+
+        final Node rootBody = fragment.getFirstChild();
+        assertEquals( Node.TEXT_NODE, rootBody.getNodeType() );
+        assertEquals( "ROOT BODY", rootBody.getNodeValue() );
+
+        final Node alpha = fragment.getChildNodes().item( 1 );
+        assertEquals( Node.ELEMENT_NODE, alpha.getNodeType() );
+        assertEquals( "alpha", alpha.getNodeName() );
+        assertNull( ( (Element) alpha ).getLocalName() );
+        assertNull( ( (Element) alpha ).getNamespaceURI() );
+        assertEquals( 1, alpha.getChildNodes().getLength() );
+        assertEquals( "ALPHA BODY", alpha.getFirstChild().getNodeValue() );
+
+        final Node beta = fragment.getChildNodes().item( 2 );
+        assertEquals( Node.ELEMENT_NODE, beta.getNodeType() );
+        assertEquals( "beta", beta.getNodeName() );
+        assertNull( ( (Element) beta ).getLocalName() );
+        assertNull( ( (Element) beta ).getNamespaceURI() );
+        assertEquals( 1, beta.getChildNodes().getLength() );
+        assertEquals( "BETA BODY", beta.getFirstChild().getNodeValue() );
+
+        final Node gamma = fragment.getChildNodes().item( 3 );
+        assertEquals( Node.ELEMENT_NODE, gamma.getNodeType() );
+        assertEquals( "gamma", gamma.getNodeName() );
+        assertNull( ( (Element) gamma ).getLocalName() );
+        assertNull( ( (Element) gamma ).getNamespaceURI() );
+        assertEquals( 1, gamma.getChildNodes().getLength() );
+        assertEquals( "GAMMA BODY", gamma.getFirstChild().getNodeValue() );
+
+    }
+
+    /**
+     * Tests whether control is returned to digester after fragment construction.
+     */
+    @Test
+    public void testNested()
+        throws SAXException, IOException
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "root" ).createObject().ofType( ArrayList.class );
+                forPattern( "root/a/b" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT )
+                    .then()
+                    .setRoot( "add" );
+                forPattern( "root/b" ).createObject().ofType( String.class )
+                    .then()
+                    .setRoot( "add" );
+            }
+
+        }).newDigester();
+
+        final List<?> list = digester.parse( getInputStream( "Test4.xml" ) );
+
+        assertNotNull( list );
+        assertEquals( 2, list.size() );
+
+        assertTrue( list.get( 0 ) instanceof DocumentFragment );
+        final DocumentFragment fragment = (DocumentFragment) list.get( 0 );
+
+        assertEquals( Node.ELEMENT_NODE, fragment.getFirstChild().getNodeType() );
+        final Element a = (Element) fragment.getFirstChild();
+        assertEquals( "a", a.getNodeName() );
+        assertEquals( 1, a.getAttributes().getLength() );
+        assertEquals( "THREE", a.getAttribute( "name" ) );
+
+        assertTrue( list.get( 1 ) instanceof String );
+
+    }
+
+    /**
+     * Tests whether attributes are correctly imported into the fragment, using the example in the Test1 XML file.
+     */
+    @Test
+    public void testAttributes()
+        throws SAXException, IOException
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "employee" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT );
+            }
+
+        }).newDigester();
+
+        final DocumentFragment fragment = digester.parse( getInputStream( "Test1.xml" ) );
+
+        assertNotNull( fragment );
+        assertEquals( 2, fragment.getChildNodes().getLength() );
+
+        assertEquals( Node.ELEMENT_NODE, fragment.getFirstChild().getNodeType() );
+        final Element address1 = (Element) fragment.getFirstChild();
+        assertEquals( "address", address1.getNodeName() );
+        assertEquals( 5, address1.getAttributes().getLength() );
+        assertEquals( "home", address1.getAttribute( "type" ) );
+        assertEquals( "Home Street", address1.getAttribute( "street" ) );
+        assertEquals( "Home City", address1.getAttribute( "city" ) );
+        assertEquals( "HS", address1.getAttribute( "state" ) );
+        assertEquals( "HmZip", address1.getAttribute( "zipCode" ) );
+
+        assertEquals( Node.ELEMENT_NODE, fragment.getLastChild().getNodeType() );
+        final Element address2 = (Element) fragment.getLastChild();
+        assertEquals( "address", address2.getNodeName() );
+        assertEquals( 5, address2.getAttributes().getLength() );
+        assertEquals( "office", address2.getAttribute( "type" ) );
+        assertEquals( "Office Street", address2.getAttribute( "street" ) );
+        assertEquals( "Office City", address2.getAttribute( "city" ) );
+        assertEquals( "OS", address2.getAttribute( "state" ) );
+        assertEquals( "OfZip", address2.getAttribute( "zipCode" ) );
+
+    }
+
+    /**
+     * Tests whether namespaces are handled correctly, using the example from the file Test3 XML file.
+     */
+    @Test
+    public void testNamespaces()
+        throws SAXException, IOException
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "employee" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT );
+            }
+
+        })
+        .setNamespaceAware( true )
+        .newDigester();
+
+        final DocumentFragment fragment = digester.parse( getInputStream( "Test3.xml" ) );
+
+        assertNotNull( fragment );
+        assertEquals( 2, fragment.getChildNodes().getLength() );
+
+        assertEquals( Node.ELEMENT_NODE, fragment.getFirstChild().getNodeType() );
+        final Element address1 = (Element) fragment.getFirstChild();
+        assertEquals( "address", address1.getNodeName() );
+        assertEquals( "http://commons.apache.org/digester/Bar", address1.getNamespaceURI() );
+        assertEquals( "address", address1.getLocalName() );
+        assertEquals( 5, address1.getAttributes().getLength() );
+        assertEquals( "home", address1.getAttribute( "type" ) );
+        assertEquals( "Home Street", address1.getAttribute( "street" ) );
+        assertEquals( "Home City", address1.getAttribute( "city" ) );
+        assertEquals( "HS", address1.getAttribute( "state" ) );
+        assertEquals( "HmZip", address1.getAttribute( "zipCode" ) );
+
+        assertEquals( Node.ELEMENT_NODE, fragment.getLastChild().getNodeType() );
+        final Element address2 = (Element) fragment.getLastChild();
+        assertEquals( "address", address2.getNodeName() );
+        assertEquals( "http://commons.apache.org/digester/Bar", address2.getNamespaceURI() );
+        assertEquals( "address", address2.getLocalName() );
+        assertEquals( 5, address2.getAttributes().getLength() );
+        assertEquals( "office", address2.getAttribute( "type" ) );
+        assertEquals( "Office Street", address2.getAttribute( "street" ) );
+        assertEquals( "Office City", address2.getAttribute( "city" ) );
+        assertEquals( "OS", address2.getAttribute( "state" ) );
+        assertEquals( "OfZip", address2.getAttribute( "zipCode" ) );
+
+    }
+
+    /**
+     * Tests whether namespaced attributes are handled correctly, using the example from the file Test10 XML file.
+     */
+    @Test
+    public void testNamespacedAttribute()
+        throws SAXException, IOException
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "employee" ).createNode().ofType( NodeType.ELEMENT );
+            }
+
+        })
+        .setNamespaceAware( true )
+        .newDigester();
+
+        final Element element = digester.parse( getInputStream( "Test10.xml" ) );
+
+        assertNotNull( element );
+
+        assertNotNull( element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ) );
+        assertEquals( "MyTestAttribute",
+                      element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getNodeValue() );
+        assertEquals( "test",
+                      element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getLocalName() );
+        assertEquals( "bar", element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getPrefix() );
+        assertEquals( "bar:test",
+                      element.getAttributeNodeNS( "http://commons.apache.org/digester/Bar", "test" ).getName() );
+
+    }
+
+    /**
+     * Tests whether non-namespaced attributes are handled correctly, using the example from the file Test11 XML file.
+     */
+    @Test
+    public void testNonNamespacedAttribute()
+        throws SAXException, IOException
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "employee" ).createNode().ofType( NodeType.ELEMENT );
+            }
+
+        })
+        .setNamespaceAware( true )
+        .newDigester();
+
+        final Element element = digester.parse( getInputStream( "Test10.xml" ) );
+
+        assertNotNull( element );
+
+        assertNotNull( element.getAttributeNode( "firstName" ) );
+        assertEquals( "First Name", element.getAttributeNode( "firstName" ).getNodeValue() );
+        assertEquals( "firstName", element.getAttributeNode( "firstName" ).getLocalName() );
+        assertEquals( null, element.getAttributeNode( "firstName" ).getPrefix() );
+        assertEquals( "firstName", element.getAttributeNode( "firstName" ).getName() );
+
+    }
+
+    /**
+     * Tests whether the created fragment can be imported into an existing document.
+     */
+    @Test
+    public void testImport()
+        throws SAXException, ParserConfigurationException, IOException
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "root" ).createNode().ofType( NodeType.DOCUMENT_FRAGMENT );
+            }
+
+        })
+        .newDigester();
+
+        final DocumentFragment fragment = digester.parse( new StringReader( TEST_XML ) );
+
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        final DocumentBuilder builder = factory.newDocumentBuilder();
+        final Document doc = builder.newDocument();
+        final Node importedFragment = doc.importNode( fragment, true );
+        doc.appendChild( doc.createElement( "root" ) );
+        doc.getFirstChild().appendChild( importedFragment );
+
+    }
+
+    /**
+     * This unit test checks that text nodes are correctly created when xml entities are used. In particular, this
+     * usually causes the xml parser to make multiple invocations of the characters(..) sax callback, rather than just
+     * one.
+     */
+    @Test
+    public void testEntityText()
+        throws Exception
+    {
+        final String TEST_XML2 = "<?xml version='1.0'?><root><alpha>&#65; &#65;</alpha></root>";
+
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "root/alpha" ).createNode();
+            }
+
+        })
+        .newDigester();
+
+        final Element element = digester.parse( new StringReader( TEST_XML2 ) );
+
+        assertNotNull( element );
+        assertEquals( "alpha", element.getNodeName() );
+        assertNull( element.getLocalName() );
+        assertNull( element.getNamespaceURI() );
+        assertEquals( 1, element.getChildNodes().getLength() );
+        assertEquals( "A A", element.getFirstChild().getNodeValue() );
+    }
+
+    // ------------------------------------------------ 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
+     * @throws IOException if an input/output error occurs
+     */
+    protected InputStream getInputStream( final String name )
+        throws IOException
+    {
+
+        return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) );
+
+    }
+
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/ObjectCreationFactoryTestImpl.java b/core/src/test/java/org/apache/commons/digester3/ObjectCreationFactoryTestImpl.java
index f2809576..eb14f182 100644
--- a/core/src/test/java/org/apache/commons/digester3/ObjectCreationFactoryTestImpl.java
+++ b/core/src/test/java/org/apache/commons/digester3/ObjectCreationFactoryTestImpl.java
@@ -1,44 +1,42 @@
-/* $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.digester3;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.helpers.AttributesImpl;
-
-/**
- * Object creation factory used for testing.
- *
- * @author Robert Burrell Donkin
- */
-
-public class ObjectCreationFactoryTestImpl
-    extends AbstractObjectCreationFactory<Object>
-{
-    public boolean called = false;
-
-    public Attributes attributes;
-
-    @Override
-    public Object createObject( final Attributes attributes )
-    {
-        this.attributes = new AttributesImpl( attributes );
-        called = true;
-        return this;
-    }
-}
+/* $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.digester3;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Object creation factory used for testing.
+ */
+
+public class ObjectCreationFactoryTestImpl
+    extends AbstractObjectCreationFactory<Object>
+{
+    public boolean called = false;
+
+    public Attributes attributes;
+
+    @Override
+    public Object createObject( final Attributes attributes )
+    {
+        this.attributes = new AttributesImpl( attributes );
+        called = true;
+        return this;
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/ObjectParamRuleTestCase.java b/core/src/test/java/org/apache/commons/digester3/ObjectParamRuleTestCase.java
index d7074319..2080fd86 100644
--- a/core/src/test/java/org/apache/commons/digester3/ObjectParamRuleTestCase.java
+++ b/core/src/test/java/org/apache/commons/digester3/ObjectParamRuleTestCase.java
@@ -1,93 +1,91 @@
-/* $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.digester3;
-
-import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.ArrayList;
-
-import org.apache.commons.digester3.binder.AbstractRulesModule;
-import org.junit.Test;
-import org.xml.sax.SAXException;
-
-/**
- * <p>
- * Tests for the {@code ObjectParamRuleTestCase}
- *
- * @author Mark Huisman
- */
-public class ObjectParamRuleTestCase
-{
-
-    // ------------------------------------------------ Individual Test Methods
-
-    private final StringBuilder sb =
-        new StringBuilder().append( "<arraylist><A/><B/><C/><D desc=\"the fourth\"/><E/></arraylist>" );
-
-    /**
-     * Test method calls with the ObjectParamRule rule. It should be possible to pass any subclass of Object as a
-     * parameter, provided that either the element or the element + attribute has been matched.
-     */
-    @Test
-    public void testBasic()
-        throws SAXException, IOException
-    {
-        final Digester digester = newLoader( new AbstractRulesModule()
-        {
-
-            @Override
-            protected void configure()
-            {
-                forPattern( "arraylist" ).createObject().ofType( ArrayList.class );
-                forPattern( "arraylist/A" ).callMethod( "add" ).withParamCount( 1 )
-                    .then()
-                    .objectParam( new Integer( -9 ) );
-                forPattern( "arraylist/B" ).callMethod( "add" ).withParamCount( 1 )
-                    .then()
-                    .objectParam( new Float( 3.14159 ) );
-                forPattern( "arraylist/C" ).callMethod( "add" ).withParamCount( 1 )
-                    .then()
-                    .objectParam( new Long( 999999999 ) );
-                forPattern( "arraylist/D" ).callMethod( "add" ).withParamCount( 1 )
-                    .then()
-                    .objectParam( "foobarbazbing" ).matchingAttribute( "desc" );
-                forPattern( "arraylist/E" ).callMethod( "add" ).withParamCount( 1 )
-                    .then()
-                    .objectParam( "ignore" ).matchingAttribute( "nonexistentattribute" );
-            }
-
-        }).newDigester();
-
-        // Parse it and obtain the ArrayList
-        final ArrayList<?> al = digester.parse( new StringReader( sb.toString() ) );
-        assertNotNull( al );
-        assertEquals( al.size(), 4 );
-        assertTrue( al.contains( new Integer( -9 ) ) );
-        assertTrue( al.contains( new Float( 3.14159 ) ) );
-        assertTrue( al.contains( new Long( 999999999 ) ) );
-        assertTrue( al.contains( "foobarbazbing" ) );
-        assertTrue( !( al.contains( "ignore" ) ) );
-    }
-
-}
+/* $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.digester3;
+
+import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+
+import org.apache.commons.digester3.binder.AbstractRulesModule;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+/**
+ * <p>
+ * Tests for the {@code ObjectParamRuleTestCase}
+ */
+public class ObjectParamRuleTestCase
+{
+
+    // ------------------------------------------------ Individual Test Methods
+
+    private final StringBuilder sb =
+        new StringBuilder().append( "<arraylist><A/><B/><C/><D desc=\"the fourth\"/><E/></arraylist>" );
+
+    /**
+     * Test method calls with the ObjectParamRule rule. It should be possible to pass any subclass of Object as a
+     * parameter, provided that either the element or the element + attribute has been matched.
+     */
+    @Test
+    public void testBasic()
+        throws SAXException, IOException
+    {
+        final Digester digester = newLoader( new AbstractRulesModule()
+        {
+
+            @Override
+            protected void configure()
+            {
+                forPattern( "arraylist" ).createObject().ofType( ArrayList.class );
+                forPattern( "arraylist/A" ).callMethod( "add" ).withParamCount( 1 )
+                    .then()
+                    .objectParam( new Integer( -9 ) );
+                forPattern( "arraylist/B" ).callMethod( "add" ).withParamCount( 1 )
+                    .then()
+                    .objectParam( new Float( 3.14159 ) );
+                forPattern( "arraylist/C" ).callMethod( "add" ).withParamCount( 1 )
+                    .then()
+                    .objectParam( new Long( 999999999 ) );
+                forPattern( "arraylist/D" ).callMethod( "add" ).withParamCount( 1 )
+                    .then()
+                    .objectParam( "foobarbazbing" ).matchingAttribute( "desc" );
+                forPattern( "arraylist/E" ).callMethod( "add" ).withParamCount( 1 )
+                    .then()
+                    .objectParam( "ignore" ).matchingAttribute( "nonexistentattribute" );
+            }
+
+        }).newDigester();
+
+        // Parse it and obtain the ArrayList
+        final ArrayList<?> al = digester.parse( new StringReader( sb.toString() ) );
+        assertNotNull( al );
+        assertEquals( al.size(), 4 );
+        assertTrue( al.contains( new Integer( -9 ) ) );
+        assertTrue( al.contains( new Float( 3.14159 ) ) );
+        assertTrue( al.contains( new Long( 999999999 ) ) );
+        assertTrue( al.contains( "foobarbazbing" ) );
+        assertTrue( !( al.contains( "ignore" ) ) );
+    }
+
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/OtherTestObjectCreationFactory.java b/core/src/test/java/org/apache/commons/digester3/OtherTestObjectCreationFactory.java
index a115caac..87de6f58 100644
--- a/core/src/test/java/org/apache/commons/digester3/OtherTestObjectCreationFactory.java
+++ b/core/src/test/java/org/apache/commons/digester3/OtherTestObjectCreationFactory.java
@@ -1,30 +1,28 @@
-/* $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.digester3;
-
-/**
- * Another Object creation factory used for testing.
- *
- * @author Robert Burrell Donkin
- */
-
-public class OtherTestObjectCreationFactory
-    extends ObjectCreationFactoryTestImpl
-{
-}
+/* $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.digester3;
+
+/**
+ * Another Object creation factory used for testing.
+ */
+
+public class OtherTestObjectCreationFactory
+    extends ObjectCreationFactoryTestImpl
+{
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/ParamBean.java b/core/src/test/java/org/apache/commons/digester3/ParamBean.java
index 2206d63a..bdcfd701 100644
--- a/core/src/test/java/org/apache/commons/digester3/ParamBean.java
+++ b/core/src/test/java/org/apache/commons/digester3/ParamBean.java
@@ -1,65 +1,63 @@
-/* $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.digester3;
-
-/**
- * This bean is used to replicate a reasonably complex use case whose behavior has changed from Digester 1.3 to 1.4.
- *
- * @author robert burrell donkin
- */
-public class ParamBean
-{
-
-    private boolean cool;
-
-    private String that;
-
-    private String _this;
-
-    public ParamBean()
-    {
-    }
-
-    public boolean isCool()
-    {
-        return cool;
-    }
-
-    public void setCool( final boolean cool )
-    {
-        this.cool = cool;
-    }
-
-    public String getThis()
-    {
-        return _this;
-    }
-
-    public String getThat()
-    {
-        return that;
-    }
-
-    public String setThisAndThat( final String _this, final String that )
-    {
-        this._this = _this;
-        this.that = that;
-        return "The Other";
-    }
-}
+/* $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.digester3;
+
+/**
+ * This bean is used to replicate a reasonably complex use case whose behavior has changed from Digester 1.3 to 1.4.
+ */
+public class ParamBean
+{
+
+    private boolean cool;
+
+    private String that;
+
+    private String _this;
+
+    public ParamBean()
+    {
+    }
+
+    public boolean isCool()
+    {
+        return cool;
+    }
+
+    public void setCool( final boolean cool )
+    {
+        this.cool = cool;
+    }
+
+    public String getThis()
+    {
+        return _this;
+    }
+
+    public String getThat()
+    {
+        return that;
+    }
+
+    public String setThisAndThat( final String _this, final String that )
+    {
+        this._this = _this;
+        this.that = that;
+        return "The Other";
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/PrimitiveBean.java b/core/src/test/java/org/apache/commons/digester3/PrimitiveBean.java
index 47926c43..cfb9ed89 100644
--- a/core/src/test/java/org/apache/commons/digester3/PrimitiveBean.java
+++ b/core/src/test/java/org/apache/commons/digester3/PrimitiveBean.java
@@ -1,57 +1,55 @@
-/* $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.digester3;
-
-/**
- * A simple bean with primitive properties. At the moment only need a boolean property. Feel free to add others later.
- *
- * @author robert burrell donkin
- */
-public class PrimitiveBean
-{
-
-    private boolean booleanValue;
-
-    private boolean setBooleanCalled;
-
-    public PrimitiveBean()
-    {
-    }
-
-    public boolean getBoolean()
-    {
-        return booleanValue;
-    }
-
-    public boolean getSetBooleanCalled()
-    {
-        return setBooleanCalled;
-    }
-
-    public void setBoolean( final boolean booleanValue )
-    {
-        this.booleanValue = booleanValue;
-        setBooleanCalled = true;
-    }
-
-    public void testSetBoolean( final String ignored, final boolean booleanValue )
-    {
-        setBoolean( booleanValue );
-    }
-}
+/* $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.digester3;
+
+/**
+ * A simple bean with primitive properties. At the moment only need a boolean property. Feel free to add others later.
+ */
+public class PrimitiveBean
+{
+
+    private boolean booleanValue;
+
+    private boolean setBooleanCalled;
+
+    public PrimitiveBean()
+    {
+    }
+
+    public boolean getBoolean()
+    {
+        return booleanValue;
+    }
+
+    public boolean getSetBooleanCalled()
+    {
+        return setBooleanCalled;
+    }
+
+    public void setBoolean( final boolean booleanValue )
+    {
+        this.booleanValue = booleanValue;
+        setBooleanCalled = true;
+    }
+
+    public void testSetBoolean( final String ignored, final boolean booleanValue )
+    {
+        setBoolean( booleanValue );
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/RegexRulesTestCase.java b/core/src/test/java/org/apache/commons/digester3/RegexRulesTestCase.java
index 9ef1461a..50e601d7 100644
--- a/core/src/test/java/org/apache/commons/digester3/RegexRulesTestCase.java
+++ b/core/src/test/java/org/apache/commons/digester3/RegexRulesTestCase.java
@@ -1,205 +1,203 @@
-/* $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.digester3;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.List;
-
-import org.junit.Test;
-
-/**
- * Test case for RegexRules
- *
- * @author Robert Burrell Donkin
- */
-public class RegexRulesTestCase
-{
-
-    /** Test regex that matches everything */
-    @Test
-    public void testMatchAll()
-    {
-        // set up which should match every rule
-        final RegexRules rules = new RegexRules( new RegexMatcher()
-        {
-            @Override
-            public boolean match( final String pathPattern, final String rulePattern )
-            {
-                return true;
-            }
-        } );
-
-        rules.add( "/a/b/b", new TestRule( "alpha" ) );
-        rules.add( "/a/d", new TestRule( "beta" ) );
-        rules.add( "/b", new TestRule( "gamma" ) );
-
-        // now test a few patterns
-        // check that all are return in the order which they were added
-        List<Rule> matches = rules.match( "", "x/g/e", null, null );
-        assertEquals( "Wrong number of rules returned (1)", 3, matches.size() );
-        assertEquals( "Rule Out Of Order (1)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() );
-        assertEquals( "Rule Out Of Order (2)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() );
-        assertEquals( "Rule Out Of Order (3)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() );
-
-        matches = rules.match( "", "/a", null, null );
-        assertEquals( "Wrong number of rules returned (2)", 3, matches.size() );
-        assertEquals( "Rule Out Of Order (4)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() );
-        assertEquals( "Rule Out Of Order (5)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() );
-        assertEquals( "Rule Out Of Order (6)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() );
-    }
-
-    /** Test regex matcher that matches nothing */
-    @Test
-    public void testMatchNothing()
-    {
-        // set up which should match every rule
-        final RegexRules rules = new RegexRules( new RegexMatcher()
-        {
-            @Override
-            public boolean match( final String pathPattern, final String rulePattern )
-            {
-                return false;
-            }
-        } );
-
-        rules.add( "/b/c/f", new TestRule( "alpha" ) );
-        rules.add( "/c/f", new TestRule( "beta" ) );
-        rules.add( "/b", new TestRule( "gamma" ) );
-
-        // now test a few patterns
-        // check that all are return in the order which they were added
-        List<Rule> matches = rules.match( "", "/b/c", null, null );
-        assertEquals( "Wrong number of rules returned (1)", 0, matches.size() );
-
-        matches = rules.match( "", "/b/c/f", null, null );
-        assertEquals( "Wrong number of rules returned (2)", 0, matches.size() );
-    }
-
-    /** Test a mixed regex - in other words, one that sometimes returns true and sometimes false */
-    @Test
-    public void testMatchMixed()
-    {
-        // set up which should match every rule
-        final RegexRules rules = new RegexRules( new RegexMatcher()
-        {
-            @Override
-            public boolean match( final String pathPattern, final String rulePattern )
-            {
-                return ( rulePattern.equals( "/match/me" ) );
-            }
-        } );
-
-        rules.add( "/match", new TestRule( "alpha" ) );
-        rules.add( "/match/me", new TestRule( "beta" ) );
-        rules.add( "/match", new TestRule( "gamma" ) );
-
-        // now test a few patterns
-        // check that all are return in the order which they were added
-        List<Rule> matches = rules.match( "", "/match", null, null );
-        assertEquals( "Wrong number of rules returned (1)", 1, matches.size() );
-        assertEquals( "Wrong Rule (1)", "beta", ( (TestRule) matches.get( 0 ) ).getIdentifier() );
-
-        matches = rules.match( "", "/a/match", null, null );
-        assertEquals( "Wrong Rule (2)", "beta", ( (TestRule) matches.get( 0 ) ).getIdentifier() );
-    }
-
-    /** Test rules and clear methods */
-    @Test
-    public void testClear()
-    {
-        // set up which should match every rule
-        final RegexRules rules = new RegexRules( new RegexMatcher()
-        {
-            @Override
-            public boolean match( final String pathPattern, final String rulePattern )
-            {
-                return true;
-            }
-        } );
-
-        rules.add( "/abba", new TestRule( "alpha" ) );
-        rules.add( "/ad/ma", new TestRule( "beta" ) );
-        rules.add( "/gamma", new TestRule( "gamma" ) );
-
-        // check that rules returns all rules in the order which they were added
-        List<Rule> matches = rules.rules();
-        assertEquals( "Wrong number of rules returned (1)", 3, matches.size() );
-        assertEquals( "Rule Out Of Order (1)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() );
-        assertEquals( "Rule Out Of Order (2)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() );
-        assertEquals( "Rule Out Of Order (3)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() );
-
-        matches = rules.match( "", "/eggs", null, null );
-        assertEquals( "Wrong number of rules returned (2)", 3, matches.size() );
-        assertEquals( "Rule Out Of Order (4)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() );
-        assertEquals( "Rule Out Of Order (5)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() );
-        assertEquals( "Rule Out Of Order (6)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() );
-
-        rules.clear();
-        matches = rules.rules();
-        assertEquals( "Wrong number of rules returned (3)", 0, matches.size() );
-
-        matches = rules.match( "", "/eggs", null, null );
-        assertEquals( "Wrong number of rules returned (4)", 0, matches.size() );
-    }
-
-    @Test
-    public void testSimpleRegexMatch()
-    {
-
-        final SimpleRegexMatcher matcher = new SimpleRegexMatcher();
-
-        // SimpleLog log = new SimpleLog("{testSimpleRegexMatch:SimpleRegexMatcher]");
-        // log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
-
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/beta/gamma' ", true,
-                      matcher.match( "/alpha/beta/gamma", "/alpha/beta/gamma" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/beta/gamma/epsilon' ", false,
-                      matcher.match( "/alpha/beta/gamma", "/alpha/beta/gamma/epsilon" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/*' ", true,
-                      matcher.match( "/alpha/beta/gamma", "/alpha/*" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/*/gamma' ", true,
-                      matcher.match( "/alpha/beta/gamma", "/alpha/*/gamma" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/*me' ", false,
-                      matcher.match( "/alpha/beta/gamma", "/alpha/*me" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '*/beta/gamma' ", true,
-                      matcher.match( "/alpha/beta/gamma", "*/beta/gamma" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '*/alpha/beta/gamma' ", true,
-                      matcher.match( "/alpha/beta/gamma", "*/alpha/beta/gamma" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '*/bet/gamma' ", false,
-                      matcher.match( "/alpha/beta/gamma", "*/bet/gamma" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to 'alph?/beta/gamma' ", true,
-                      matcher.match( "/alpha/beta/gamma", "/alph?/beta/gamma" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/?lpha/beta/gamma' ", true,
-                      matcher.match( "/alpha/beta/gamma", "/?lpha/beta/gamma" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/?beta/gamma' ", false,
-                      matcher.match( "/alpha/beta/gamma", "/alpha/?beta/gamma" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/?eta/*' ", true,
-                      matcher.match( "/alpha/beta/gamma", "/alpha/?eta/*" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/?eta/*e' ", false,
-                      matcher.match( "/alpha/beta/gamma", "/alpha/?eta/*e" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '*/?et?/?amma' ", true,
-                      matcher.match( "/alpha/beta/gamma", "*/?et?/?amma" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma/beta/epsilon/beta/gamma/epsilon' to "
-                          + " '*/beta/gamma/?p*n' ", true,
-                      matcher.match( "/alpha/beta/gamma/beta/epsilon/beta/gamma/epsilon", "*/beta/gamma/?p*n" ) );
-        assertEquals( "Simple Regex Match '/alpha/beta/gamma/beta/epsilon/beta/gamma/epsilon' to "
-            + " '*/beta/gamma/?p*no' ", false, matcher.match( "/alpha/beta/gamma", "*/beta/gamma/?p*no" ) );
-    }
-}
+/* $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.digester3;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * Test case for RegexRules
+ */
+public class RegexRulesTestCase
+{
+
+    /** Test regex that matches everything */
+    @Test
+    public void testMatchAll()
+    {
+        // set up which should match every rule
+        final RegexRules rules = new RegexRules( new RegexMatcher()
+        {
+            @Override
+            public boolean match( final String pathPattern, final String rulePattern )
+            {
+                return true;
+            }
+        } );
+
+        rules.add( "/a/b/b", new TestRule( "alpha" ) );
+        rules.add( "/a/d", new TestRule( "beta" ) );
+        rules.add( "/b", new TestRule( "gamma" ) );
+
+        // now test a few patterns
+        // check that all are return in the order which they were added
+        List<Rule> matches = rules.match( "", "x/g/e", null, null );
+        assertEquals( "Wrong number of rules returned (1)", 3, matches.size() );
+        assertEquals( "Rule Out Of Order (1)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() );
+        assertEquals( "Rule Out Of Order (2)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() );
+        assertEquals( "Rule Out Of Order (3)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() );
+
+        matches = rules.match( "", "/a", null, null );
+        assertEquals( "Wrong number of rules returned (2)", 3, matches.size() );
+        assertEquals( "Rule Out Of Order (4)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() );
+        assertEquals( "Rule Out Of Order (5)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() );
+        assertEquals( "Rule Out Of Order (6)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() );
+    }
+
+    /** Test regex matcher that matches nothing */
+    @Test
+    public void testMatchNothing()
+    {
+        // set up which should match every rule
+        final RegexRules rules = new RegexRules( new RegexMatcher()
+        {
+            @Override
+            public boolean match( final String pathPattern, final String rulePattern )
+            {
+                return false;
+            }
+        } );
+
+        rules.add( "/b/c/f", new TestRule( "alpha" ) );
+        rules.add( "/c/f", new TestRule( "beta" ) );
+        rules.add( "/b", new TestRule( "gamma" ) );
+
+        // now test a few patterns
+        // check that all are return in the order which they were added
+        List<Rule> matches = rules.match( "", "/b/c", null, null );
+        assertEquals( "Wrong number of rules returned (1)", 0, matches.size() );
+
+        matches = rules.match( "", "/b/c/f", null, null );
+        assertEquals( "Wrong number of rules returned (2)", 0, matches.size() );
+    }
+
+    /** Test a mixed regex - in other words, one that sometimes returns true and sometimes false */
+    @Test
+    public void testMatchMixed()
+    {
+        // set up which should match every rule
+        final RegexRules rules = new RegexRules( new RegexMatcher()
+        {
+            @Override
+            public boolean match( final String pathPattern, final String rulePattern )
+            {
+                return ( rulePattern.equals( "/match/me" ) );
+            }
+        } );
+
+        rules.add( "/match", new TestRule( "alpha" ) );
+        rules.add( "/match/me", new TestRule( "beta" ) );
+        rules.add( "/match", new TestRule( "gamma" ) );
+
+        // now test a few patterns
+        // check that all are return in the order which they were added
+        List<Rule> matches = rules.match( "", "/match", null, null );
+        assertEquals( "Wrong number of rules returned (1)", 1, matches.size() );
+        assertEquals( "Wrong Rule (1)", "beta", ( (TestRule) matches.get( 0 ) ).getIdentifier() );
+
+        matches = rules.match( "", "/a/match", null, null );
+        assertEquals( "Wrong Rule (2)", "beta", ( (TestRule) matches.get( 0 ) ).getIdentifier() );
+    }
+
+    /** Test rules and clear methods */
+    @Test
+    public void testClear()
+    {
+        // set up which should match every rule
+        final RegexRules rules = new RegexRules( new RegexMatcher()
+        {
+            @Override
+            public boolean match( final String pathPattern, final String rulePattern )
+            {
+                return true;
+            }
+        } );
+
+        rules.add( "/abba", new TestRule( "alpha" ) );
+        rules.add( "/ad/ma", new TestRule( "beta" ) );
+        rules.add( "/gamma", new TestRule( "gamma" ) );
+
+        // check that rules returns all rules in the order which they were added
+        List<Rule> matches = rules.rules();
+        assertEquals( "Wrong number of rules returned (1)", 3, matches.size() );
+        assertEquals( "Rule Out Of Order (1)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() );
+        assertEquals( "Rule Out Of Order (2)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() );
+        assertEquals( "Rule Out Of Order (3)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() );
+
+        matches = rules.match( "", "/eggs", null, null );
+        assertEquals( "Wrong number of rules returned (2)", 3, matches.size() );
+        assertEquals( "Rule Out Of Order (4)", "alpha", ( (TestRule) matches.get( 0 ) ).getIdentifier() );
+        assertEquals( "Rule Out Of Order (5)", "beta", ( (TestRule) matches.get( 1 ) ).getIdentifier() );
+        assertEquals( "Rule Out Of Order (6)", "gamma", ( (TestRule) matches.get( 2 ) ).getIdentifier() );
+
+        rules.clear();
+        matches = rules.rules();
+        assertEquals( "Wrong number of rules returned (3)", 0, matches.size() );
+
+        matches = rules.match( "", "/eggs", null, null );
+        assertEquals( "Wrong number of rules returned (4)", 0, matches.size() );
+    }
+
+    @Test
+    public void testSimpleRegexMatch()
+    {
+
+        final SimpleRegexMatcher matcher = new SimpleRegexMatcher();
+
+        // SimpleLog log = new SimpleLog("{testSimpleRegexMatch:SimpleRegexMatcher]");
+        // log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
+
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/beta/gamma' ", true,
+                      matcher.match( "/alpha/beta/gamma", "/alpha/beta/gamma" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/beta/gamma/epsilon' ", false,
+                      matcher.match( "/alpha/beta/gamma", "/alpha/beta/gamma/epsilon" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/*' ", true,
+                      matcher.match( "/alpha/beta/gamma", "/alpha/*" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/*/gamma' ", true,
+                      matcher.match( "/alpha/beta/gamma", "/alpha/*/gamma" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/*me' ", false,
+                      matcher.match( "/alpha/beta/gamma", "/alpha/*me" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '*/beta/gamma' ", true,
+                      matcher.match( "/alpha/beta/gamma", "*/beta/gamma" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '*/alpha/beta/gamma' ", true,
+                      matcher.match( "/alpha/beta/gamma", "*/alpha/beta/gamma" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '*/bet/gamma' ", false,
+                      matcher.match( "/alpha/beta/gamma", "*/bet/gamma" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to 'alph?/beta/gamma' ", true,
+                      matcher.match( "/alpha/beta/gamma", "/alph?/beta/gamma" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/?lpha/beta/gamma' ", true,
+                      matcher.match( "/alpha/beta/gamma", "/?lpha/beta/gamma" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/?beta/gamma' ", false,
+                      matcher.match( "/alpha/beta/gamma", "/alpha/?beta/gamma" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/?eta/*' ", true,
+                      matcher.match( "/alpha/beta/gamma", "/alpha/?eta/*" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '/alpha/?eta/*e' ", false,
+                      matcher.match( "/alpha/beta/gamma", "/alpha/?eta/*e" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma' to '*/?et?/?amma' ", true,
+                      matcher.match( "/alpha/beta/gamma", "*/?et?/?amma" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma/beta/epsilon/beta/gamma/epsilon' to "
+                          + " '*/beta/gamma/?p*n' ", true,
+                      matcher.match( "/alpha/beta/gamma/beta/epsilon/beta/gamma/epsilon", "*/beta/gamma/?p*n" ) );
+        assertEquals( "Simple Regex Match '/alpha/beta/gamma/beta/epsilon/beta/gamma/epsilon' to "
+            + " '*/beta/gamma/?p*no' ", false, matcher.match( "/alpha/beta/gamma", "*/beta/gamma/?p*no" ) );
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/RuleTestCase.java b/core/src/test/java/org/apache/commons/digester3/RuleTestCase.java
index 6a333c6e..47a5d785 100644
--- a/core/src/test/java/org/apache/commons/digester3/RuleTestCase.java
+++ b/core/src/test/java/org/apache/commons/digester3/RuleTestCase.java
@@ -1,569 +1,566 @@
-/* $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.digester3;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.xml.sax.SAXException;
-
-/**
- * <p>
- * Test Case for the Digester class. These tests perform parsing of XML documents to exercise the built-in rules.
- * </p>
- *
- * @author Craig R. McClanahan
- * @author Janek Bogucki
- */
-public class RuleTestCase
-{
-
-    // ----------------------------------------------------- Instance Variables
-
-    /**
-     * The digester instance we will be processing.
-     */
-    protected Digester digester = null;
-
-    // --------------------------------------------------- Overall Test Methods
-
-    /**
-     * Set up instance variables required by this test case.
-     */
-    @Before
-    public void setUp()
-    {
-
-        digester = new Digester();
-
-    }
-
-    /**
-     * Tear down instance variables required by this test case.
-     */
-    @After
-    public void tearDown()
-    {
-
-        digester = null;
-
-    }
-
-    // ------------------------------------------------ Individual Test Methods
-
-    /**
-     * Test object creation (and associated property setting) with nothing on the stack, which should cause an
-     * appropriate Employee object to be returned.
-     */
-    @Test
-    public void testObjectCreate1()
-        throws SAXException, IOException
-    {
-
-        // Configure the digester as required
-        digester.addObjectCreate( "employee", "org.apache.commons.digester3.Employee" );
-        digester.addSetProperties( "employee" );
-
-        // Parse our test input.
-        final Employee employee = digester.parse( getInputStream( "Test1.xml" ) );
-
-        assertNotNull( "Digester returned an object", employee );
-        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
-        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
-
-    }
-
-    /**
-     * Test object creation (and associated property setting) with nothing on the stack, which should cause an
-     * appropriate Employee object to be returned. The processing rules will process the nested Address elements as
-     * well, but will not attempt to add them to the Employee.
-     */
-    @Test
-    public void testObjectCreate2()
-        throws SAXException, IOException
-    {
-
-        // Configure the digester as required
-        digester.addObjectCreate( "employee", Employee.class );
-        digester.addSetProperties( "employee" );
-        digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" );
-        digester.addSetProperties( "employee/address" );
-
-        // Parse our test input.
-        final Employee employee = digester.parse( getInputStream( "Test1.xml" ) );
-
-        assertNotNull( "Digester returned an object", employee );
-        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
-        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
-
-    }
-
-    /**
-     * Test object creation (and associated property setting) with nothing on the stack, which should cause an
-     * appropriate Employee object to be returned. The processing rules will process the nested Address elements as
-     * well, and will add them to the owning Employee.
-     */
-    @Test
-    public void testObjectCreate3()
-        throws SAXException, IOException
-    {
-
-        // Configure the digester as required
-        digester.addObjectCreate( "employee", Employee.class );
-        digester.addSetProperties( "employee" );
-        digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" );
-        digester.addSetProperties( "employee/address" );
-        digester.addSetNext( "employee/address", "addAddress" );
-
-        // Parse our test input once
-        Object root = digester.parse( getInputStream( "Test1.xml" ) );
-
-        validateObjectCreate3( root );
-
-        // Parse the same input again
-        try
-        {
-            root = digester.parse( getInputStream( "Test1.xml" ) );
-        }
-        catch ( final Throwable t )
-        {
-            fail( "Digester threw IOException: " + t );
-        }
-        validateObjectCreate3( root );
-
-    }
-
-    /**
-     * Same as testObjectCreate1(), except use individual call method rules to set the properties of the Employee.
-     */
-    @Test
-    public void testObjectCreate4()
-        throws SAXException, IOException
-    {
-
-        // Configure the digester as required
-        digester.addObjectCreate( "employee", Employee.class );
-        digester.addCallMethod( "employee", "setFirstName", 1 );
-        digester.addCallParam( "employee", 0, "firstName" );
-        digester.addCallMethod( "employee", "setLastName", 1 );
-        digester.addCallParam( "employee", 0, "lastName" );
-
-        // Parse our test input.
-        final Employee employee = digester.parse( getInputStream( "Test1.xml" ) );
-
-        assertNotNull( "Digester returned an object", employee );
-        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
-        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
-
-    }
-
-    /**
-     * Same as testObjectCreate1(), except use individual call method rules to set the properties of the Employee. Bean
-     * data are defined using elements instead of attributes. The purpose is to test CallMethod with a paramCount=0 (ie
-     * the body of the element is the argument of the method).
-     */
-    @Test
-    public void testObjectCreate5()
-        throws SAXException, IOException
-    {
-
-        // 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.
-        final Employee employee = digester.parse( getInputStream( "Test5.xml" ) );
-
-        assertNotNull( "Digester returned an object", employee );
-        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
-        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
-
-    }
-
-    /**
-     * It should be possible to parse the same input twice, and get trees of objects that are isomorphic but not be
-     * identical object instances.
-     */
-    @Test
-    public void testRepeatedParse()
-        throws SAXException, IOException
-    {
-
-        // Configure the digester as required
-        digester.addObjectCreate( "employee", Employee.class );
-        digester.addSetProperties( "employee" );
-        digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" );
-        digester.addSetProperties( "employee/address" );
-        digester.addSetNext( "employee/address", "addAddress" );
-
-        // Parse our test input the first time
-        Object root1;
-        root1 = digester.parse( getInputStream( "Test1.xml" ) );
-
-        validateObjectCreate3( root1 );
-
-        // Parse our test input the second time
-        Object root2;
-        root2 = digester.parse( getInputStream( "Test1.xml" ) );
-
-        validateObjectCreate3( root2 );
-
-        // Make sure that it was a different root
-        assertTrue( "Different tree instances were returned", root1 != root2 );
-
-    }
-
-    /**
-     * Test object creation (and associated property setting) with nothing on the stack, which should cause an
-     * appropriate Employee object to be returned. The processing rules will process the nested Address elements as
-     * well, but will not attempt to add them to the Employee.
-     */
-    @Test
-    public void testRuleSet1()
-        throws SAXException, IOException
-    {
-
-        // Configure the digester as required
-        final RuleSet rs = new TestRuleSet();
-        digester.addRuleSet( rs );
-
-        // Parse our test input.
-        final Employee employee = digester.parse( getInputStream( "Test1.xml" ) );
-
-        assertNotNull( "Digester returned an object", employee );
-        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
-        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
-        assertNotNull( "Can retrieve home address", employee.getAddress( "home" ) );
-        assertNotNull( "Can retrieve office address", employee.getAddress( "office" ) );
-
-    }
-
-    /**
-     * Same as {@code testRuleSet1} except using a single namespace.
-     */
-    @Test
-    public void testRuleSet2()
-        throws SAXException, IOException
-    {
-
-        // Configure the digester as required
-        digester.setNamespaceAware( true );
-        final RuleSet rs = new TestRuleSet( null, "http://commons.apache.org/digester/Foo" );
-        digester.addRuleSet( rs );
-
-        // Parse our test input.
-        final Employee employee = digester.parse( getInputStream( "Test2.xml" ) );
-
-        assertNotNull( "Digester returned an object", employee );
-        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
-        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
-        assertNotNull( "Can retrieve home address", employee.getAddress( "home" ) );
-        assertNotNull( "Can retrieve office address", employee.getAddress( "office" ) );
-
-    }
-
-    /**
-     * Same as {@code testRuleSet2} except using a namespace for employee that we should recognize, and a namespace
-     * for address that we should skip.
-     */
-    @Test
-    public void testRuleSet3()
-        throws SAXException, IOException
-    {
-
-        // Configure the digester as required
-        digester.setNamespaceAware( true );
-        final RuleSet rs = new TestRuleSet( null, "http://commons.apache.org/digester/Foo" );
-        digester.addRuleSet( rs );
-
-        // Parse our test input.
-        final Employee employee = digester.parse( getInputStream( "Test3.xml" ) );
-
-        assertNotNull( "Digester returned an object", employee );
-        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
-        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
-        assertNull( "Can not retrieve home address", employee.getAddress( "home" ) );
-        assertNull( "Can not retrieve office address", employee.getAddress( "office" ) );
-
-    }
-
-    /**
-     * Test the two argument version of the SetTopRule rule. This test is based on testObjectCreate3 and should result
-     * in the same tree of objects. Instead of using the SetNextRule rule which results in a method invocation on the
-     * (top-1) (parent) object with the top object (child) as an argument, this test uses the SetTopRule rule which
-     * results in a method invocation on the top object (child) with the top-1 (parent) object as an argument. The three
-     * argument form is tested in {@code testSetTopRule2}.
-     */
-    @Test
-    public void testSetTopRule1()
-        throws SAXException, IOException
-    {
-
-        // Configure the digester as required
-        digester.addObjectCreate( "employee", "org.apache.commons.digester3.Employee" );
-        digester.addSetProperties( "employee" );
-        digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" );
-        digester.addSetProperties( "employee/address" );
-        digester.addSetTop( "employee/address", "setEmployee" );
-
-        // Parse our test input.
-        Object root;
-        root = digester.parse( getInputStream( "Test1.xml" ) );
-        validateObjectCreate3( root );
-
-    }
-
-    /**
-     * Same as {@code testSetTopRule1} except using the three argument form of the SetTopRule rule.
-     */
-    @Test
-    public void testSetTopRule2()
-        throws SAXException, IOException
-    {
-
-        // Configure the digester as required
-        digester.addObjectCreate( "employee", "org.apache.commons.digester3.Employee" );
-        digester.addSetProperties( "employee" );
-        digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" );
-        digester.addSetProperties( "employee/address" );
-        digester.addSetTop( "employee/address", "setEmployee", "org.apache.commons.digester3.Employee" );
-
-        // Parse our test input.
-        Object root;
-        root = digester.parse( getInputStream( "Test1.xml" ) );
-
-        validateObjectCreate3( root );
-
-    }
-
-    /**
-     * Test rule addition - this boils down to making sure that digester is set properly on rule addition.
-     */
-    @Test
-    public void testAddRule()
-    {
-        final Digester digester = new Digester();
-        final TestRule rule = new TestRule( "Test" );
-        digester.addRule( "/root", rule );
-
-        assertEquals( "Digester is not properly on rule addition.", digester, rule.getDigester() );
-
-    }
-
-    @Test
-    public void testSetNext()
-        throws SAXException, IOException
-    {
-        final Digester digester = new Digester();
-        digester.setRules( new ExtendedBaseRules() );
-        digester.setValidating( false );
-
-        digester.addObjectCreate( "!*/b", BetaBean.class );
-        digester.addObjectCreate( "!*/a", AlphaBean.class );
-        digester.addObjectCreate( "root", ArrayList.class );
-        digester.addSetProperties( "!*" );
-        digester.addSetNext( "!*/b/?", "setChild" );
-        digester.addSetNext( "!*/a/?", "setChild" );
-        digester.addSetNext( "!root/?", "add" );
-        final ArrayList<?> root = digester.parse( getInputStream( "Test4.xml" ) );
-
-        assertEquals( "Wrong array size", 2, root.size() );
-        final AlphaBean one = (AlphaBean) root.get( 0 );
-        assertTrue( one.getChild() instanceof BetaBean );
-        final BetaBean two = (BetaBean) one.getChild();
-        assertEquals( "Wrong name (1)", two.getName(), "TWO" );
-        assertTrue( two.getChild() instanceof AlphaBean );
-        final AlphaBean three = (AlphaBean) two.getChild();
-        assertEquals( "Wrong name (2)", three.getName(), "THREE" );
-        final BetaBean four = (BetaBean) root.get( 1 );
-        assertEquals( "Wrong name (3)", four.getName(), "FOUR" );
-        assertTrue( four.getChild() instanceof BetaBean );
-        final BetaBean five = (BetaBean) four.getChild();
-        assertEquals( "Wrong name (4)", five.getName(), "FIVE" );
-
-    }
-
-    @Test
-    public void testSetTop()
-        throws SAXException, IOException
-    {
-        final Digester digester = new Digester();
-        digester.setRules( new ExtendedBaseRules() );
-        digester.setValidating( false );
-
-        digester.addObjectCreate( "!*/b", BetaBean.class );
-        digester.addObjectCreate( "!*/a", AlphaBean.class );
-        digester.addObjectCreate( "root", ArrayList.class );
-        digester.addSetProperties( "!*" );
-        digester.addSetTop( "!*/b/?", "setParent" );
-        digester.addSetTop( "!*/a/?", "setParent" );
-        digester.addSetRoot( "!*/a", "add" );
-        digester.addSetRoot( "!*/b", "add" );
-        final ArrayList<?> root = digester.parse( getInputStream( "Test4.xml" ) );
-
-        assertEquals( "Wrong array size", 5, root.size() );
-
-        // note that the array is in popped order (rather than pushed)
-
-        Object obj = root.get( 1 );
-        assertTrue( "TWO should be a BetaBean", obj instanceof BetaBean );
-        final BetaBean two = (BetaBean) obj;
-        assertNotNull( "Two's parent should not be null", two.getParent() );
-        assertEquals( "Wrong name (1)", "TWO", two.getName() );
-        assertEquals( "Wrong name (2)", "ONE", two.getParent().getName() );
-
-        obj = root.get( 0 );
-        assertTrue( "THREE should be an AlphaBean", obj instanceof AlphaBean );
-        final AlphaBean three = (AlphaBean) obj;
-        assertNotNull( "Three's parent should not be null", three.getParent() );
-        assertEquals( "Wrong name (3)", "THREE", three.getName() );
-        assertEquals( "Wrong name (4)", "TWO", three.getParent().getName() );
-
-        obj = root.get( 3 );
-        assertTrue( "FIVE should be a BetaBean", obj instanceof BetaBean );
-        final BetaBean five = (BetaBean) obj;
-        assertNotNull( "Five's parent should not be null", five.getParent() );
-        assertEquals( "Wrong name (5)", "FIVE", five.getName() );
-        assertEquals( "Wrong name (6)", "FOUR", five.getParent().getName() );
-
-    }
-
-    /**
-     */
-    @Test
-    public void testSetCustomProperties()
-        throws SAXException, IOException
-    {
-
-        final Digester digester = new Digester();
-
-        digester.setValidating( false );
-
-        digester.addObjectCreate( "toplevel", ArrayList.class );
-        digester.addObjectCreate( "toplevel/one", Address.class );
-        digester.addSetNext( "toplevel/one", "add" );
-        digester.addObjectCreate( "toplevel/two", Address.class );
-        digester.addSetNext( "toplevel/two", "add" );
-        digester.addObjectCreate( "toplevel/three", Address.class );
-        digester.addSetNext( "toplevel/three", "add" );
-        digester.addObjectCreate( "toplevel/four", Address.class );
-        digester.addSetNext( "toplevel/four", "add" );
-        digester.addSetProperties( "toplevel/one" );
-        digester.addSetProperties( "toplevel/two", new String[] { "alt-street", "alt-city", "alt-state" },
-                                   new String[] { "street", "city", "state" } );
-        digester.addSetProperties( "toplevel/three", new String[] { "aCity", "state" }, new String[] { "city" } );
-        digester.addSetProperties( "toplevel/four", "alt-city", "city" );
-
-        final ArrayList<?> root = digester.parse( getInputStream( "Test7.xml" ) );
-
-        assertEquals( "Wrong array size", 4, root.size() );
-
-        // note that the array is in popped order (rather than pushed)
-
-        Object obj = root.get( 0 );
-        assertTrue( "(1) Should be an Address ", obj instanceof Address );
-        final Address addressOne = (Address) obj;
-        assertEquals( "(1) Street attribute", "New Street", addressOne.getStreet() );
-        assertEquals( "(1) City attribute", "Las Vegas", addressOne.getCity() );
-        assertEquals( "(1) State attribute", "Nevada", addressOne.getState() );
-
-        obj = root.get( 1 );
-        assertTrue( "(2) Should be an Address ", obj instanceof Address );
-        final Address addressTwo = (Address) obj;
-        assertEquals( "(2) Street attribute", "Old Street", addressTwo.getStreet() );
-        assertEquals( "(2) City attribute", "Portland", addressTwo.getCity() );
-        assertEquals( "(2) State attribute", "Oregon", addressTwo.getState() );
-
-        obj = root.get( 2 );
-        assertTrue( "(3) Should be an Address ", obj instanceof Address );
-        final Address addressThree = (Address) obj;
-        assertEquals( "(3) Street attribute", "4th Street", addressThree.getStreet() );
-        assertEquals( "(3) City attribute", "Dayton", addressThree.getCity() );
-        assertEquals( "(3) State attribute", "US", addressThree.getState() );
-
-        obj = root.get( 3 );
-        assertTrue( "(4) Should be an Address ", obj instanceof Address );
-        final Address addressFour = (Address) obj;
-        assertEquals( "(4) Street attribute", "6th Street", addressFour.getStreet() );
-        assertEquals( "(4) City attribute", "Cleveland", addressFour.getCity() );
-        assertEquals( "(4) State attribute", "Ohio", addressFour.getState() );
-
-    }
-
-    // ------------------------------------------------ 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
-     * @throws IOException if an input/output error occurs
-     */
-    protected InputStream getInputStream( final String name )
-        throws IOException
-    {
-
-        return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) );
-
-    }
-
-    /**
-     * Validate the assertions for ObjectCreateRule3.
-     *
-     * @param root Root object returned by {@code digester.parse()}
-     */
-    protected void validateObjectCreate3( final Object root )
-    {
-
-        // Validate the retrieved Employee
-        assertNotNull( "Digester returned an object", root );
-        assertTrue( "Digester returned an Employee", root instanceof Employee );
-        final Employee employee = (Employee) root;
-        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
-        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
-
-        // Validate the corresponding "home" Address
-        final Address home = employee.getAddress( "home" );
-        assertNotNull( "Retrieved home address", home );
-        assertEquals( "Home street", "Home Street", home.getStreet() );
-        assertEquals( "Home city", "Home City", home.getCity() );
-        assertEquals( "Home state", "HS", home.getState() );
-        assertEquals( "Home zip", "HmZip", home.getZipCode() );
-
-        // Validate the corresponding "office" Address
-        final Address office = employee.getAddress( "office" );
-        assertNotNull( "Retrieved office address", office );
-        assertEquals( "Office street", "Office Street", office.getStreet() );
-        assertEquals( "Office city", "Office City", office.getCity() );
-        assertEquals( "Office state", "OS", office.getState() );
-        assertEquals( "Office zip", "OfZip", office.getZipCode() );
-
-    }
-
-}
+/* $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.digester3;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+/**
+ * <p>
+ * Test Case for the Digester class. These tests perform parsing of XML documents to exercise the built-in rules.
+ * </p>
+ */
+public class RuleTestCase
+{
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The digester instance we will be processing.
+     */
+    protected Digester digester = null;
+
+    // --------------------------------------------------- Overall Test Methods
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    @Before
+    public void setUp()
+    {
+
+        digester = new Digester();
+
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    @After
+    public void tearDown()
+    {
+
+        digester = null;
+
+    }
+
+    // ------------------------------------------------ Individual Test Methods
+
+    /**
+     * Test object creation (and associated property setting) with nothing on the stack, which should cause an
+     * appropriate Employee object to be returned.
+     */
+    @Test
+    public void testObjectCreate1()
+        throws SAXException, IOException
+    {
+
+        // Configure the digester as required
+        digester.addObjectCreate( "employee", "org.apache.commons.digester3.Employee" );
+        digester.addSetProperties( "employee" );
+
+        // Parse our test input.
+        final Employee employee = digester.parse( getInputStream( "Test1.xml" ) );
+
+        assertNotNull( "Digester returned an object", employee );
+        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
+        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
+
+    }
+
+    /**
+     * Test object creation (and associated property setting) with nothing on the stack, which should cause an
+     * appropriate Employee object to be returned. The processing rules will process the nested Address elements as
+     * well, but will not attempt to add them to the Employee.
+     */
+    @Test
+    public void testObjectCreate2()
+        throws SAXException, IOException
+    {
+
+        // Configure the digester as required
+        digester.addObjectCreate( "employee", Employee.class );
+        digester.addSetProperties( "employee" );
+        digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" );
+        digester.addSetProperties( "employee/address" );
+
+        // Parse our test input.
+        final Employee employee = digester.parse( getInputStream( "Test1.xml" ) );
+
+        assertNotNull( "Digester returned an object", employee );
+        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
+        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
+
+    }
+
+    /**
+     * Test object creation (and associated property setting) with nothing on the stack, which should cause an
+     * appropriate Employee object to be returned. The processing rules will process the nested Address elements as
+     * well, and will add them to the owning Employee.
+     */
+    @Test
+    public void testObjectCreate3()
+        throws SAXException, IOException
+    {
+
+        // Configure the digester as required
+        digester.addObjectCreate( "employee", Employee.class );
+        digester.addSetProperties( "employee" );
+        digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" );
+        digester.addSetProperties( "employee/address" );
+        digester.addSetNext( "employee/address", "addAddress" );
+
+        // Parse our test input once
+        Object root = digester.parse( getInputStream( "Test1.xml" ) );
+
+        validateObjectCreate3( root );
+
+        // Parse the same input again
+        try
+        {
+            root = digester.parse( getInputStream( "Test1.xml" ) );
+        }
+        catch ( final Throwable t )
+        {
+            fail( "Digester threw IOException: " + t );
+        }
+        validateObjectCreate3( root );
+
+    }
+
+    /**
+     * Same as testObjectCreate1(), except use individual call method rules to set the properties of the Employee.
+     */
+    @Test
+    public void testObjectCreate4()
+        throws SAXException, IOException
+    {
+
+        // Configure the digester as required
+        digester.addObjectCreate( "employee", Employee.class );
+        digester.addCallMethod( "employee", "setFirstName", 1 );
+        digester.addCallParam( "employee", 0, "firstName" );
+        digester.addCallMethod( "employee", "setLastName", 1 );
+        digester.addCallParam( "employee", 0, "lastName" );
+
+        // Parse our test input.
+        final Employee employee = digester.parse( getInputStream( "Test1.xml" ) );
+
+        assertNotNull( "Digester returned an object", employee );
+        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
+        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
+
+    }
+
+    /**
+     * Same as testObjectCreate1(), except use individual call method rules to set the properties of the Employee. Bean
+     * data are defined using elements instead of attributes. The purpose is to test CallMethod with a paramCount=0 (ie
+     * the body of the element is the argument of the method).
+     */
+    @Test
+    public void testObjectCreate5()
+        throws SAXException, IOException
+    {
+
+        // 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.
+        final Employee employee = digester.parse( getInputStream( "Test5.xml" ) );
+
+        assertNotNull( "Digester returned an object", employee );
+        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
+        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
+
+    }
+
+    /**
+     * It should be possible to parse the same input twice, and get trees of objects that are isomorphic but not be
+     * identical object instances.
+     */
+    @Test
+    public void testRepeatedParse()
+        throws SAXException, IOException
+    {
+
+        // Configure the digester as required
+        digester.addObjectCreate( "employee", Employee.class );
+        digester.addSetProperties( "employee" );
+        digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" );
+        digester.addSetProperties( "employee/address" );
+        digester.addSetNext( "employee/address", "addAddress" );
+
+        // Parse our test input the first time
+        Object root1;
+        root1 = digester.parse( getInputStream( "Test1.xml" ) );
+
+        validateObjectCreate3( root1 );
+
+        // Parse our test input the second time
+        Object root2;
+        root2 = digester.parse( getInputStream( "Test1.xml" ) );
+
+        validateObjectCreate3( root2 );
+
+        // Make sure that it was a different root
+        assertTrue( "Different tree instances were returned", root1 != root2 );
+
+    }
+
+    /**
+     * Test object creation (and associated property setting) with nothing on the stack, which should cause an
+     * appropriate Employee object to be returned. The processing rules will process the nested Address elements as
+     * well, but will not attempt to add them to the Employee.
+     */
+    @Test
+    public void testRuleSet1()
+        throws SAXException, IOException
+    {
+
+        // Configure the digester as required
+        final RuleSet rs = new TestRuleSet();
+        digester.addRuleSet( rs );
+
+        // Parse our test input.
+        final Employee employee = digester.parse( getInputStream( "Test1.xml" ) );
+
+        assertNotNull( "Digester returned an object", employee );
+        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
+        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
+        assertNotNull( "Can retrieve home address", employee.getAddress( "home" ) );
+        assertNotNull( "Can retrieve office address", employee.getAddress( "office" ) );
+
+    }
+
+    /**
+     * Same as {@code testRuleSet1} except using a single namespace.
+     */
+    @Test
+    public void testRuleSet2()
+        throws SAXException, IOException
+    {
+
+        // Configure the digester as required
+        digester.setNamespaceAware( true );
+        final RuleSet rs = new TestRuleSet( null, "http://commons.apache.org/digester/Foo" );
+        digester.addRuleSet( rs );
+
+        // Parse our test input.
+        final Employee employee = digester.parse( getInputStream( "Test2.xml" ) );
+
+        assertNotNull( "Digester returned an object", employee );
+        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
+        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
+        assertNotNull( "Can retrieve home address", employee.getAddress( "home" ) );
+        assertNotNull( "Can retrieve office address", employee.getAddress( "office" ) );
+
+    }
+
+    /**
+     * Same as {@code testRuleSet2} except using a namespace for employee that we should recognize, and a namespace
+     * for address that we should skip.
+     */
+    @Test
+    public void testRuleSet3()
+        throws SAXException, IOException
+    {
+
+        // Configure the digester as required
+        digester.setNamespaceAware( true );
+        final RuleSet rs = new TestRuleSet( null, "http://commons.apache.org/digester/Foo" );
+        digester.addRuleSet( rs );
+
+        // Parse our test input.
+        final Employee employee = digester.parse( getInputStream( "Test3.xml" ) );
+
+        assertNotNull( "Digester returned an object", employee );
+        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
+        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
+        assertNull( "Can not retrieve home address", employee.getAddress( "home" ) );
+        assertNull( "Can not retrieve office address", employee.getAddress( "office" ) );
+
+    }
+
+    /**
+     * Test the two argument version of the SetTopRule rule. This test is based on testObjectCreate3 and should result
+     * in the same tree of objects. Instead of using the SetNextRule rule which results in a method invocation on the
+     * (top-1) (parent) object with the top object (child) as an argument, this test uses the SetTopRule rule which
+     * results in a method invocation on the top object (child) with the top-1 (parent) object as an argument. The three
+     * argument form is tested in {@code testSetTopRule2}.
+     */
+    @Test
+    public void testSetTopRule1()
+        throws SAXException, IOException
+    {
+
+        // Configure the digester as required
+        digester.addObjectCreate( "employee", "org.apache.commons.digester3.Employee" );
+        digester.addSetProperties( "employee" );
+        digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" );
+        digester.addSetProperties( "employee/address" );
+        digester.addSetTop( "employee/address", "setEmployee" );
+
+        // Parse our test input.
+        Object root;
+        root = digester.parse( getInputStream( "Test1.xml" ) );
+        validateObjectCreate3( root );
+
+    }
+
+    /**
+     * Same as {@code testSetTopRule1} except using the three argument form of the SetTopRule rule.
+     */
+    @Test
+    public void testSetTopRule2()
+        throws SAXException, IOException
+    {
+
+        // Configure the digester as required
+        digester.addObjectCreate( "employee", "org.apache.commons.digester3.Employee" );
+        digester.addSetProperties( "employee" );
+        digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" );
+        digester.addSetProperties( "employee/address" );
+        digester.addSetTop( "employee/address", "setEmployee", "org.apache.commons.digester3.Employee" );
+
+        // Parse our test input.
+        Object root;
+        root = digester.parse( getInputStream( "Test1.xml" ) );
+
+        validateObjectCreate3( root );
+
+    }
+
+    /**
+     * Test rule addition - this boils down to making sure that digester is set properly on rule addition.
+     */
+    @Test
+    public void testAddRule()
+    {
+        final Digester digester = new Digester();
+        final TestRule rule = new TestRule( "Test" );
+        digester.addRule( "/root", rule );
+
+        assertEquals( "Digester is not properly on rule addition.", digester, rule.getDigester() );
+
+    }
+
+    @Test
+    public void testSetNext()
+        throws SAXException, IOException
+    {
+        final Digester digester = new Digester();
+        digester.setRules( new ExtendedBaseRules() );
+        digester.setValidating( false );
+
+        digester.addObjectCreate( "!*/b", BetaBean.class );
+        digester.addObjectCreate( "!*/a", AlphaBean.class );
+        digester.addObjectCreate( "root", ArrayList.class );
+        digester.addSetProperties( "!*" );
+        digester.addSetNext( "!*/b/?", "setChild" );
+        digester.addSetNext( "!*/a/?", "setChild" );
+        digester.addSetNext( "!root/?", "add" );
+        final ArrayList<?> root = digester.parse( getInputStream( "Test4.xml" ) );
+
+        assertEquals( "Wrong array size", 2, root.size() );
+        final AlphaBean one = (AlphaBean) root.get( 0 );
+        assertTrue( one.getChild() instanceof BetaBean );
+        final BetaBean two = (BetaBean) one.getChild();
+        assertEquals( "Wrong name (1)", two.getName(), "TWO" );
+        assertTrue( two.getChild() instanceof AlphaBean );
+        final AlphaBean three = (AlphaBean) two.getChild();
+        assertEquals( "Wrong name (2)", three.getName(), "THREE" );
+        final BetaBean four = (BetaBean) root.get( 1 );
+        assertEquals( "Wrong name (3)", four.getName(), "FOUR" );
+        assertTrue( four.getChild() instanceof BetaBean );
+        final BetaBean five = (BetaBean) four.getChild();
+        assertEquals( "Wrong name (4)", five.getName(), "FIVE" );
+
+    }
+
+    @Test
+    public void testSetTop()
+        throws SAXException, IOException
+    {
+        final Digester digester = new Digester();
+        digester.setRules( new ExtendedBaseRules() );
+        digester.setValidating( false );
+
+        digester.addObjectCreate( "!*/b", BetaBean.class );
+        digester.addObjectCreate( "!*/a", AlphaBean.class );
+        digester.addObjectCreate( "root", ArrayList.class );
+        digester.addSetProperties( "!*" );
+        digester.addSetTop( "!*/b/?", "setParent" );
+        digester.addSetTop( "!*/a/?", "setParent" );
+        digester.addSetRoot( "!*/a", "add" );
+        digester.addSetRoot( "!*/b", "add" );
+        final ArrayList<?> root = digester.parse( getInputStream( "Test4.xml" ) );
+
+        assertEquals( "Wrong array size", 5, root.size() );
+
+        // note that the array is in popped order (rather than pushed)
+
+        Object obj = root.get( 1 );
+        assertTrue( "TWO should be a BetaBean", obj instanceof BetaBean );
+        final BetaBean two = (BetaBean) obj;
+        assertNotNull( "Two's parent should not be null", two.getParent() );
+        assertEquals( "Wrong name (1)", "TWO", two.getName() );
+        assertEquals( "Wrong name (2)", "ONE", two.getParent().getName() );
+
+        obj = root.get( 0 );
+        assertTrue( "THREE should be an AlphaBean", obj instanceof AlphaBean );
+        final AlphaBean three = (AlphaBean) obj;
+        assertNotNull( "Three's parent should not be null", three.getParent() );
+        assertEquals( "Wrong name (3)", "THREE", three.getName() );
+        assertEquals( "Wrong name (4)", "TWO", three.getParent().getName() );
+
+        obj = root.get( 3 );
+        assertTrue( "FIVE should be a BetaBean", obj instanceof BetaBean );
+        final BetaBean five = (BetaBean) obj;
+        assertNotNull( "Five's parent should not be null", five.getParent() );
+        assertEquals( "Wrong name (5)", "FIVE", five.getName() );
+        assertEquals( "Wrong name (6)", "FOUR", five.getParent().getName() );
+
+    }
+
+    /**
+     */
+    @Test
+    public void testSetCustomProperties()
+        throws SAXException, IOException
+    {
+
+        final Digester digester = new Digester();
+
+        digester.setValidating( false );
+
+        digester.addObjectCreate( "toplevel", ArrayList.class );
+        digester.addObjectCreate( "toplevel/one", Address.class );
+        digester.addSetNext( "toplevel/one", "add" );
+        digester.addObjectCreate( "toplevel/two", Address.class );
+        digester.addSetNext( "toplevel/two", "add" );
+        digester.addObjectCreate( "toplevel/three", Address.class );
+        digester.addSetNext( "toplevel/three", "add" );
+        digester.addObjectCreate( "toplevel/four", Address.class );
+        digester.addSetNext( "toplevel/four", "add" );
+        digester.addSetProperties( "toplevel/one" );
+        digester.addSetProperties( "toplevel/two", new String[] { "alt-street", "alt-city", "alt-state" },
+                                   new String[] { "street", "city", "state" } );
+        digester.addSetProperties( "toplevel/three", new String[] { "aCity", "state" }, new String[] { "city" } );
+        digester.addSetProperties( "toplevel/four", "alt-city", "city" );
+
+        final ArrayList<?> root = digester.parse( getInputStream( "Test7.xml" ) );
+
+        assertEquals( "Wrong array size", 4, root.size() );
+
+        // note that the array is in popped order (rather than pushed)
+
+        Object obj = root.get( 0 );
+        assertTrue( "(1) Should be an Address ", obj instanceof Address );
+        final Address addressOne = (Address) obj;
+        assertEquals( "(1) Street attribute", "New Street", addressOne.getStreet() );
+        assertEquals( "(1) City attribute", "Las Vegas", addressOne.getCity() );
+        assertEquals( "(1) State attribute", "Nevada", addressOne.getState() );
+
+        obj = root.get( 1 );
+        assertTrue( "(2) Should be an Address ", obj instanceof Address );
+        final Address addressTwo = (Address) obj;
+        assertEquals( "(2) Street attribute", "Old Street", addressTwo.getStreet() );
+        assertEquals( "(2) City attribute", "Portland", addressTwo.getCity() );
+        assertEquals( "(2) State attribute", "Oregon", addressTwo.getState() );
+
+        obj = root.get( 2 );
+        assertTrue( "(3) Should be an Address ", obj instanceof Address );
+        final Address addressThree = (Address) obj;
+        assertEquals( "(3) Street attribute", "4th Street", addressThree.getStreet() );
+        assertEquals( "(3) City attribute", "Dayton", addressThree.getCity() );
+        assertEquals( "(3) State attribute", "US", addressThree.getState() );
+
+        obj = root.get( 3 );
+        assertTrue( "(4) Should be an Address ", obj instanceof Address );
+        final Address addressFour = (Address) obj;
+        assertEquals( "(4) Street attribute", "6th Street", addressFour.getStreet() );
+        assertEquals( "(4) City attribute", "Cleveland", addressFour.getCity() );
+        assertEquals( "(4) State attribute", "Ohio", addressFour.getState() );
+
+    }
+
+    // ------------------------------------------------ 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
+     * @throws IOException if an input/output error occurs
+     */
+    protected InputStream getInputStream( final String name )
+        throws IOException
+    {
+
+        return ( this.getClass().getResourceAsStream( "/org/apache/commons/digester3/" + name ) );
+
+    }
+
+    /**
+     * Validate the assertions for ObjectCreateRule3.
+     *
+     * @param root Root object returned by {@code digester.parse()}
+     */
+    protected void validateObjectCreate3( final Object root )
+    {
+
+        // Validate the retrieved Employee
+        assertNotNull( "Digester returned an object", root );
+        assertTrue( "Digester returned an Employee", root instanceof Employee );
+        final Employee employee = (Employee) root;
+        assertEquals( "First name is correct", "First Name", employee.getFirstName() );
+        assertEquals( "Last name is correct", "Last Name", employee.getLastName() );
+
+        // Validate the corresponding "home" Address
+        final Address home = employee.getAddress( "home" );
+        assertNotNull( "Retrieved home address", home );
+        assertEquals( "Home street", "Home Street", home.getStreet() );
+        assertEquals( "Home city", "Home City", home.getCity() );
+        assertEquals( "Home state", "HS", home.getState() );
+        assertEquals( "Home zip", "HmZip", home.getZipCode() );
+
+        // Validate the corresponding "office" Address
+        final Address office = employee.getAddress( "office" );
+        assertNotNull( "Retrieved office address", office );
+        assertEquals( "Office street", "Office Street", office.getStreet() );
+        assertEquals( "Office city", "Office City", office.getCity() );
+        assertEquals( "Office state", "OS", office.getState() );
+        assertEquals( "Office zip", "OfZip", office.getZipCode() );
+
+    }
+
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/RulesBaseTestCase.java b/core/src/test/java/org/apache/commons/digester3/RulesBaseTestCase.java
index 156787f2..a4bb381c 100644
--- a/core/src/test/java/org/apache/commons/digester3/RulesBaseTestCase.java
+++ b/core/src/test/java/org/apache/commons/digester3/RulesBaseTestCase.java
@@ -1,273 +1,271 @@
-/* $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.digester3;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.Iterator;
-import java.util.List;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * <p>
- * Test Case for the RulesBase matching rules. Most of this material was original contained in the digester test case
- * but was moved into this class so that extensions of the basic matching rules behavior can extend this test case.
- * </p>
- *
- * @author Craig R. McClanahan
- */
-public class RulesBaseTestCase
-{
-
-    // ----------------------------------------------------- Instance Variables
-
-    /**
-     * The digester instance we will be processing.
-     */
-    protected Digester digester = null;
-
-    // -------------------------------------------------- Overall Test Methods
-
-    /**
-     * Set up instance variables required by this test case.
-     */
-    @Before
-    public void setUp()
-    {
-
-        digester = new Digester();
-        digester.setRules( createMatchingRulesForTest() );
-
-    }
-
-    /**
-     * <p>
-     * This should be overriden by subclasses.
-     *
-     * @return the matching rules to be tested.
-     */
-    protected Rules createMatchingRulesForTest()
-    {
-        return new RulesBase();
-    }
-
-    /**
-     * Tear down instance variables required by this test case.
-     */
-    @After
-    public void tearDown()
-    {
-
-        digester = null;
-
-    }
-
-    // ------------------------------------------------ Individual Test Methods
-
-    /**
-     * Basic test for rule creation and matching.
-     */
-    @Test
-    public void testRules()
-    {
-
-        // clear any existing rules
-        digester.getRules().clear();
-
-        // perform tests
-
-        assertEquals( "Initial rules list is empty", 0, digester.getRules().match( null, "a", null, null ).size() );
-        digester.addSetProperties( "a" );
-        assertEquals( "Add a matching rule", 1, digester.getRules().match( null, "a", null, null ).size() );
-        digester.addSetProperties( "b" );
-        assertEquals( "Add a non-matching rule", 1, digester.getRules().match( null, "a", null, null ).size() );
-        digester.addSetProperties( "a/b" );
-        assertEquals( "Add a non-matching nested rule", 1, digester.getRules().match( null, "a", null, null ).size() );
-        digester.addSetProperties( "a/b" );
-        assertEquals( "Add a second matching rule", 2, digester.getRules().match( null, "a/b", null, null ).size() );
-
-        // clean up
-        digester.getRules().clear();
-
-    }
-
-    /**
-     * <p>
-     * Test matching rules in {@link RulesBase}.
-     * </p>
-     * <p>
-     * Tests:
-     * </p>
-     * <ul>
-     * <li>exact match</li>
-     * <li>tail match</li>
-     * <li>longest pattern rule</li>
-     * </ul>
-     */
-    @Test
-    public void testRulesBase()
-    {
-
-        // clear any existing rules
-        digester.getRules().clear();
-
-        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
-
-        // We're going to set up
-        digester.addRule( "a/b/c/d", new TestRule( "a/b/c/d" ) );
-        digester.addRule( "*/d", new TestRule( "*/d" ) );
-        digester.addRule( "*/c/d", new TestRule( "*/c/d" ) );
-
-        // Test exact match
-        assertEquals( "Exact match takes precedence 1", 1, digester.getRules().match( null, "a/b/c/d", null, null ).size() );
-        assertEquals( "Exact match takes precedence 2", "a/b/c/d",
-                      ( (TestRule) digester.getRules().match( null, "a/b/c/d", null, null ).iterator().next() ).getIdentifier() );
-
-        // Test wildcard tail matching
-        assertEquals( "Wildcard tail matching rule 1", 1, digester.getRules().match( null, "a/b/d", null, null ).size() );
-        assertEquals( "Wildcard tail matching rule 2", "*/d",
-                      ( (TestRule) digester.getRules().match( null, "a/b/d", null, null ).iterator().next() ).getIdentifier() );
-
-        // Test the longest matching pattern rule
-        assertEquals( "Longest tail rule 1", 1, digester.getRules().match( null, "x/c/d", null, null ).size() );
-        assertEquals( "Longest tail rule 2", "*/c/d",
-                      ( (TestRule) digester.getRules().match( null, "x/c/d", null, null ).iterator().next() ).getIdentifier() );
-
-        // Test wildcard tail matching at the top level,
-        // i.e. the wildcard is nothing
-        digester.addRule( "*/a", new TestRule( "*/a" ) );
-        assertEquals( "Wildcard tail matching rule 3", 1, digester.getRules().match( null, "a", null, null ).size() );
-
-        assertEquals( "Wildcard tail matching rule 3 (match too much)", 0,
-                      digester.getRules().match( null, "aa", null, null ).size() );
-        // clean up
-        digester.getRules().clear();
-
-    }
-
-    /**
-     * Test basic matchings involving namespaces.
-     */
-    @Test
-    public void testBasicNamespaceMatching()
-    {
-
-        // clear any existing rules
-        digester.getRules().clear();
-
-        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
-
-        // Set up rules
-        digester.addRule( "alpha/beta/gamma", new TestRule( "No-Namespace" ) );
-        digester.addRule( "alpha/beta/gamma", new TestRule( "Euclidean-Namespace", "euclidean" ) );
-
-        List<Rule> list = digester.getRules().rules();
-
-        // test that matching null namespace brings back namespace and non-namespace rules
-        list = digester.getRules().match( null, "alpha/beta/gamma", null, null );
-
-        assertEquals( "Null namespace match (A)", 2, list.size() );
-
-        Iterator<Rule> it = list.iterator();
-        assertEquals( "Null namespace match (B)", "No-Namespace", ( (TestRule) it.next() ).getIdentifier() );
-        assertEquals( "Null namespace match (C)", "Euclidean-Namespace", ( (TestRule) it.next() ).getIdentifier() );
-
-        // test that matching euclid namespace brings back namespace and non-namespace rules
-        list = digester.getRules().match( "euclidean", "alpha/beta/gamma", null, null );
-
-        assertEquals( "Matching namespace match (A)", 2, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Matching namespace match (B)", "No-Namespace", ( (TestRule) it.next() ).getIdentifier() );
-        assertEquals( "Matching namespace match (C)", "Euclidean-Namespace", ( (TestRule) it.next() ).getIdentifier() );
-
-        // test that matching another namespace brings back only non-namespace rule
-        list = digester.getRules().match( "hyperbolic", "alpha/beta/gamma", null, null );
-
-        assertEquals( "Non matching namespace match (A)", 1, list.size() );
-
-        it = list.iterator();
-        assertEquals( "Non matching namespace match (B)", "No-Namespace", ( (TestRule) it.next() ).getIdentifier() );
-
-        // clean up
-        digester.getRules().clear();
-
-    }
-
-    /**
-     * Rules must always be returned in the correct order.
-     */
-    @Test
-    public void testOrdering()
-    {
-
-        // clear any existing rules
-        digester.getRules().clear();
-
-        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
-
-        // Set up rules
-        digester.addRule( "alpha/beta/gamma", new TestRule( "one" ) );
-        digester.addRule( "alpha/beta/gamma", new TestRule( "two" ) );
-        digester.addRule( "alpha/beta/gamma", new TestRule( "three" ) );
-
-        // test that rules are returned in set order
-        final List<Rule> list = digester.getRules().match( null, "alpha/beta/gamma", null, null );
-
-        assertEquals( "Testing ordering mismatch (A)", 3, list.size() );
-
-        final Iterator<Rule> it = list.iterator();
-        assertEquals( "Testing ordering mismatch (B)", "one", ( (TestRule) it.next() ).getIdentifier() );
-        assertEquals( "Testing ordering mismatch (C)", "two", ( (TestRule) it.next() ).getIdentifier() );
-        assertEquals( "Testing ordering mismatch (D)", "three", ( (TestRule) it.next() ).getIdentifier() );
-
-        // clean up
-        digester.getRules().clear();
-
-    }
-
-    /** Tests the behavior when a rule is added with a trailing slash */
-    @Test
-    public void testTrailingSlash()
-    {
-        // clear any existing rules
-        digester.getRules().clear();
-
-        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
-
-        // Set up rules
-        digester.addRule( "alpha/beta/gamma/", new TestRule( "one" ) );
-        digester.addRule( "alpha/beta/", new TestRule( "two" ) );
-        digester.addRule( "beta/gamma/alpha", new TestRule( "three" ) );
-
-        // test that rules are returned in set order
-        final List<Rule> list = digester.getRules().match( null, "alpha/beta/gamma", null, null );
-
-        assertEquals( "Testing number of matches", 1, list.size() );
-
-        final Iterator<Rule> it = list.iterator();
-        assertEquals( "Testing ordering (A)", "one", ( (TestRule) it.next() ).getIdentifier() );
-
-        // clean up
-        digester.getRules().clear();
-    }
-}
+/* $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.digester3;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * <p>
+ * Test Case for the RulesBase matching rules. Most of this material was original contained in the digester test case
+ * but was moved into this class so that extensions of the basic matching rules behavior can extend this test case.
+ * </p>
+ */
+public class RulesBaseTestCase
+{
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The digester instance we will be processing.
+     */
+    protected Digester digester = null;
+
+    // -------------------------------------------------- Overall Test Methods
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    @Before
+    public void setUp()
+    {
+
+        digester = new Digester();
+        digester.setRules( createMatchingRulesForTest() );
+
+    }
+
+    /**
+     * <p>
+     * This should be overriden by subclasses.
+     *
+     * @return the matching rules to be tested.
+     */
+    protected Rules createMatchingRulesForTest()
+    {
+        return new RulesBase();
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    @After
+    public void tearDown()
+    {
+
+        digester = null;
+
+    }
+
+    // ------------------------------------------------ Individual Test Methods
+
+    /**
+     * Basic test for rule creation and matching.
+     */
+    @Test
+    public void testRules()
+    {
+
+        // clear any existing rules
+        digester.getRules().clear();
+
+        // perform tests
+
+        assertEquals( "Initial rules list is empty", 0, digester.getRules().match( null, "a", null, null ).size() );
+        digester.addSetProperties( "a" );
+        assertEquals( "Add a matching rule", 1, digester.getRules().match( null, "a", null, null ).size() );
+        digester.addSetProperties( "b" );
+        assertEquals( "Add a non-matching rule", 1, digester.getRules().match( null, "a", null, null ).size() );
+        digester.addSetProperties( "a/b" );
+        assertEquals( "Add a non-matching nested rule", 1, digester.getRules().match( null, "a", null, null ).size() );
+        digester.addSetProperties( "a/b" );
+        assertEquals( "Add a second matching rule", 2, digester.getRules().match( null, "a/b", null, null ).size() );
+
+        // clean up
+        digester.getRules().clear();
+
+    }
+
+    /**
+     * <p>
+     * Test matching rules in {@link RulesBase}.
+     * </p>
+     * <p>
+     * Tests:
+     * </p>
+     * <ul>
+     * <li>exact match</li>
+     * <li>tail match</li>
+     * <li>longest pattern rule</li>
+     * </ul>
+     */
+    @Test
+    public void testRulesBase()
+    {
+
+        // clear any existing rules
+        digester.getRules().clear();
+
+        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
+
+        // We're going to set up
+        digester.addRule( "a/b/c/d", new TestRule( "a/b/c/d" ) );
+        digester.addRule( "*/d", new TestRule( "*/d" ) );
+        digester.addRule( "*/c/d", new TestRule( "*/c/d" ) );
+
+        // Test exact match
+        assertEquals( "Exact match takes precedence 1", 1, digester.getRules().match( null, "a/b/c/d", null, null ).size() );
+        assertEquals( "Exact match takes precedence 2", "a/b/c/d",
+                      ( (TestRule) digester.getRules().match( null, "a/b/c/d", null, null ).iterator().next() ).getIdentifier() );
+
+        // Test wildcard tail matching
+        assertEquals( "Wildcard tail matching rule 1", 1, digester.getRules().match( null, "a/b/d", null, null ).size() );
+        assertEquals( "Wildcard tail matching rule 2", "*/d",
+                      ( (TestRule) digester.getRules().match( null, "a/b/d", null, null ).iterator().next() ).getIdentifier() );
+
+        // Test the longest matching pattern rule
+        assertEquals( "Longest tail rule 1", 1, digester.getRules().match( null, "x/c/d", null, null ).size() );
+        assertEquals( "Longest tail rule 2", "*/c/d",
+                      ( (TestRule) digester.getRules().match( null, "x/c/d", null, null ).iterator().next() ).getIdentifier() );
+
+        // Test wildcard tail matching at the top level,
+        // i.e. the wildcard is nothing
+        digester.addRule( "*/a", new TestRule( "*/a" ) );
+        assertEquals( "Wildcard tail matching rule 3", 1, digester.getRules().match( null, "a", null, null ).size() );
+
+        assertEquals( "Wildcard tail matching rule 3 (match too much)", 0,
+                      digester.getRules().match( null, "aa", null, null ).size() );
+        // clean up
+        digester.getRules().clear();
+
+    }
+
+    /**
+     * Test basic matchings involving namespaces.
+     */
+    @Test
+    public void testBasicNamespaceMatching()
+    {
+
+        // clear any existing rules
+        digester.getRules().clear();
+
+        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
+
+        // Set up rules
+        digester.addRule( "alpha/beta/gamma", new TestRule( "No-Namespace" ) );
+        digester.addRule( "alpha/beta/gamma", new TestRule( "Euclidean-Namespace", "euclidean" ) );
+
+        List<Rule> list = digester.getRules().rules();
+
+        // test that matching null namespace brings back namespace and non-namespace rules
+        list = digester.getRules().match( null, "alpha/beta/gamma", null, null );
+
+        assertEquals( "Null namespace match (A)", 2, list.size() );
+
+        Iterator<Rule> it = list.iterator();
+        assertEquals( "Null namespace match (B)", "No-Namespace", ( (TestRule) it.next() ).getIdentifier() );
+        assertEquals( "Null namespace match (C)", "Euclidean-Namespace", ( (TestRule) it.next() ).getIdentifier() );
+
+        // test that matching euclid namespace brings back namespace and non-namespace rules
+        list = digester.getRules().match( "euclidean", "alpha/beta/gamma", null, null );
+
+        assertEquals( "Matching namespace match (A)", 2, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Matching namespace match (B)", "No-Namespace", ( (TestRule) it.next() ).getIdentifier() );
+        assertEquals( "Matching namespace match (C)", "Euclidean-Namespace", ( (TestRule) it.next() ).getIdentifier() );
+
+        // test that matching another namespace brings back only non-namespace rule
+        list = digester.getRules().match( "hyperbolic", "alpha/beta/gamma", null, null );
+
+        assertEquals( "Non matching namespace match (A)", 1, list.size() );
+
+        it = list.iterator();
+        assertEquals( "Non matching namespace match (B)", "No-Namespace", ( (TestRule) it.next() ).getIdentifier() );
+
+        // clean up
+        digester.getRules().clear();
+
+    }
+
+    /**
+     * Rules must always be returned in the correct order.
+     */
+    @Test
+    public void testOrdering()
+    {
+
+        // clear any existing rules
+        digester.getRules().clear();
+
+        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
+
+        // Set up rules
+        digester.addRule( "alpha/beta/gamma", new TestRule( "one" ) );
+        digester.addRule( "alpha/beta/gamma", new TestRule( "two" ) );
+        digester.addRule( "alpha/beta/gamma", new TestRule( "three" ) );
+
+        // test that rules are returned in set order
+        final List<Rule> list = digester.getRules().match( null, "alpha/beta/gamma", null, null );
+
+        assertEquals( "Testing ordering mismatch (A)", 3, list.size() );
+
+        final Iterator<Rule> it = list.iterator();
+        assertEquals( "Testing ordering mismatch (B)", "one", ( (TestRule) it.next() ).getIdentifier() );
+        assertEquals( "Testing ordering mismatch (C)", "two", ( (TestRule) it.next() ).getIdentifier() );
+        assertEquals( "Testing ordering mismatch (D)", "three", ( (TestRule) it.next() ).getIdentifier() );
+
+        // clean up
+        digester.getRules().clear();
+
+    }
+
+    /** Tests the behavior when a rule is added with a trailing slash */
+    @Test
+    public void testTrailingSlash()
+    {
+        // clear any existing rules
+        digester.getRules().clear();
+
+        assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
+
+        // Set up rules
+        digester.addRule( "alpha/beta/gamma/", new TestRule( "one" ) );
+        digester.addRule( "alpha/beta/", new TestRule( "two" ) );
+        digester.addRule( "beta/gamma/alpha", new TestRule( "three" ) );
+
+        // test that rules are returned in set order
+        final List<Rule> list = digester.getRules().match( null, "alpha/beta/gamma", null, null );
+
+        assertEquals( "Testing number of matches", 1, list.size() );
+
+        final Iterator<Rule> it = list.iterator();
+        assertEquals( "Testing ordering (A)", "one", ( (TestRule) it.next() ).getIdentifier() );
+
+        // clean up
+        digester.getRules().clear();
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/TestBean.java b/core/src/test/java/org/apache/commons/digester3/TestBean.java
index 6d111485..deaff66f 100644
--- a/core/src/test/java/org/apache/commons/digester3/TestBean.java
+++ b/core/src/test/java/org/apache/commons/digester3/TestBean.java
@@ -1,317 +1,315 @@
-/* $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.digester3;
-
-import org.apache.commons.digester3.annotations.rules.CallParam;
-import org.apache.commons.digester3.annotations.rules.ObjectCreate;
-
-/**
- * General purpose test bean for Digester tests.
- *
- * @author Craig R. McClanahan
- */
-public class TestBean
-{
-
-    public TestBean()
-    {
-        // do nothing
-    }
-
-    @ObjectCreate( pattern = "toplevel/bean" )
-    public TestBean( @CallParam( pattern = "toplevel/bean", attributeName = "boolean" ) final boolean booleanProperty,
-                     @CallParam( pattern = "toplevel/bean", attributeName = "double" ) final double doubleProperty )
-    {
-        setBooleanProperty( booleanProperty );
-        setDoubleProperty( doubleProperty );
-    }
-
-    /**
-     * see DIGESTER-154
-     *
-     * @param booleanProperty
-     * @param doubleProperty
-     */
-    public TestBean( final Boolean booleanProperty, final Double doubleProperty )
-    {
-        this( booleanProperty.booleanValue(), doubleProperty.doubleValue() );
-    }
-
-    // ------------------------------------------------------------- Properties
-
-    /**
-     * A boolean property whose initial value is true.
-     */
-    private boolean booleanProperty = true;
-
-    public boolean getBooleanProperty()
-    {
-        return ( booleanProperty );
-    }
-
-    public void setBooleanProperty( final boolean booleanProperty )
-    {
-        this.booleanProperty = booleanProperty;
-    }
-
-    /**
-     * A double property.
-     */
-    private double doubleProperty = 321.0;
-
-    public double getDoubleProperty()
-    {
-        return ( this.doubleProperty );
-    }
-
-    public void setDoubleProperty( final double doubleProperty )
-    {
-        this.doubleProperty = doubleProperty;
-    }
-
-    /**
-     * A boolean property whose initial value is false
-     */
-    private boolean falseProperty = false;
-
-    public boolean getFalseProperty()
-    {
-        return ( falseProperty );
-    }
-
-    public void setFalseProperty( final boolean falseProperty )
-    {
-        this.falseProperty = falseProperty;
-    }
-
-    /**
-     * A float property.
-     */
-    private float floatProperty = (float) 123.0;
-
-    public float getFloatProperty()
-    {
-        return ( this.floatProperty );
-    }
-
-    public void setFloatProperty( final float floatProperty )
-    {
-        this.floatProperty = floatProperty;
-    }
-
-    /**
-     * Integer arrays that are accessed as an array as well as indexed.
-     */
-    private int intArray[] = { 0, 10, 20, 30, 40 };
-
-    public int[] getIntArray()
-    {
-        return ( this.intArray );
-    }
-
-    public void setIntArray( final int intArray[] )
-    {
-        this.intArray = intArray;
-    }
-
-    private final int intIndexed[] = { 0, 10, 20, 30, 40 };
-
-    public int getIntIndexed( final int index )
-    {
-        return ( intIndexed[index] );
-    }
-
-    public void setIntIndexed( final int index, final int value )
-    {
-        intIndexed[index] = value;
-    }
-
-    private int intMultibox[] = new int[0];
-
-    public int[] getIntMultibox()
-    {
-        return ( this.intMultibox );
-    }
-
-    public void setIntMultibox( final int intMultibox[] )
-    {
-        this.intMultibox = intMultibox;
-    }
-
-    /**
-     * An integer property.
-     */
-    private int intProperty = 123;
-
-    public int getIntProperty()
-    {
-        return ( this.intProperty );
-    }
-
-    public void setIntProperty( final int intProperty )
-    {
-        this.intProperty = intProperty;
-    }
-
-    /**
-     * A long property.
-     */
-    private long longProperty = 321;
-
-    public long getLongProperty()
-    {
-        return ( this.longProperty );
-    }
-
-    public void setLongProperty( final long longProperty )
-    {
-        this.longProperty = longProperty;
-    }
-
-    /**
-     * A multiple-String SELECT element.
-     */
-    private String[] multipleSelect = { "Multiple 3", "Multiple 5", "Multiple 7" };
-
-    public String[] getMultipleSelect()
-    {
-        return ( this.multipleSelect );
-    }
-
-    public void setMultipleSelect( final String multipleSelect[] )
-    {
-        this.multipleSelect = multipleSelect;
-    }
-
-    /**
-     * A nested reference to another test bean (populated as needed).
-     */
-    private TestBean nested = null;
-
-    public TestBean getNested()
-    {
-        if ( nested == null ) {
-            nested = new TestBean();
-        }
-        return ( nested );
-    }
-
-    /**
-     * A String property with an initial value of null.
-     */
-    private String nullProperty = null;
-
-    public String getNullProperty()
-    {
-        return ( this.nullProperty );
-    }
-
-    public void setNullProperty( final String nullProperty )
-    {
-        this.nullProperty = nullProperty;
-    }
-
-    /**
-     * A short property.
-     */
-    private short shortProperty = (short) 987;
-
-    public short getShortProperty()
-    {
-        return ( this.shortProperty );
-    }
-
-    public void setShortProperty( final short shortProperty )
-    {
-        this.shortProperty = shortProperty;
-    }
-
-    /**
-     * A single-String value for a SELECT element.
-     */
-    private String singleSelect = "Single 5";
-
-    public String getSingleSelect()
-    {
-        return ( this.singleSelect );
-    }
-
-    public void setSingleSelect( final String singleSelect )
-    {
-        this.singleSelect = singleSelect;
-    }
-
-    /**
-     * String arrays that are accessed as an array as well as indexed.
-     */
-    private String stringArray[] = { "String 0", "String 1", "String 2", "String 3", "String 4" };
-
-    public String[] getStringArray()
-    {
-        return ( this.stringArray );
-    }
-
-    public void setStringArray( final String stringArray[] )
-    {
-        this.stringArray = stringArray;
-    }
-
-    private final String stringIndexed[] = { "String 0", "String 1", "String 2", "String 3", "String 4" };
-
-    public String getStringIndexed( final int index )
-    {
-        return ( stringIndexed[index] );
-    }
-
-    public void setStringIndexed( final int index, final String value )
-    {
-        stringIndexed[index] = value;
-    }
-
-    /**
-     * A String property.
-     */
-    private String stringProperty = "This is a string";
-
-    public String getStringProperty()
-    {
-        return ( this.stringProperty );
-    }
-
-    public void setStringProperty( final String stringProperty )
-    {
-        this.stringProperty = stringProperty;
-    }
-
-    /**
-     * An empty String property.
-     */
-    private String emptyStringProperty = "";
-
-    public String getEmptyStringProperty()
-    {
-        return ( this.emptyStringProperty );
-    }
-
-    public void setEmptyStringProperty( final String emptyStringProperty )
-    {
-        this.emptyStringProperty = emptyStringProperty;
-    }
-
-}
+/* $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.digester3;
+
+import org.apache.commons.digester3.annotations.rules.CallParam;
+import org.apache.commons.digester3.annotations.rules.ObjectCreate;
+
+/**
+ * General purpose test bean for Digester tests.
+ */
+public class TestBean
+{
+
+    public TestBean()
+    {
+        // do nothing
+    }
+
+    @ObjectCreate( pattern = "toplevel/bean" )
+    public TestBean( @CallParam( pattern = "toplevel/bean", attributeName = "boolean" ) final boolean booleanProperty,
+                     @CallParam( pattern = "toplevel/bean", attributeName = "double" ) final double doubleProperty )
+    {
+        setBooleanProperty( booleanProperty );
+        setDoubleProperty( doubleProperty );
+    }
+
+    /**
+     * see DIGESTER-154
+     *
+     * @param booleanProperty
+     * @param doubleProperty
+     */
+    public TestBean( final Boolean booleanProperty, final Double doubleProperty )
+    {
+        this( booleanProperty.booleanValue(), doubleProperty.doubleValue() );
+    }
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * A boolean property whose initial value is true.
+     */
+    private boolean booleanProperty = true;
+
+    public boolean getBooleanProperty()
+    {
+        return ( booleanProperty );
+    }
+
+    public void setBooleanProperty( final boolean booleanProperty )
+    {
+        this.booleanProperty = booleanProperty;
+    }
+
+    /**
+     * A double property.
+     */
+    private double doubleProperty = 321.0;
+
+    public double getDoubleProperty()
+    {
+        return ( this.doubleProperty );
+    }
+
+    public void setDoubleProperty( final double doubleProperty )
+    {
+        this.doubleProperty = doubleProperty;
+    }
+
+    /**
+     * A boolean property whose initial value is false
+     */
+    private boolean falseProperty = false;
+
+    public boolean getFalseProperty()
+    {
+        return ( falseProperty );
+    }
+
+    public void setFalseProperty( final boolean falseProperty )
+    {
+        this.falseProperty = falseProperty;
+    }
+
+    /**
+     * A float property.
+     */
+    private float floatProperty = (float) 123.0;
+
+    public float getFloatProperty()
+    {
+        return ( this.floatProperty );
+    }
+
+    public void setFloatProperty( final float floatProperty )
+    {
+        this.floatProperty = floatProperty;
+    }
+
+    /**
+     * Integer arrays that are accessed as an array as well as indexed.
+     */
+    private int intArray[] = { 0, 10, 20, 30, 40 };
+
+    public int[] getIntArray()
+    {
+        return ( this.intArray );
+    }
+
+    public void setIntArray( final int intArray[] )
+    {
+        this.intArray = intArray;
+    }
+
+    private final int intIndexed[] = { 0, 10, 20, 30, 40 };
+
+    public int getIntIndexed( final int index )
+    {
+        return ( intIndexed[index] );
+    }
+
+    public void setIntIndexed( final int index, final int value )
+    {
+        intIndexed[index] = value;
+    }
+
+    private int intMultibox[] = new int[0];
+
+    public int[] getIntMultibox()
+    {
+        return ( this.intMultibox );
+    }
+
+    public void setIntMultibox( final int intMultibox[] )
+    {
+        this.intMultibox = intMultibox;
+    }
+
+    /**
+     * An integer property.
+     */
+    private int intProperty = 123;
+
+    public int getIntProperty()
+    {
+        return ( this.intProperty );
+    }
+
+    public void setIntProperty( final int intProperty )
+    {
+        this.intProperty = intProperty;
+    }
+
+    /**
+     * A long property.
+     */
+    private long longProperty = 321;
+
+    public long getLongProperty()
+    {
+        return ( this.longProperty );
+    }
+
+    public void setLongProperty( final long longProperty )
+    {
+        this.longProperty = longProperty;
+    }
+
+    /**
+     * A multiple-String SELECT element.
+     */
+    private String[] multipleSelect = { "Multiple 3", "Multiple 5", "Multiple 7" };
+
+    public String[] getMultipleSelect()
+    {
+        return ( this.multipleSelect );
+    }
+
+    public void setMultipleSelect( final String multipleSelect[] )
+    {
+        this.multipleSelect = multipleSelect;
+    }
+
+    /**
+     * A nested reference to another test bean (populated as needed).
+     */
+    private TestBean nested = null;
+
+    public TestBean getNested()
+    {
+        if ( nested == null ) {
+            nested = new TestBean();
+        }
+        return ( nested );
+    }
+
+    /**
+     * A String property with an initial value of null.
+     */
+    private String nullProperty = null;
+
+    public String getNullProperty()
+    {
+        return ( this.nullProperty );
+    }
+
+    public void setNullProperty( final String nullProperty )
+    {
+        this.nullProperty = nullProperty;
+    }
+
+    /**
+     * A short property.
+     */
+    private short shortProperty = (short) 987;
+
+    public short getShortProperty()
+    {
+        return ( this.shortProperty );
+    }
+
+    public void setShortProperty( final short shortProperty )
+    {
+        this.shortProperty = shortProperty;
+    }
+
+    /**
+     * A single-String value for a SELECT element.
+     */
+    private String singleSelect = "Single 5";
+
+    public String getSingleSelect()
+    {
+        return ( this.singleSelect );
+    }
+
+    public void setSingleSelect( final String singleSelect )
+    {
+        this.singleSelect = singleSelect;
+    }
+
+    /**
+     * String arrays that are accessed as an array as well as indexed.
+     */
+    private String stringArray[] = { "String 0", "String 1", "String 2", "String 3", "String 4" };
+
+    public String[] getStringArray()
+    {
+        return ( this.stringArray );
+    }
+
+    public void setStringArray( final String stringArray[] )
+    {
+        this.stringArray = stringArray;
+    }
+
+    private final String stringIndexed[] = { "String 0", "String 1", "String 2", "String 3", "String 4" };
+
+    public String getStringIndexed( final int index )
+    {
+        return ( stringIndexed[index] );
+    }
+
+    public void setStringIndexed( final int index, final String value )
+    {
+        stringIndexed[index] = value;
+    }
+
+    /**
+     * A String property.
+     */
+    private String stringProperty = "This is a string";
+
+    public String getStringProperty()
+    {
+        return ( this.stringProperty );
+    }
+
+    public void setStringProperty( final String stringProperty )
+    {
+        this.stringProperty = stringProperty;
+    }
+
+    /**
+     * An empty String property.
+     */
+    private String emptyStringProperty = "";
+
+    public String getEmptyStringProperty()
+    {
+        return ( this.emptyStringProperty );
+    }
+
+    public void setEmptyStringProperty( final String emptyStringProperty )
+    {
+        this.emptyStringProperty = emptyStringProperty;
+    }
+
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/TestEntityResolution.java b/core/src/test/java/org/apache/commons/digester3/TestEntityResolution.java
index f75d7c00..b2048ca3 100644
--- a/core/src/test/java/org/apache/commons/digester3/TestEntityResolution.java
+++ b/core/src/test/java/org/apache/commons/digester3/TestEntityResolution.java
@@ -1,57 +1,55 @@
-/* $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.digester3;
-
-import java.io.File;
-
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.junit.Test;
-import org.xml.sax.helpers.DefaultHandler;
-
-/**
- * Tests for entity resolution.
- *
- * @author <a href='http://commons.apache.org/'>Apache Commons Team</a>
- */
-public class TestEntityResolution
-{
-
-    @Test
-    public void testParserResolveRelative()
-        throws Exception
-    {
-        final SAXParserFactory factory = SAXParserFactory.newInstance();
-        factory.setValidating( true );
-        factory.setNamespaceAware( true );
-        final SAXParser parser = factory.newSAXParser();
-
-        parser.parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd.xml" ),
-                      new DefaultHandler() );
-    }
-
-    @Test
-    public void testDigesterResolveRelative()
-        throws Exception
-    {
-        final Digester digester = new Digester();
-        digester.setValidating( true );
-        digester.parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd.xml" ) );
-    }
-}
+/* $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.digester3;
+
+import java.io.File;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.junit.Test;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Tests for entity resolution.
+ */
+public class TestEntityResolution
+{
+
+    @Test
+    public void testParserResolveRelative()
+        throws Exception
+    {
+        final SAXParserFactory factory = SAXParserFactory.newInstance();
+        factory.setValidating( true );
+        factory.setNamespaceAware( true );
+        final SAXParser parser = factory.newSAXParser();
+
+        parser.parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd.xml" ),
+                      new DefaultHandler() );
+    }
+
+    @Test
+    public void testDigesterResolveRelative()
+        throws Exception
+    {
+        final Digester digester = new Digester();
+        digester.setValidating( true );
+        digester.parse( new File( "src/test/resources/org/apache/commons/digester3/document-with-relative-dtd.xml" ) );
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/TestFactoryCreate.java b/core/src/test/java/org/apache/commons/digester3/TestFactoryCreate.java
index 135ca5c0..e964923d 100644
--- a/core/src/test/java/org/apache/commons/digester3/TestFactoryCreate.java
+++ b/core/src/test/java/org/apache/commons/digester3/TestFactoryCreate.java
@@ -1,227 +1,225 @@
-/* $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.digester3;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.xml.sax.Attributes;
-
-/**
- * Test case for factory create rules.
- *
- * @author Robert Burrell Donkin
- */
-@RunWith(value = Parameterized.class)
-public class TestFactoryCreate
-{
-
-    private final boolean ignoreCreateExceptions;
-
-    public TestFactoryCreate( final boolean ignoreCreateExceptions )
-    {
-        this.ignoreCreateExceptions = ignoreCreateExceptions;
-    }
-
-    @Parameters
-    public static Collection<Object[]> data()
-    {
-        final Collection<Object[]> data = new ArrayList<Object[]>(2);
-
-        data.add( new Object[] { true } );
-        data.add( new Object[] { false } );
-
-        return data;
-    }
-
-    // --------------------------------------------------------------- Test cases
-
-    @Test
-    public void testPropagateException()
-        throws Exception
-    {
-
-        // only used with this method
-        class ThrowExceptionCreateRule
-            extends AbstractObjectCreationFactory<Object>
-        {
-            @Override
-            public Object createObject( final Attributes attributes )
-                throws Exception
-            {
-                throw new RuntimeException();
-            }
-        }
-
-        // now for the tests
-        final String xml = "<?xml version='1.0' ?><root><element/></root>";
-
-        // test default - which is to propagate the exception
-        final Digester digester = new Digester();
-        digester.addFactoryCreate( "root", new ThrowExceptionCreateRule(), ignoreCreateExceptions );
-        try
-        {
-
-            digester.parse( new StringReader( xml ) );
-            if ( !ignoreCreateExceptions )
-            {
-                fail( "Exception should be propagated from create rule" );
-            }
-
-        }
-        catch ( final Exception e )
-        {
-            if ( ignoreCreateExceptions )
-            {
-                fail( "Exception should not be propagated" );
-            }
-        }
-    }
-
-    @Test
-    public void testFactoryCreateRule()
-        throws Exception
-    {
-
-        // test passing object create
-        Digester digester = new Digester();
-        ObjectCreationFactoryTestImpl factory = new ObjectCreationFactoryTestImpl();
-        digester.addFactoryCreate( "root", factory, ignoreCreateExceptions );
-        String xml = "<?xml version='1.0' ?><root one='good' two='bad' three='ugly'><element/></root>";
-        digester.parse( new StringReader( xml ) );
-
-        assertEquals( "Object create not called(1)[" + ignoreCreateExceptions + "]", factory.called, true );
-        assertEquals( "Attribute not passed (1)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "one" ),
-                      "good" );
-        assertEquals( "Attribute not passed (2)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "two" ),
-                      "bad" );
-        assertEquals( "Attribute not passed (3)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ),
-                      "ugly" );
-
-        digester = new Digester();
-        digester.addFactoryCreate( "root", "org.apache.commons.digester3.ObjectCreationFactoryTestImpl",
-                                   ignoreCreateExceptions );
-        digester.addSetNext( "root", "add" );
-        xml = "<?xml version='1.0' ?><root one='good' two='bad' three='ugly'><element/></root>";
-        List<ObjectCreationFactoryTestImpl> list = new ArrayList<ObjectCreationFactoryTestImpl>();
-        digester.push( list );
-        digester.parse( new StringReader( xml ) );
-
-        assertEquals( "List should contain only the factory object", list.size(), 1 );
-        factory = list.get( 0 );
-        assertEquals( "Object create not called(2)[" + ignoreCreateExceptions + "]", factory.called, true );
-        assertEquals( "Attribute not passed (4)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "one" ),
-                      "good" );
-        assertEquals( "Attribute not passed (5)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "two" ),
-                      "bad" );
-        assertEquals( "Attribute not passed (6)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ),
-                      "ugly" );
-
-        digester = new Digester();
-        digester.addFactoryCreate( "root", "org.apache.commons.digester3.ObjectCreationFactoryTestImpl", "override",
-                                   ignoreCreateExceptions );
-        digester.addSetNext( "root", "add" );
-        xml = "<?xml version='1.0' ?><root one='good' two='bad' three='ugly'><element/></root>";
-        list = new ArrayList<ObjectCreationFactoryTestImpl>();
-        digester.push( list );
-        digester.parse( new StringReader( xml ) );
-
-        assertEquals( "List should contain only the factory object", list.size(), 1 );
-        factory = list.get( 0 );
-        assertEquals( "Object create not called(3)[" + ignoreCreateExceptions + "]", factory.called, true );
-        assertEquals( "Attribute not passed (7)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "one" ),
-                      "good" );
-        assertEquals( "Attribute not passed (8)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "two" ),
-                      "bad" );
-        assertEquals( "Attribute not passed (8)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ),
-                      "ugly" );
-
-        digester = new Digester();
-        digester.addFactoryCreate( "root", "org.apache.commons.digester3.ObjectCreationFactoryTestImpl", "override",
-                                   ignoreCreateExceptions );
-        digester.addSetNext( "root", "add" );
-        xml =
-            ("<?xml version='1.0' ?><root one='good' two='bad' three='ugly' "
-                + " override='org.apache.commons.digester3.OtherTestObjectCreationFactory' >" + "<element/></root>");
-        list = new ArrayList<ObjectCreationFactoryTestImpl>();
-        digester.push( list );
-        digester.parse( new StringReader( xml ) );
-
-        assertEquals( "List should contain only the factory object", list.size(), 1 );
-        factory = list.get( 0 );
-        assertEquals( "Attribute Override Failed (1)", factory.getClass().getName(),
-                      "org.apache.commons.digester3.OtherTestObjectCreationFactory" );
-        assertEquals( "Object create not called(4)[" + ignoreCreateExceptions + "]", factory.called, true );
-        assertEquals( "Attribute not passed (10)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "one" ),
-                      "good" );
-        assertEquals( "Attribute not passed (11)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "two" ),
-                      "bad" );
-        assertEquals( "Attribute not passed (12)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ),
-                      "ugly" );
-
-        digester = new Digester();
-        digester.addFactoryCreate( "root", ObjectCreationFactoryTestImpl.class, "override", ignoreCreateExceptions );
-        digester.addSetNext( "root", "add" );
-        xml = "<?xml version='1.0' ?><root one='good' two='bad' three='ugly'><element/></root>";
-        list = new ArrayList<ObjectCreationFactoryTestImpl>();
-        digester.push( list );
-        digester.parse( new StringReader( xml ) );
-
-        assertEquals( "List should contain only the factory object", list.size(), 1 );
-        factory = list.get( 0 );
-        assertEquals( "Object create not called(5)[" + ignoreCreateExceptions + "]", factory.called, true );
-        assertEquals( "Attribute not passed (13)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "one" ),
-                      "good" );
-        assertEquals( "Attribute not passed (14)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "two" ),
-                      "bad" );
-        assertEquals( "Attribute not passed (15)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ),
-                      "ugly" );
-
-        digester = new Digester();
-        digester.addFactoryCreate( "root", ObjectCreationFactoryTestImpl.class, "override", ignoreCreateExceptions );
-        digester.addSetNext( "root", "add" );
-        xml =
-            ("<?xml version='1.0' ?><root one='good' two='bad' three='ugly' "
-                + " override='org.apache.commons.digester3.OtherTestObjectCreationFactory' >" + "<element/></root>");
-        list = new ArrayList<ObjectCreationFactoryTestImpl>();
-        digester.push( list );
-        digester.parse( new StringReader( xml ) );
-
-        assertEquals( "List should contain only the factory object", list.size(), 1 );
-        factory = list.get( 0 );
-        assertEquals( "Attribute Override Failed (2)", factory.getClass().getName(),
-                      "org.apache.commons.digester3.OtherTestObjectCreationFactory" );
-        assertEquals( "Object create not called(6)[" + ignoreCreateExceptions + "]", factory.called, true );
-        assertEquals( "Attribute not passed (16)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "one" ),
-                      "good" );
-        assertEquals( "Attribute not passed (17)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "two" ),
-                      "bad" );
-        assertEquals( "Attribute not passed (18)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ),
-                      "ugly" );
-    }
-}
+/* $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.digester3;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.xml.sax.Attributes;
+
+/**
+ * Test case for factory create rules.
+ */
+@RunWith(value = Parameterized.class)
+public class TestFactoryCreate
+{
+
+    private final boolean ignoreCreateExceptions;
+
+    public TestFactoryCreate( final boolean ignoreCreateExceptions )
+    {
+        this.ignoreCreateExceptions = ignoreCreateExceptions;
+    }
+
+    @Parameters
+    public static Collection<Object[]> data()
+    {
+        final Collection<Object[]> data = new ArrayList<Object[]>(2);
+
+        data.add( new Object[] { true } );
+        data.add( new Object[] { false } );
+
+        return data;
+    }
+
+    // --------------------------------------------------------------- Test cases
+
+    @Test
+    public void testPropagateException()
+        throws Exception
+    {
+
+        // only used with this method
+        class ThrowExceptionCreateRule
+            extends AbstractObjectCreationFactory<Object>
+        {
+            @Override
+            public Object createObject( final Attributes attributes )
+                throws Exception
+            {
+                throw new RuntimeException();
+            }
+        }
+
+        // now for the tests
+        final String xml = "<?xml version='1.0' ?><root><element/></root>";
+
+        // test default - which is to propagate the exception
+        final Digester digester = new Digester();
+        digester.addFactoryCreate( "root", new ThrowExceptionCreateRule(), ignoreCreateExceptions );
+        try
+        {
+
+            digester.parse( new StringReader( xml ) );
+            if ( !ignoreCreateExceptions )
+            {
+                fail( "Exception should be propagated from create rule" );
+            }
+
+        }
+        catch ( final Exception e )
+        {
+            if ( ignoreCreateExceptions )
+            {
+                fail( "Exception should not be propagated" );
+            }
+        }
+    }
+
+    @Test
+    public void testFactoryCreateRule()
+        throws Exception
+    {
+
+        // test passing object create
+        Digester digester = new Digester();
+        ObjectCreationFactoryTestImpl factory = new ObjectCreationFactoryTestImpl();
+        digester.addFactoryCreate( "root", factory, ignoreCreateExceptions );
+        String xml = "<?xml version='1.0' ?><root one='good' two='bad' three='ugly'><element/></root>";
+        digester.parse( new StringReader( xml ) );
+
+        assertEquals( "Object create not called(1)[" + ignoreCreateExceptions + "]", factory.called, true );
+        assertEquals( "Attribute not passed (1)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "one" ),
+                      "good" );
+        assertEquals( "Attribute not passed (2)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "two" ),
+                      "bad" );
+        assertEquals( "Attribute not passed (3)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ),
+                      "ugly" );
+
+        digester = new Digester();
+        digester.addFactoryCreate( "root", "org.apache.commons.digester3.ObjectCreationFactoryTestImpl",
+                                   ignoreCreateExceptions );
+        digester.addSetNext( "root", "add" );
+        xml = "<?xml version='1.0' ?><root one='good' two='bad' three='ugly'><element/></root>";
+        List<ObjectCreationFactoryTestImpl> list = new ArrayList<ObjectCreationFactoryTestImpl>();
+        digester.push( list );
+        digester.parse( new StringReader( xml ) );
+
+        assertEquals( "List should contain only the factory object", list.size(), 1 );
+        factory = list.get( 0 );
+        assertEquals( "Object create not called(2)[" + ignoreCreateExceptions + "]", factory.called, true );
+        assertEquals( "Attribute not passed (4)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "one" ),
+                      "good" );
+        assertEquals( "Attribute not passed (5)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "two" ),
+                      "bad" );
+        assertEquals( "Attribute not passed (6)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ),
+                      "ugly" );
+
+        digester = new Digester();
+        digester.addFactoryCreate( "root", "org.apache.commons.digester3.ObjectCreationFactoryTestImpl", "override",
+                                   ignoreCreateExceptions );
+        digester.addSetNext( "root", "add" );
+        xml = "<?xml version='1.0' ?><root one='good' two='bad' three='ugly'><element/></root>";
+        list = new ArrayList<ObjectCreationFactoryTestImpl>();
+        digester.push( list );
+        digester.parse( new StringReader( xml ) );
+
+        assertEquals( "List should contain only the factory object", list.size(), 1 );
+        factory = list.get( 0 );
+        assertEquals( "Object create not called(3)[" + ignoreCreateExceptions + "]", factory.called, true );
+        assertEquals( "Attribute not passed (7)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "one" ),
+                      "good" );
+        assertEquals( "Attribute not passed (8)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "two" ),
+                      "bad" );
+        assertEquals( "Attribute not passed (8)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ),
+                      "ugly" );
+
+        digester = new Digester();
+        digester.addFactoryCreate( "root", "org.apache.commons.digester3.ObjectCreationFactoryTestImpl", "override",
+                                   ignoreCreateExceptions );
+        digester.addSetNext( "root", "add" );
+        xml =
+            ("<?xml version='1.0' ?><root one='good' two='bad' three='ugly' "
+                + " override='org.apache.commons.digester3.OtherTestObjectCreationFactory' >" + "<element/></root>");
+        list = new ArrayList<ObjectCreationFactoryTestImpl>();
+        digester.push( list );
+        digester.parse( new StringReader( xml ) );
+
+        assertEquals( "List should contain only the factory object", list.size(), 1 );
+        factory = list.get( 0 );
+        assertEquals( "Attribute Override Failed (1)", factory.getClass().getName(),
+                      "org.apache.commons.digester3.OtherTestObjectCreationFactory" );
+        assertEquals( "Object create not called(4)[" + ignoreCreateExceptions + "]", factory.called, true );
+        assertEquals( "Attribute not passed (10)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "one" ),
+                      "good" );
+        assertEquals( "Attribute not passed (11)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "two" ),
+                      "bad" );
+        assertEquals( "Attribute not passed (12)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ),
+                      "ugly" );
+
+        digester = new Digester();
+        digester.addFactoryCreate( "root", ObjectCreationFactoryTestImpl.class, "override", ignoreCreateExceptions );
+        digester.addSetNext( "root", "add" );
+        xml = "<?xml version='1.0' ?><root one='good' two='bad' three='ugly'><element/></root>";
+        list = new ArrayList<ObjectCreationFactoryTestImpl>();
+        digester.push( list );
+        digester.parse( new StringReader( xml ) );
+
+        assertEquals( "List should contain only the factory object", list.size(), 1 );
+        factory = list.get( 0 );
+        assertEquals( "Object create not called(5)[" + ignoreCreateExceptions + "]", factory.called, true );
+        assertEquals( "Attribute not passed (13)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "one" ),
+                      "good" );
+        assertEquals( "Attribute not passed (14)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "two" ),
+                      "bad" );
+        assertEquals( "Attribute not passed (15)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ),
+                      "ugly" );
+
+        digester = new Digester();
+        digester.addFactoryCreate( "root", ObjectCreationFactoryTestImpl.class, "override", ignoreCreateExceptions );
+        digester.addSetNext( "root", "add" );
+        xml =
+            ("<?xml version='1.0' ?><root one='good' two='bad' three='ugly' "
+                + " override='org.apache.commons.digester3.OtherTestObjectCreationFactory' >" + "<element/></root>");
+        list = new ArrayList<ObjectCreationFactoryTestImpl>();
+        digester.push( list );
+        digester.parse( new StringReader( xml ) );
+
+        assertEquals( "List should contain only the factory object", list.size(), 1 );
+        factory = list.get( 0 );
+        assertEquals( "Attribute Override Failed (2)", factory.getClass().getName(),
+                      "org.apache.commons.digester3.OtherTestObjectCreationFactory" );
+        assertEquals( "Object create not called(6)[" + ignoreCreateExceptions + "]", factory.called, true );
+        assertEquals( "Attribute not passed (16)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "one" ),
+                      "good" );
+        assertEquals( "Attribute not passed (17)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "two" ),
+                      "bad" );
+        assertEquals( "Attribute not passed (18)[" + ignoreCreateExceptions + "]", factory.attributes.getValue( "three" ),
+                      "ugly" );
+    }
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/TestRule.java b/core/src/test/java/org/apache/commons/digester3/TestRule.java
index 8775fbc9..e7271e46 100644
--- a/core/src/test/java/org/apache/commons/digester3/TestRule.java
+++ b/core/src/test/java/org/apache/commons/digester3/TestRule.java
@@ -1,191 +1,189 @@
-/* $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.digester3;
-
-import java.util.List;
-
-import org.apache.commons.digester3.binder.RuleProvider;
-import org.xml.sax.Attributes;
-
-/**
- * <p>
- * This rule implementation is intended to help test digester. The idea is that you can test which rule matches by
- * looking at the identifier.
- * </p>
- *
- * @author Robert Burrell Donkin
- */
-
-public class TestRule
-    extends Rule
-{
-
-    // ----------------------------------------------------- Instance Variables
-
-    /** String identifing this particular {@code TestRule} */
-    private final String identifier;
-
-    /** Used when testing body text */
-    private String bodyText;
-
-    /** Used when testing call orders */
-    private List<Rule> order;
-
-    // ----------------------------------------------------------- Constructors
-
-    /**
-     * Base constructor.
-     *
-     * @param identifier Used to tell which TestRule is which
-     */
-    public TestRule( final String identifier )
-    {
-
-        this.identifier = identifier;
-    }
-
-    /**
-     * Constructor sets namespace URI.
-     *
-     * @param identifier Used to tell which TestRule is which
-     * @param namespaceURI Set rule namespace
-     */
-    public TestRule( final String identifier, final String namespaceURI )
-    {
-
-        this.identifier = identifier;
-        setNamespaceURI( namespaceURI );
-
-    }
-
-    // ------------------------------------------------ Rule Implementation
-
-    /**
-     * 'Begin' call.
-     */
-    @Override
-    public void begin( final String namespace, final String name, final Attributes attributes )
-        throws Exception
-    {
-        appendCall();
-    }
-
-    /**
-     * 'Body' call.
-     */
-    @Override
-    public void body( final String namespace, final String name, final String text )
-        throws Exception
-    {
-        this.bodyText = text;
-        appendCall();
-    }
-
-    /**
-     * 'End' call.
-     */
-    @Override
-    public void end( final String namespace, final String name )
-        throws Exception
-    {
-        appendCall();
-    }
-
-    // ------------------------------------------------ Methods
-
-    /**
-     * If a list has been set, append this to the list.
-     */
-    protected void appendCall()
-    {
-        if ( order != null ) {
-            order.add( this );
-        }
-    }
-
-    /**
-     * Get the body text that was set.
-     */
-    public String getBodyText()
-    {
-        return bodyText;
-    }
-
-    /**
-     * Get the identifier associated with this test.
-     */
-    public String getIdentifier()
-    {
-        return identifier;
-    }
-
-    /**
-     * Get call order list.
-     */
-    public List<Rule> getOrder()
-    {
-        return order;
-    }
-
-    /**
-     * Set call order list
-     */
-    public void setOrder( final List<Rule> order )
-    {
-        this.order = order;
-    }
-
-    /**
-     * Return the identifier.
-     */
-    @Override
-    public String toString()
-    {
-        return identifier;
-    }
-
-    public static class TestRuleProvider implements RuleProvider<TestRule>
-    {
-
-        private final String identifier;
-
-        private final List<Rule> callOrder;
-
-        public TestRuleProvider( final String identifier )
-        {
-            this( identifier, null );
-        }
-
-        public TestRuleProvider( final String identifier, final List<Rule> callOrder )
-        {
-            this.identifier = identifier;
-            this.callOrder = callOrder;
-        }
-
-        @Override
-        public TestRule get()
-        {
-            final TestRule testRule = new TestRule( identifier );
-            testRule.setOrder( callOrder );
-            return testRule;
-        }
-
-    }
-
-}
+/* $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.digester3;
+
+import java.util.List;
+
+import org.apache.commons.digester3.binder.RuleProvider;
+import org.xml.sax.Attributes;
+
+/**
+ * <p>
+ * This rule implementation is intended to help test digester. The idea is that you can test which rule matches by
+ * looking at the identifier.
+ * </p>
+ */
+
+public class TestRule
+    extends Rule
+{
+
+    // ----------------------------------------------------- Instance Variables
+
+    /** String identifing this particular {@code TestRule} */
+    private final String identifier;
+
+    /** Used when testing body text */
+    private String bodyText;
+
+    /** Used when testing call orders */
+    private List<Rule> order;
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Base constructor.
+     *
+     * @param identifier Used to tell which TestRule is which
+     */
+    public TestRule( final String identifier )
+    {
+
+        this.identifier = identifier;
+    }
+
+    /**
+     * Constructor sets namespace URI.
+     *
+     * @param identifier Used to tell which TestRule is which
+     * @param namespaceURI Set rule namespace
+     */
+    public TestRule( final String identifier, final String namespaceURI )
+    {
+
+        this.identifier = identifier;
+        setNamespaceURI( namespaceURI );
+
+    }
+
+    // ------------------------------------------------ Rule Implementation
+
+    /**
+     * 'Begin' call.
+     */
+    @Override
+    public void begin( final String namespace, final String name, final Attributes attributes )
+        throws Exception
+    {
+        appendCall();
+    }
+
+    /**
+     * 'Body' call.
+     */
+    @Override
+    public void body( final String namespace, final String name, final String text )
+        throws Exception
+    {
+        this.bodyText = text;
+        appendCall();
+    }
+
+    /**
+     * 'End' call.
+     */
+    @Override
+    public void end( final String namespace, final String name )
+        throws Exception
+    {
+        appendCall();
+    }
+
+    // ------------------------------------------------ Methods
+
+    /**
+     * If a list has been set, append this to the list.
+     */
+    protected void appendCall()
+    {
+        if ( order != null ) {
+            order.add( this );
+        }
+    }
+
+    /**
+     * Get the body text that was set.
+     */
+    public String getBodyText()
+    {
+        return bodyText;
+    }
+
+    /**
+     * Get the identifier associated with this test.
+     */
+    public String getIdentifier()
+    {
+        return identifier;
+    }
+
+    /**
+     * Get call order list.
+     */
+    public List<Rule> getOrder()
+    {
+        return order;
+    }
+
+    /**
+     * Set call order list
+     */
+    public void setOrder( final List<Rule> order )
+    {
+        this.order = order;
+    }
+
+    /**
+     * Return the identifier.
+     */
+    @Override
+    public String toString()
+    {
+        return identifier;
+    }
+
+    public static class TestRuleProvider implements RuleProvider<TestRule>
+    {
+
+        private final String identifier;
+
+        private final List<Rule> callOrder;
+
+        public TestRuleProvider( final String identifier )
+        {
+            this( identifier, null );
+        }
+
+        public TestRuleProvider( final String identifier, final List<Rule> callOrder )
+        {
+            this.identifier = identifier;
+            this.callOrder = callOrder;
+        }
+
+        @Override
+        public TestRule get()
+        {
+            final TestRule testRule = new TestRule( identifier );
+            testRule.setOrder( callOrder );
+            return testRule;
+        }
+
+    }
+
+}
diff --git a/core/src/test/java/org/apache/commons/digester3/TestRuleSet.java b/core/src/test/java/org/apache/commons/digester3/TestRuleSet.java
index 78413985..4d4270e7 100644
--- a/core/src/test/java/org/apache/commons/digester3/TestRuleSet.java
+++ b/core/src/test/java/org/apache/commons/digester3/TestRuleSet.java
@@ -1,100 +1,98 @@
-/* $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.digester3;
-
-/**
- * RuleSet that mimics the rules set used for Employee and Address creation, optionally associated with a particular
- * namespace URI.
- *
- * @author Craig R. McClanahan
- */
-public class TestRuleSet
-    extends RuleSetBase
-{
-
-    // ----------------------------------------------------------- Constructors
-
-    /**
-     * Construct an instance of this RuleSet with default values.
-     */
-    public TestRuleSet()
-    {
-
-        this( null, null );
-
-    }
-
-    /**
-     * Construct an instance of this RuleSet associated with the specified prefix, associated with no namespace URI.
-     *
-     * @param prefix Matching pattern prefix (must end with '/') or null.
-     */
-    public TestRuleSet( final String prefix )
-    {
-
-        this( prefix, null );
-
-    }
-
-    /**
-     * Construct an instance of this RuleSet associated with the specified prefix and namespace URI.
-     *
-     * @param prefix Matching pattern prefix (must end with '/') or null.
-     * @param namespaceURI The namespace URI these rules belong to
-     */
-    public TestRuleSet( final String prefix, final String namespaceURI )
-    {
-
-        super(namespaceURI);
-        if ( prefix == null )
-        {
-            this.prefix = "";
-        }
-        else
-        {
-            this.prefix = prefix;
-        }
-
-    }
-
-    // ----------------------------------------------------- Instance Variables
-
-    /**
-     * The prefix for each matching pattern added to the Digester instance, or an empty String for no prefix.
-     */
-    protected String prefix = null;
-
-    // --------------------------------------------------------- Public Methods
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void addRuleInstances( final Digester digester )
-    {
-
-        digester.addObjectCreate( prefix + "employee", Employee.class );
-        digester.addSetProperties( prefix + "employee" );
-        digester.addObjectCreate( "employee/address", "org.apache.commons.digester3.Address" );
-        digester.addSetProperties( prefix + "employee/address" );
-        digester.addSetNext( prefix + "employee/address", "addAddress" );
-
-    }
-
-}
+/* $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.digester3;
+
+/**
+ * RuleSet that mimics the rules set used for Employee and Address creation, optionally associated with a particular
+ * namespace URI.
+ */
+public class TestRuleSet
+    extends RuleSetBase
+{
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Construct an instance of this RuleSet with default values.
+     */
+    public TestRuleSet()
+    {
+
+        this( null, null );
+
+    }
+
+    /**
+     * Construct an instance of this RuleSet associated with the specified prefix, associated with no namespace URI.
+     *
+     * @param prefix Matching pattern prefix (must end with '/') or null.
+     */
+    public TestRuleSet( final String prefix )
+    {
+
+        this( prefix, null );
+
+    }
+
+    /**
+     * Construct an instance of this RuleSet associated with the specified prefix and namespace URI.
+     *
+     * @param prefix Matching pattern prefix (must end with '/') or null.
+     * @param namespaceURI The namespace URI these rules belong to
+     */
+    public TestRuleSet( final String prefix, final String namespaceURI )
+    {
+
+        super(namespaceURI);
+        if ( prefix == null )
+        {
+            this.prefix = "";
+        }
+        else
+        {
+            this.prefix = prefix;
+        }
+
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The prefix for each matching pattern added to the Digester instance, or an empty String for no prefix.
+     */
+    protected String prefix = null;
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * {@inheritDoc}
... 1550 lines suppressed ...