You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by sk...@apache.org on 2005/02/04 02:51:54 UTC

svn commit: r151287 [2/7] - in jakarta/commons/proper/digester/branches/digester2: ./ src/ src/conf/ src/examples/ src/examples/api/ src/examples/api/addressbook/ src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/commons/ src/java/org/apache/commons/digester2/ src/java/org/apache/commons/digester2/actions/ src/java/org/apache/commons/digester2/factory/ src/media/ src/test/ src/test/org/ src/test/org/apache/ src/test/org/apache/commons/ src/test/org/apache/commons/digester2/ xdocs/ xdocs/dtds/ xdocs/images/ xdocs/style/

Added: jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/build.xml
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/build.xml?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/build.xml (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/build.xml Thu Feb  3 17:51:43 2005
@@ -0,0 +1,127 @@
+<!--
+ Copyright 2004 The Apache Software Foundation.
+  
+ Licensed 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.
+-->
+
+<project name="Example-AddressBook" default="compile" basedir=".">
+
+
+<!-- ========== Initialize Properties ===================================== -->
+
+
+  <property file="build.properties"/>                <!-- Component local   -->
+  <property file="../build.properties"/>             <!-- examples/api local-->
+  <property file="../../../../build.properties"/>    <!-- Digester local     -->
+  <property file="../../../../../build.properties"/> <!-- Commons local     -->
+  <property file="${user.home}/build.properties"/>   <!-- User local        -->
+
+
+<!-- ========== External Dependencies ===================================== -->
+
+
+  <!-- The directories corresponding to your necessary dependencies -->
+  <property name="jaxp.home"               value="/usr/local/jaxp1.1"/>
+  <property name="commons.home"            value="../../../../.."/>
+  <property name="beanutils.home"          value="${commons.home}/beanutils"/>
+  <property name="collections.home"        value="${commons.home}/collections"/>
+  <property name="logging.home"            value="${commons.home}/logging"/>
+  <property name="digester.home"            value="${commons.home}/digester"/>
+
+
+<!-- ========== Derived Values ============================================ -->
+
+
+  <!-- The locations of necessary jar files -->
+  <property name="jaxp.jaxp.jar"           value="${jaxp.home}/jaxp.jar"/>
+  <property name="jaxp.parser.jar"         value="${jaxp.home}/crimson.jar"/>
+  <property name="commons-beanutils.jar"   value="${beanutils.home}/dist/commons-beanutils.jar"/>
+  <property name="commons-collections.jar" value="${collections.home}/dist/commons-collections.jar"/>
+  <property name="commons-logging.jar"     value="${logging.home}/dist/commons-logging.jar"/>
+  <property name="commons-digester.jar"     value="${digester.home}/dist/commons-digester.jar"/>
+
+
+<!-- ========== Component Declarations ==================================== -->
+
+  <!-- The name of this component -->
+  <property name="component.name"          value="addressbook"/>
+
+
+<!-- ========== Compiler Defaults ========================================= -->
+
+  <!-- Should Java compilations set the 'debug' compiler option? -->
+  <property name="compile.debug"           value="true"/>
+
+  <!-- Should Java compilations set the 'deprecation' compiler option? -->
+  <property name="compile.deprecation"     value="false"/>
+
+  <!-- Should Java compilations set the 'optimize' compiler option? -->
+  <property name="compile.optimize"        value="true"/>
+
+  <!-- Construct compile classpath -->
+  <path id="compile.classpath">
+    <pathelement location="."/>
+    <pathelement location="${jaxp.jaxp.jar}"/>
+    <pathelement location="${jaxp.parser.jar}"/>
+    <pathelement location="${commons-beanutils.jar}"/>
+    <pathelement location="${commons-collections.jar}"/>
+    <pathelement location="${commons-logging.jar}"/>
+    <pathelement location="${commons-digester.jar}"/>
+    <pathelement location="${log4j.jar}"/>
+  </path>
+
+
+<!-- ========== Executable Targets ======================================== -->
+
+
+  <target name="compile">
+    <javac  srcdir="."
+           destdir="."
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}">
+      <classpath refid="compile.classpath"/>
+    </javac>
+  </target>
+
+
+  <target name="clean">
+    <delete>
+      <fileset dir="." includes="*.class"/>
+    </delete>
+    <delete dir="docs"/>
+  </target>
+
+  <target name="all" depends="clean,compile"/>
+
+  <target name="javadoc" depends="compile">
+    <mkdir      dir="docs"/>
+    <javadoc destdir="docs"
+                 author="true"
+                private="true"
+                version="true">
+      <classpath  refid="compile.classpath"/>
+      <fileset dir="." includes="*.java"/>
+    </javadoc>
+  </target>
+
+  <target name="run" depends="compile">
+    <java classname="Main" fork="yes">
+      <arg value="example.xml"/>
+      <classpath refid="compile.classpath"/>
+      <classpath>
+        <pathelement location="."/>
+      </classpath>
+    </java>
+  </target>
+</project>

Added: jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/example.xml
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/example.xml?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/example.xml (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/example.xml Thu Feb  3 17:51:43 2005
@@ -0,0 +1,29 @@
+<!--
+ Copyright 2004 The Apache Software Foundation.
+  
+ Licensed 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.
+-->
+
+<address-book>
+  <person id="1" category="acquaintance">
+    <name>Gonzo</name>
+    <email type="business">gonzo@muppets.com</email>
+  </person>
+
+  <person id="2" category="rolemodel">
+    <name>Kermit</name>
+    <email type="business">kermit@muppets.com</email>
+    <email type="home">kermie@acme.com</email>
+  </person>
+
+</address-book>
\ No newline at end of file

Added: jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/log4j.xml
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/log4j.xml?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/log4j.xml (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/log4j.xml Thu Feb  3 17:51:43 2005
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration>
+        <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+           <layout class="org.apache.log4j.PatternLayout">
+             <param name="ConversionPattern"
+                    value="aa:%p#%d#%c#%m%n"/>
+           </layout>
+        </appender>
+
+        <category name="org.apache.log4j.xml">
+          <priority value="INFO" />
+        </category>
+
+        <root>
+           <priority value = "DEBUG2"/>
+           <appender-ref ref="STDOUT" />
+        </root>
+</log4j:configuration>
+

Added: jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/readme.txt
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/readme.txt?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/readme.txt (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/examples/api/addressbook/readme.txt Thu Feb  3 17:51:43 2005
@@ -0,0 +1,49 @@
+#########################################################################
+# Copyright 2004 The Apache Software Foundation.
+#  
+# Licensed 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.
+#########################################################################
+
+== overview
+
+The files in this directory are intended as an example of how to use
+the Apache Digester's basic functionality via its java interface.
+
+Topics covered:
+* how to create a digester instance
+* how to parse a file
+* how to use the "object create" rule to create java objects
+* how to use the "set properties" rule (basic usage) to map xml attributes
+  to java bean properties.
+* how to use the "set next" rule to build trees of java objects.
+* how to use the "call method rule" (basic usage)
+* how to use the "call parameter rule" to process the text contained
+  in a tag's body
+* how to use the "call parameter rule" to process the contents of an 
+  xml attribute.
+
+== compiling and running
+
+First rename the build.properties.sample file in the parent directory
+to build.properties and edit it to suit your environment. Then in this
+directory:
+
+* to compile:
+  ant compile
+
+* to run:
+  ant run
+
+Alternatively, you can set up your CLASSPATH appropriately, and
+run the example directly. See the build.properties and build.xml
+files for details.

Added: jakarta/commons/proper/digester/branches/digester2/src/examples/api/build.properties
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/examples/api/build.properties?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/examples/api/build.properties (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/examples/api/build.properties Thu Feb  3 17:51:43 2005
@@ -0,0 +1,33 @@
+#####################################################
+# Copyright 2004 The Apache Software Foundation.
+#  
+# Licensed 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.
+####################################################
+
+# 
+# Example build properties
+# 
+# Copy this to build.properties and then edit the properties to 
+# provide local references.
+#
+
+
+myjars=/home/simon/jar
+
+commons-beanutils.jar=${myjars}/commons-beanutils.jar
+commons-collections.jar=${myjars}/commons-collections.jar
+commons-logging.jar=${myjars}/commons-logging.jar
+commons-digester.jar=/home/simon/digester2/target/commons-digester-1.6.jar
+log4j.jar=${myjars}/log4j.jar
+
+

Added: jakarta/commons/proper/digester/branches/digester2/src/examples/api/build.xml
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/examples/api/build.xml?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/examples/api/build.xml (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/examples/api/build.xml Thu Feb  3 17:51:43 2005
@@ -0,0 +1,98 @@
+<!--
+ Copyright 2004 The Apache Software Foundation.
+  
+ Licensed 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.
+-->
+<!-- 
+*******************************************************
+API Examples Script
+===================
+Provides helpful services to get folks up and running
+the examples quickly.
+
+* Compile - compiles all the examples to the directories
+in which the their source files reside.
+
+* Run - runs each example in turn. 
+
+Note: 
+These examples will only build and run if the 
+rights jars are in the classpath. The classpath
+can be set by copying the build.properties.sample file
+in this directory to build.properties and then setting
+the properties appropriately. Or by editing the 
+build.properties file (based on the build.properties.sample
+file in CVS) so that it contains absolute paths.
+
+Note:
+build.xml files are provided in each subdirectory. This
+provide similar fuctionality for each example individually.
+
+Note:
+The examples are graduated. It is best to look at them 
+in order. Please consult the documentation for more details.
+
+*******************************************************
+-->
+<project name="Examples" default="about" basedir=".">
+    
+  <target name='about'>
+    <echo>
+*******************************************************
+API Examples Script
+===================
+Provides helpful services to get folks up and running
+the examples quickly.
+
+* Compile - compiles all the examples to the directories
+in which the their source files reside.
+
+* Run - runs each example in turn. 
+
+Note: 
+These examples will only build and run if the 
+rights jars are in the classpath. The classpath
+can be set by copying the build.properties.sample file
+in this directory to build.properties and then setting
+the properties appropriately. Or by editing the 
+build.properties file (based on the build.properties.sample
+file in CVS) so that it contains absolute paths.
+
+Note:
+build.xml files are provided in each subdirectory. This
+provide similar fuctionality for each example individually.
+
+Note:
+The examples are graduated. It is best to look at them 
+in order. Please consult the documentation for more details.
+
+*******************************************************
+    </echo>
+  </target>
+
+  <target name="compile">
+    <ant dir="addressbook" target="compile"/>
+    <ant dir="catalog" target="compile"/>
+  </target>
+
+  <target name="run" depends="compile">
+    <ant dir="addressbook" target="compile"/>
+    <ant dir="catalog" target="compile"/>
+  </target>
+  
+  <target name="clean">
+    <ant dir="addressbook" target="clean"/>
+    <ant dir="catalog" target="clean"/>
+  </target>
+
+</project>

Added: jakarta/commons/proper/digester/branches/digester2/src/examples/api/readme.txt
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/examples/api/readme.txt?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/examples/api/readme.txt (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/examples/api/readme.txt Thu Feb  3 17:51:43 2005
@@ -0,0 +1,32 @@
+#########################################################################
+# Copyright 2004 The Apache Software Foundation.
+#  
+# Licensed 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.
+#########################################################################
+
+The subdirectories of this directory provide examples of how to use
+the Apache Digester's java API.
+
+With the API approach, java code is used to configure the digester with
+a set of rules to execute when xml is processed. It is these rules that
+determine how the input xml is mapped into a tree of java objects.
+
+An alternative is to use the "xmlrules" digester extension, which allows
+the digester rules to be configured via an xml file. This allows the
+mapping between input xml and java objects to be modified without
+recompilation of any source code.
+
+The examples are graduated in the following order:
+
+1. addressbook
+2. catalog
\ No newline at end of file

Added: jakarta/commons/proper/digester/branches/digester2/src/examples/readme.txt
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/examples/readme.txt?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/examples/readme.txt (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/examples/readme.txt Thu Feb  3 17:51:43 2005
@@ -0,0 +1,10 @@
+The subdirectories of this directory divide the examples into topics.
+
+The examples in API deal with the main Digester API. 
+
+The xmlrules directory deals with the xmlrules extension which allows
+the digester rules to be configured via an xml file. This allows the
+mapping between input xml and java objects to be modified without
+recompilation of any source code.
+
+

Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/AbstractAction.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/AbstractAction.java?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/AbstractAction.java (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/AbstractAction.java Thu Feb  3 17:51:43 2005
@@ -0,0 +1,129 @@
+/* $Id: $
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.digester2;
+
+import org.xml.sax.Attributes;
+
+/**
+ * Provides a base implementation for custom actions (ie things that
+ * should be executed when certain input xml is seen).
+ * <p>
+ * Note that extending this abstract class rather than directly implementing
+ * the Action interface provides much better "forward compatibility". Digester
+ * minor releases (2.x -> 2.y) guarantee not to break any classes that subclass
+ * this abstract class. However no such guarantee exists for classes that
+ * directly implement the Action interface.
+ * <p>
+ * You <strong>must</strong> read the comments on the Action interface before
+ * implementing any subclass of this class, as there are constraints on the
+ * permitted behaviour of Action classes. 
+ */
+
+public abstract class AbstractAction implements Action {
+
+    /**
+     * This method is called once at the start of parsing of an input
+     * document. The context object is the one that will be used during
+     * the parsing of this document.
+     */
+    public void startParse(Context context) 
+    throws ParseException {
+    }
+    
+   /**
+     * This method is called when the beginning of a matching XML element
+     * is encountered.
+     *
+     * @param context is the current processing context object.
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the element has no namespace
+     * @param name the local name of the element
+     * @param attributes The attribute list of this element
+     */
+    public void begin(Context context, String namespace, String name, Attributes attributes)
+    throws ParseException {
+    }
+
+    /**
+     * This method is called when the body of a matching XML element is 
+     * encountered.  If the element has no body, this method is not called at 
+     * all.
+     * <p>
+     * Note that if the element has multiple pieces of body text separated by
+     * child elements (ie is "mixed content") then this method is called once
+     * for each separate block of text, at the point that the child element
+     * is encountered. In each call, only the text since the last call to this
+     * method (ie since the last nested child element) is passed.
+     * <p>
+     * In the case of an element with just text content (no child elements),
+     * this method is exactly equivalent to the body method; either can be
+     * overridden to perform the necessary work.
+     *
+     * @param context is the current processing context object.
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the element has no namespace
+     * @param name the local name of the element.
+     * @param text The text of the body of this element
+     */
+    public void bodySegment(Context context, String namespace, String name, String text)
+    throws ParseException {
+    }
+
+    /**
+     * This method is called when the body of a matching XML element is 
+     * encountered.  If the element has no body, this method is not called at 
+     * all.
+     * <p>
+     * Note that if the element has multiple pieces of body text separated by
+     * child elements (ie is "mixed content") then all the text is merged
+     * into a single string and this method is called only once with the
+     * complete text as a parameter.
+     *
+     * @param context is the current processing context object.
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the element has no namespace
+     * @param name the local name of the element.
+     * @param text The text of the body of this element
+     */
+    public void body(Context context, String namespace, String name, String text)
+    throws ParseException {
+    }
+
+    /**
+     * This method is called when the end of a matching XML element
+     * is encountered.
+     *
+     * @param context is the current processing context object.
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the element has no namespace
+     * @param name the local name of the element.
+     */
+    public void end(Context context, String namespace, String name)
+    throws ParseException {
+    }
+
+    /**
+     * This method is called after all parsing methods have been
+     * called, to allow Actions to remove temporary data.
+     *
+     * @param context is the current processing context object.
+     */
+    public void finishParse(Context context) 
+    throws ParseException {
+    }
+}

Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Action.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Action.java?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Action.java (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Action.java Thu Feb  3 17:51:43 2005
@@ -0,0 +1,139 @@
+/* $Id: $
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.digester2;
+
+import org.xml.sax.Attributes;
+
+/**
+ * Defines the interface required of classes which implement actions to be 
+ * taken when a corresponding nested pattern of XML elements has been matched.
+ * <p>
+ * Classes implementing the Action interface <i>must</i> not alter any instance
+ * variables during parsing (ie from the init/begin/body/end/finish methods).
+ * All necessary state-related data <i>must</i> be stored instead on the Context
+ * object passed as a parameter to each parsing-related method.
+ * <p>
+ * An Action instance may be added to the same RuleManager more than once, with 
+ * different associated patterns, or the pattern may include wildcards; either 
+ * of these situations may cause the rule to be invoked in a "re-entrant" 
+ * manner, where the pattern of calls is like:
+ * <pre>
+ *    begin[1]/begin[2]/body[2]/end[2]/body[1]/end[1]
+ * </pre>
+ * This obviously will cause errors if the Action object stores state on itself. 
+ * <p>
+ * In addition, an Action object may be added to different Digester instances,
+ * which potentially are parsing different documents concurrently in
+ * different threads. Again, storing state on the Action instance itself will
+ * cause problems in these situations.
+ * <p>
+ * <strong>IMPORTANT NOTE</strong>: Anyone implementing a custom Action is
+ * strongly encouraged to subclass AbstractAction rather than implement this
+ * interface directly. Digester minor releases (2.x -> 2.y) guarantee that
+ * subclasses of AbstractAction will not be broken. However the Action
+ * interface <i>may</i> change in minor releases, which will break any class
+ * which implements this interface directly.
+ */
+
+public interface Action {
+
+    /**
+     * This method is called once at the start of parsing of an input
+     * document. The context object is the one that will be used during
+     * the parsing of this document.
+     */
+    public void startParse(Context context) throws ParseException;
+    
+   /**
+     * This method is called when the beginning of a matching XML element
+     * is encountered.
+     *
+     * @param context is the current processing context object.
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the element has no namespace
+     * @param name the local name of the element
+     * @param attributes The attribute list of this element
+     */
+    public void begin(
+    Context context, String namespace, 
+    String name, Attributes attributes)
+    throws ParseException;
+
+    /**
+     * This method is called when the body of a matching XML element is 
+     * encountered.  If the element has no body, this method is not called at 
+     * all.
+     * <p>
+     * Note that if the element has multiple pieces of body text separated by
+     * child elements (ie is "mixed content") then this method is called once
+     * for each separate block of text, at the point that the child element
+     * is encountered. In each call, only the text since the last call to this
+     * method is passed.
+     * <p>
+     * In the case of an element with just text content (no child elements),
+     * this method is exactly equivalent to the body method; either can be
+     * overridden to perform the necessary work.
+     *
+     * @param context is the current processing context object.
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the element has no namespace
+     * @param name the local name of the element.
+     * @param text The text of the body of this element
+     */
+    public void bodySegment(Context context, String namespace, String name, String text)
+    throws ParseException;
+
+    /**
+     * This method is called when the body of a matching XML element is 
+     * encountered.  If the element has no body, this method is not called at 
+     * all.
+     * <p>
+     * Note that if the element has multiple pieces of body text separated by
+     * child elements (ie is "mixed content") then all the text is merged
+     * into a single string and this method is called only once with the
+     * complete text as a parameter.
+     *
+     * @param context is the current processing context object.
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the element has no namespace
+     * @param name the local name of the element.
+     * @param text The text of the body of this element
+     */
+    public void body(Context context, String namespace, String name, String text)
+    throws ParseException;
+
+    /**
+     * This method is called when the end of a matching XML element
+     * is encountered.
+     *
+     * @param context is the current processing context object.
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the element has no namespace
+     * @param name the local name of the element.
+     */
+    public void end(Context context, String namespace, String name)
+    throws ParseException;
+
+    /**
+     * This method is called after all parsing methods have been
+     * called, to allow Actions to remove temporary data.
+     *
+     * @param context is the current processing context object.
+     */
+    public void finishParse(Context context) throws ParseException;
+}

Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/ArrayStack.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/ArrayStack.java?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/ArrayStack.java (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/ArrayStack.java Thu Feb  3 17:51:43 2005
@@ -0,0 +1,170 @@
+/*
+ *  Copyright 2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.digester2;
+
+import java.util.ArrayList;
+import java.util.EmptyStackException;
+
+/**
+ * An implementation of the {@link java.util.Stack} API that is based on an
+ * <code>ArrayList</code> instead of a <code>Vector</code>, so it is not
+ * synchronized to protect against multi-threaded access.  The implementation
+ * is therefore operates faster in environments where you do not need to
+ * worry about multiple thread contention.
+ * <p>
+ * The removal order of an <code>ArrayStack</code> is based on insertion 
+ * order: The most recently added element is removed first.  The iteration
+ * order is <i>not</i> the same as the removal order.  The iterator returns
+ * elements from the bottom up, whereas the inherited ArrayList.remove() method 
+ * removes them from the top down.
+ * <p>
+ * Unlike <code>Stack</code>, <code>ArrayStack</code> accepts null entries.
+ * <p>
+ * This code is copied from the Apache Jakarta Commons collections library,
+ * version 3.1, with minor modifications.
+ *
+ * @see java.util.Stack
+ * @since Commons Collections 1.0
+ * @version $Revision: 1.17 $ $Date: 2004/02/18 01:15:42 $
+ * 
+ * @author Craig R. McClanahan
+ * @author Paul Jack
+ * @author Stephen Colebourne
+ */
+public class ArrayStack extends ArrayList {
+
+    /** Ensure serialization compatibility */    
+    private static final long serialVersionUID = 2130079159931574599L;
+
+    /**
+     * Constructs a new empty <code>ArrayStack</code>. The initial size
+     * is controlled by <code>ArrayList</code> and is currently 10.
+     */
+    public ArrayStack() {
+        super();
+    }
+
+    /**
+     * Constructs a new empty <code>ArrayStack</code> with an initial size.
+     * 
+     * @param initialSize  the initial size to use
+     * @throws IllegalArgumentException  if the specified initial size
+     *  is negative
+     */
+    public ArrayStack(int initialSize) {
+        super(initialSize);
+    }
+
+    /**
+     * Return <code>true</code> if this stack is currently empty.
+     * <p>
+     * This method exists for compatibility with <code>java.util.Stack</code>.
+     * New users of this class should use <code>isEmpty</code> instead.
+     * 
+     * @return true if the stack is currently empty
+     */
+    public boolean empty() {
+        return isEmpty();
+    }
+
+    /**
+     * Returns the top item off of this stack without removing it.
+     *
+     * @return the top item on the stack
+     * @throws EmptyStackException  if the stack is empty
+     */
+    public Object peek() throws EmptyStackException {
+        int n = size();
+        if (n <= 0) {
+            throw new EmptyStackException();
+        } else {
+            return get(n - 1);
+        }
+    }
+
+    /**
+     * Returns the n'th item down (zero-relative) from the top of this
+     * stack without removing it.
+     *
+     * @param n the number of items down to go
+     * @return the n'th item on the stack, zero relative
+     * @throws EmptyStackException  if there are not enough items on the
+     *  stack to satisfy this request
+     * @throws ArrayOutOfBoundsException if n is < 0
+     */
+    public Object peek(int n) 
+    throws EmptyStackException, IndexOutOfBoundsException {
+        int m = (size() - n) - 1;
+        if (m < 0) {
+            throw new EmptyStackException();
+        } else {
+            return get(m);
+        }
+    }
+
+    /**
+     * Pops the top item off of this stack and return it.
+     *
+     * @return the top item on the stack
+     * @throws EmptyStackException  if the stack is empty
+     */
+    public Object pop() throws EmptyStackException {
+        int n = size();
+        if (n <= 0) {
+            throw new EmptyStackException();
+        } else {
+            return remove(n - 1);
+        }
+    }
+
+    /**
+     * Pushes a new item onto the top of this stack. The pushed item is also
+     * returned. This is equivalent to calling <code>add</code>.
+     *
+     * @param item  the item to be added
+     * @return the item just pushed
+     */
+    public Object push(Object item) {
+        add(item);
+        return item;
+    }
+
+    /**
+     * Returns the one-based position of the distance from the top that the
+     * specified object exists on this stack, where the top-most element is
+     * considered to be at distance <code>1</code>.  If the object is not
+     * present on the stack, return <code>-1</code> instead.  The
+     * <code>equals()</code> method is used to compare to the items
+     * in this stack.
+     *
+     * @param object  the object to be searched for
+     * @return the 1-based depth into the stack of the object, or -1 if not found
+     */
+    public int search(Object object) {
+        int i = size() - 1;        // Current index
+        int n = 1;                 // Current distance
+        while (i >= 0) {
+            Object current = get(i);
+            if ((object == null && current == null) ||
+                (object != null && object.equals(current))) {
+                return n;
+            }
+            i--;
+            n++;
+        }
+        return -1;
+    }
+}

Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Context.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Context.java?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Context.java (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Context.java Thu Feb  3 17:51:43 2005
@@ -0,0 +1,477 @@
+/* $Id: $
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * Licensed 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.digester2;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.reflect.InvocationTargetException;
+import java.util.EmptyStackException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.collections.ArrayStack;
+
+
+/**
+ * <p>Holds information that evolves as the parsing of an input xml document
+ * progresses. The Action objects regularly interact with this object when their
+ * various methods fire as a result of their associated patterns being matched
+ * by input data. </p>
+ *
+ * <p>A new Context object is created for each document parsed, to ensure that
+ * state left over from a previous parse does not affect later parses. It
+ * is the SAXHandler class that creates the Context instances.</p>
+ *
+ * <p>This class does not hold information that is directly related to
+ * handling sax parsing events.</p>
+ */
+
+public class Context {
+
+    // --------------------------------------------------------- Constructors
+
+    /**
+     * Construct a new Context.
+     */
+    public Context(SAXHandler saxHandler, Log log) {
+        this.saxHandler = saxHandler;
+        this.log = log;
+    }
+
+    // --------------------------------------------------- Instance Variables
+
+    /**
+     * The owner of this object.
+     */
+    private SAXHandler saxHandler = null;
+
+    /**
+     * An object through which log messages can be issued. Note that this
+     * is equivalent to saxHandler.getLogger(), and a direct reference is
+     * kept here only for performance.
+     */
+    private Log log;
+
+    /**
+     * The owner of the set of rules (pattern, action pairs).
+     */
+    private RuleManager ruleManager = null;
+
+    /**
+     * Stack whose elements are List objects, each containing a list of
+     * Action objects as returned from RuleManager.getMatchingActions().
+     * As each xml element in the input is entered, the list of matching
+     * actions is pushed onto this stack. After the end tag is reached, the
+     * matches are popped again. The depth of is stack is therefore exactly
+     * the same as the current "nesting" level of the input xml.
+     */
+    private ArrayStack matchedActionLists = new ArrayStack(10);
+
+    /**
+     * The path to the xml element for which Actions are currently being fired.
+     */
+    private Path currentElementPath = new Path();
+
+    /**
+     * The parameters stack being utilized by CallMethodAction and
+     * CallParamAction.
+     */
+    private ArrayStack params = new ArrayStack();
+
+    /**
+     * The object that forms the root of the tree of objects being
+     * created during a parse. Note that if setRoot has been called, then
+     * this is the same as looking at the bottom element on the object stack.
+     * However when an Action has created the root object, then the root should
+     * be acessable even after the parse is complete (at which time the stack
+     * will be empty).
+     */
+    private Object root;
+
+    /**
+     * The object stack being constructed.
+     */
+    private ArrayStack stack = new ArrayStack();
+
+    /**
+     * Stacks used by Action objects to store internal state.
+     * They can also be used for inter-action communication.
+     *
+     * By convention, Action instances use their class name as the key
+     * (or as a key prefix) to this map.
+     */
+    private HashMap stacksByName = new HashMap();
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the current Logger associated with this instance of the Digester
+     */
+    public Log getLogger() {
+        return log;
+    }
+
+    /**
+     * Return the path to the xml element currently being processed.
+     */
+    public String getMatchPath() {
+        return currentElementPath.getPath();
+    }
+
+    /**
+     * Enter a new element.
+     */
+    public String pushMatchPath(String namespace, String elementName) {
+        currentElementPath.push(namespace, elementName);
+        return currentElementPath.getPath();
+    }
+
+    /**
+     * Leave an element; restore the previous match path.
+     */
+    public String popMatchPath() {
+        currentElementPath.pop();
+        return currentElementPath.getPath();
+    }
+
+    public void pushMatchingActions(List actions) {
+        matchedActionLists.push(actions);
+    }
+
+    public List popMatchingActions() {
+        return (List) matchedActionLists.pop();
+    }
+
+    public List peekMatchingActions() {
+        return (List) matchedActionLists.peek();
+    }
+
+    public ClassLoader getClassLoader() {
+        return saxHandler.getClassLoader();
+    }
+
+    public SAXHandler getSAXHandler() {
+        return saxHandler;
+    }
+
+    // --------------------------------------------------- Object Stack Methods
+
+    /**
+     * The root object of the Object stack.
+     */
+    public void setRoot(Object o) {
+        stack.clear(); // just in case setRoot called multiple times
+        stack.push(o);
+        root = o;
+    }
+
+    /**
+     * <p>Is the object stack empty?</p>
+     *
+     * @return true if the object stack if empty
+     */
+    public boolean isEmpty() {
+        return stack.isEmpty();
+    }
+
+    /**
+     * Return the current depth of the object stack.
+     */
+    public int getStackSize() {
+        return stack.size();
+    }
+
+    /**
+     * Return the top object on the stack without removing it.
+     *
+     * @throws EmptyStackException (a RuntimeException subclass) if the stack
+     * is empty. Note that all the Digester.parse methods will turn this into
+     * a (checked) DigestionException.
+     */
+    public Object peek() throws EmptyStackException {
+        return stack.peek();
+    }
+
+    /**
+     * Return the n'th object down the stack, where 0 is the top element
+     * and [getStackSize()-1] is the bottom element.
+     *
+     * @param n Index of the desired element, where 0 is the top of the stack,
+     *  1 is the next element down, and so on.
+     *
+     * @throws EmptyStackException (a RuntimeException subclass) if the index
+     * is out-of-range. Note that all the Digester.parse methods will turn this
+     * into a (checked) DigestionException.
+     *
+     * @throws ArrayOutOfBoundsException (a RuntimeException subclass) if 
+     * index < 0. Note that all the Digester.parse methods will turn this
+     * into a (checked) DigestionException.
+     */
+    public Object peek(int n) 
+    throws EmptyStackException, IndexOutOfBoundsException {
+            return stack.peek(n);
+    }
+
+    /**
+     * Pop the top object off of the stack, and return it.  
+     *
+     * @throws EmptyStackException (a RuntimeException subclass) if the stack
+     * is empty. Note that all the Digester.parse methods will turn this into
+     * a (checked) DigestionException.
+     */
+    public Object pop() throws EmptyStackException {
+            return stack.pop();
+    }
+
+    /**
+     * Push a new object onto the top of the object stack.
+     *
+     * @param object The new object
+     */
+    public void push(Object object) {
+        if (stack.isEmpty()) {
+            root = object;
+        }
+        stack.push(object);
+    }
+
+    /**
+     * <p>Is the stack with the given name empty?</p>
+     * <p><strong>Note:</strong> a stack is considered empty
+     * if no objects have been pushed onto it yet.</p>
+     *
+     * @param stackName the name of the stack whose emptiness
+     * should be evaluated
+     *
+     * @return true if the given stack if empty
+     */
+    public boolean isEmpty(String stackName) {
+        boolean result = true;
+        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
+        if (namedStack != null ) {
+            result = namedStack.isEmpty();
+        }
+        return result;
+    }
+
+    /**
+     * Return the current depth of the specified stack.
+     *
+     * @param stackName the name of the stack to be peeked
+     */
+    public int getStackSize(String stackName) {
+        boolean result = true;
+        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
+        if (namedStack != null ) {
+            return namedStack.size();
+        }
+        return 0;
+    }
+
+    /**
+     * <p>Gets the top object from the stack with the given name.
+     * This method does not remove the object from the stack.
+     * </p>
+     * <p><strong>Note:</strong> a stack is considered empty
+     * if no objects have been pushed onto it yet.</p>
+     *
+     * @param stackName the name of the stack to be peeked
+     * @return the top <code>Object</code> on the stack.
+     * @throws EmptyStackException if the named stack is empty
+     */
+    public Object peek(String stackName) throws EmptyStackException {
+        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
+        if (namedStack == null ) {
+            if (log.isDebugEnabled()) {
+                log.debug("Stack '" + stackName + "' is empty");
+            }
+            throw new EmptyStackException();
+        } else {
+            return namedStack.peek();
+        }
+    }
+
+    /**
+     * Returns an element from the stack with the given name. The element
+     * returned is the n'th object down the stack, where 0 is the top element
+     * and [getStackSize()-1] is the bottom element.
+     *
+     * <p><strong>Note:</strong> a stack is considered empty
+     * if no objects have been pushed onto it yet.</p>
+     *
+     * @param stackName the name of the stack to be peeked
+     *
+     * @param n Index of the desired element, where 0 is the top of the stack,
+     *  1 is the next element down, and so on.
+     *
+     * @throws EmptyStackException (a RuntimeException subclass) if the index
+     * is out-of-range. Note that all the Digester.parse methods will turn this
+     * into a (checked) DigestionException.
+     *
+     * @throws ArrayOutOfBoundsException (a RuntimeException subclass) if 
+     * index < 0. Note that all the Digester.parse methods will turn this
+     * into a (checked) DigestionException.
+     */
+    public Object peek(String stackName, int n) 
+    throws EmptyStackException, IndexOutOfBoundsException {
+        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
+        if (namedStack == null ) {
+            if (log.isDebugEnabled()) {
+                log.debug("Stack '" + stackName + "' is empty");
+            }
+            throw new EmptyStackException();
+        } else {
+            return namedStack.peek(n);
+        }
+    }
+
+    /**
+     * <p>Pops (gets and removes) the top object from the stack with the 
+     * given name.</p>
+     *
+     * <p><strong>Note:</strong> a stack is considered empty
+     * if no objects have been pushed onto it yet.</p>
+     *
+     * @param stackName the name of the stack from which the top value is to 
+     * be popped
+     *
+     * @return the top <code>Object</code> on the stack.
+     *
+     * @throws EmptyStackException if the named stack is empty, or has not 
+     * yet been created.
+     */
+    public Object pop(String stackName) throws EmptyStackException {
+        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
+        if (namedStack == null) {
+            if (log.isDebugEnabled()) {
+                log.debug("Stack '" + stackName + "' is empty");
+            }
+            throw new EmptyStackException();
+        } else {
+            return namedStack.pop();
+        }
+    }
+
+    /**
+     * Pushes the given object onto the stack with the given name.
+     * If no stack already exists with the given name then one will be created.
+     *
+     * @param stackName the name of the stack onto which the object should be pushed
+     * @param value the Object to be pushed onto the named stack.
+     */
+    public void push(String stackName, Object value) {
+        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
+        if (namedStack == null) {
+            namedStack = new ArrayStack();
+            stacksByName.put(stackName, namedStack);
+        }
+        namedStack.push(value);
+    }
+
+    /**
+     * Returns the root element of the tree of objects created as a result
+     * of applying the action objects to the input XML.
+     * <p>
+     * If the digester stack was "primed" by explicitly calling setRoot before
+     * parsing started, then that root object is returned here.
+     * <p>
+     * Alternatively, if an Action which creates an object (eg ObjectCreateAction)
+     * matched the root element of the xml, then the object created will be
+     * returned here.
+     * <p>
+     * In other cases, the object most recently pushed onto an empty digester
+     * stack is returned. This would be a most unusual use of digester, however;
+     * one of the previous configurations is much more likely.
+     * <p>
+     * Note that when using one of the Digester.parse methods, the return
+     * value from the parse method is exactly the same as the return value
+     * from this method. However when the Digester's SAXHandler has been used
+     * explicitly as the content-handler for a user-provided sax parser, no
+     * such return value is available; in this case, this method allows you
+     * to access the root object that has been created after parsing has
+     * completed.
+     *
+     * @return the root object that has been created after parsing
+     *  or null if the digester has not parsed any XML yet.
+     */
+    public Object getRoot() {
+        return root;
+    }
+
+    /**
+     * <p>Return the top object on the parameters stack without removing it.</p>
+     *
+     * <p>The parameters stack is used to store <code>CallMethodAction</code>
+     * parameters. See {@link #params}.</p>
+     *
+     * @throws EmptyStackException if the parameters stack is empty.
+     */
+    public Object peekParams() throws EmptyStackException {
+        return params.peek();
+    }
+
+    /**
+     * <p>Return the n'th object down the parameters stack, where 0 is the 
+     * top element and [stacksize-1] is the bottom element.
+     *
+     * <p>The parameters stack is used to store <code>CallMethodAction</code> 
+     * parameters. See {@link #params}.</p>
+     *
+     * @param n Index of the desired element, where 0 is the top of the stack,
+     *  1 is the next element down, and so on.
+     *
+     * @throws EmptyStackException if the parameters stack is empty.
+     */
+    public Object peekParams(int n) throws EmptyStackException {
+        return params.peek(n);
+    }
+
+    /**
+     * <p>Pop the top object off of the parameters stack, and return it.</p>
+     *
+     * <p>The parameters stack is used to store <code>CallMethodAction</code>
+     * parameters. See {@link #params}.</p>
+     *
+     * @throws EmptyStackException if the parameters stack is empty.
+     */
+    public Object popParams() throws EmptyStackException {
+        return params.pop();
+    }
+
+    /**
+     * <p>Push a new object onto the top of the parameters stack.</p>
+     *
+     * <p>The parameters stack is used to store <code>CallMethodAction</code> 
+     * parameters. See {@link #params}.</p>
+     *
+     * @param object The new object
+     */
+    public void pushParams(Object object) {
+        params.push(object);
+
+    }
+}

Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/DefaultRuleManager.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/DefaultRuleManager.java?view=auto&rev=151287
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/DefaultRuleManager.java (added)
+++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/DefaultRuleManager.java Thu Feb  3 17:51:43 2005
@@ -0,0 +1,245 @@
+/* $Id: $
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.digester2;
+
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+
+/**
+ * <p>Default implementation of the <code>RuleManager</code> interface that supports
+ * the standard rule matching behavior.  This class can also be used as a
+ * base class for specialized <code>RuleManager</code> implementations.</p>
+ *
+ * <p>The matching policies implemented by this class support two different
+ * types of pattern matching rules:</p>
+ * <ul>
+ * <li><em>Exact Match</em> - A pattern "/a/b/c" exactly matches a
+ *     <code>&lt;c&gt;</code> element, nested inside a <code>&lt;b&gt;</code>
+ *     element, which is nested inside an <code>&lt;a&gt;</code> element.</li>
+ * <li><em>Tail Match</em> - A pattern "a/b" matches a
+ *     <code>&lt;b&gt;</code> element, nested inside an <code>&lt;a&gt;</code>
+ *      element, no matter how deeply the pair is nested.</li>
+ * </ul>
+ * <p>
+ * ActionManager objects cannot be shared between Digester objects; if you have
+ * multiple Digester objects then a different RuleManager object must be
+ * created for each one. However the copy() method may be used to duplicate
+ * an existing RuleManager instance. 
+ */
+
+public class DefaultRuleManager extends RuleManager {
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Map of namespace-prefix to namespace-uri, used only by the
+     * addAction() method.
+     */
+    private HashMap namespaces = new HashMap();
+    
+    /**
+     * The list of all actions in the cache. This set allows us to
+     * iterate over the set of actions to invoke the startParse/finishParse
+     * methods.
+     */
+    protected ArrayList actions = new ArrayList(20);
+    
+    /**
+     * Map of expanded-pattern -> list-of-actions, so that we can
+     * find the pattern that matches the current xml element, then
+     * return the list of actions.
+     */
+    private MultiHashMap rules = new MultiHashMap();
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Return a clone of this object. The Action objects currently
+     * registered are not copied, as Action objects are required to be
+     * re-entrant and thread-safe.
+     */
+    public RuleManager copy() {
+        DefaultRuleManager copy = new DefaultRuleManager();
+        copy.namespaces = (HashMap) this.namespaces.clone();
+        copy.actions = (ArrayList) this.actions.clone();
+        copy.rules = (MultiHashMap) this.rules.clone();
+        return copy;
+    }
+    
+    /**
+     * This method is called at the start of parsing a new input document.
+     * <p>
+     * TODO: build a mapping from element-name to "list of patterns ending
+     * in that element name". When we have to do leading-wildcard-matching,
+     * therefore, we can omit all the patterns that don't match the last
+     * element, which will be a massive optimisation over scanning the entire
+     * list of patterns. And we only need to compute this first time, or if
+     * new rules have been added in the meantime.
+     */
+    public void startParse(Context context) {
+    }
+    
+    /**
+     * This method is called after parsing of a new input document has completed.
+     * <p>
+     * Note that if an error has occurred during parsing, then this method
+     * might not be called.
+     */
+    public void finishParse(Context context) {
+    }
+    
+    /**
+     * When rules are added, the pattern is of form
+     * <pre>
+     *   /prefix:element/prefix:element/prefix:element....
+     * </pre>
+     * The prefixes must have previously been defined here.
+     */
+    public void addNamespace(String prefix, String uri) {
+        namespaces.put(prefix, uri);
+    }
+
+    /**
+     * Store the specified Action.
+     * <p>
+     * The pattern may include namespace-prefixes, in which case the prefixes
+     * are expanded to the corresponding namespace URIs using the mapping
+     * previously defined in the addNamespace method.
+     */
+    public void addRule(String pattern, Action action) 
+    throws InvalidRuleException {
+        // if pattern has namespaces, then convert them to URIs.
+        String path = patternToPath(namespaces, pattern);
+
+        // if the action isn't already in the unique-action list, add it
+        if (!actions.contains(action)) {
+            actions.add(action);
+        }
+        
+        // add the mapping to the multilist
+        rules.put(path, action);
+    }
+    
+    /**
+     * Given a string of form "prefix:name/prefix:name", return a string of
+     * form "{namespace-uri}name/{namespace-uri}/name".
+     */
+    public String patternToPath(Map namespaces, String pattern) 
+    throws InvalidRuleException {
+        int nsEndPos = pattern.indexOf(':');
+        if (nsEndPos == -1) {
+            /* no namespace prefixes present */
+            return pattern;
+        }
+        
+        int currPos = 0;
+        StringBuffer out = new StringBuffer();
+        while (nsEndPos != -1) {
+            int nsStartPos = pattern.lastIndexOf('/', nsEndPos);
+            String prefix = pattern.substring(nsStartPos+1, nsEndPos);
+            
+            String uri = (String) namespaces.get(prefix);
+            if (uri == null) {
+                throw new InvalidRuleException(
+                    "No namespace for prefix [" + prefix + "]");
+            }
+            
+            String prePrefix = pattern.substring(currPos, nsStartPos);
+            out.append(prePrefix);
+            
+            out.append('{');
+            out.append(uri);
+            out.append('}');
+            
+            currPos = nsEndPos + 1;
+            nsEndPos = pattern.indexOf(':', nsEndPos+1);
+        }
+        out.append(pattern.substring(currPos));
+        
+        return out.toString();
+    }
+
+    /**
+     * Return a List of all registered Action instances that match the specified
+     * path, or a zero-length List if there are no matches.  If more
+     * than one Action instance matches, they <strong>must</strong> be returned
+     * in the order originally registered through the <code>add()</code>
+     * method.
+     *
+     * @param path
+     */
+    public List getMatchingActions(String path) {
+        // assert path.startsWith('/');
+        List actionList = (List) rules.get(path);
+        
+        if ((actionList == null) || (actionList.size() < 1)) {
+            /*
+             * Ok, there is no absolute path that matches. So what we need to
+             * do now is iterate over all the available paths which are not 
+             * absolute, and see which is the longest match.
+             */
+
+            int longestMatch = -1;
+            int thisPathLength = path.length();
+            Iterator keys = this.rules.keySet().iterator();
+            while (keys.hasNext()) {
+                String key = (String) keys.next();
+                if (!key.startsWith("/")) {
+                    // yep, this is non-absolute
+                    int thisKeyLength = key.length();
+                    if ((thisKeyLength > longestMatch) && path.endsWith(key)) {
+                        
+                        // just check that we aren't trying to match a
+                        // pattern of "a/b" against a path of "/gotcha/b"!
+                        if (path.charAt(thisPathLength - thisKeyLength - 1) == '/') {
+                            actionList = (List) rules.get(key);
+                            longestMatch = thisKeyLength;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (actionList == null) {
+            return java.util.Collections.EMPTY_LIST;
+        }
+        else {
+            return actionList;
+        }
+    }
+
+
+    /**
+     * Return a List of all registered Action instances, or a zero-length List
+     * if there are no registered Action instances.
+     * <p>
+     * The rules are returned in the order they were added. If an Action
+     * instance has been added multiple times, then its order is set by the
+     * first time it was added.
+     */
+    public List actions() {
+        return this.actions;
+    }
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org