Author: skitching
Date: Sun Apr 30 03:19:12 2006
New Revision: 398306

URL: http://svn.apache.org/viewcvs?rev=398306&view=rev
Log:
Add new Digester.setStackAction feature; this allows user code to monitor the 
digester
stacks. In particular it helps with tracking info on xml file source for 
created objects.

Added:
    
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/StackAction.java
   (with props)
    
jakarta/commons/proper/digester/trunk/src/test/org/apache/commons/digester/LocationTrackerTestCase.java
   (with props)
Modified:
    
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/Digester.java
    
jakarta/commons/proper/digester/trunk/src/test/org/apache/commons/digester/DigesterTestCase.java

Modified: 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/Digester.java
URL: 
http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/Digester.java?rev=398306&r1=398305&r2=398306&view=diff
==============================================================================
--- 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/Digester.java
 (original)
+++ 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/Digester.java
 Sun Apr 30 03:19:12 2006
@@ -340,6 +340,12 @@
      */
     private ContentHandler customContentHandler = null;
 
+    /**
+     * Object which will receive callbacks for every pop/push action
+     * on the default stack or named stacks. 
+     */
+    private StackAction stackAction = null;
+
     // ------------------------------------------------------------- Properties
 
     /**
@@ -1013,6 +1019,14 @@
         customContentHandler = handler;
     }
 
+    public void setStackAction(StackAction stackAction) {
+       this.stackAction = stackAction;
+    }
+
+    public StackAction getStackAction() {
+       return stackAction;
+    }
+
     // ------------------------------------------------- ContentHandler Methods
 
 
@@ -2622,7 +2636,11 @@
     public Object pop() {
 
         try {
-            return (stack.pop());
+               Object popped = stack.pop();
+               if (stackAction != null) {
+                       popped = stackAction.onPop(this, null, popped);
+               }
+            return popped;
         } catch (EmptyStackException e) {
             log.warn("Empty stack (returning null)");
             return (null);
@@ -2638,11 +2656,14 @@
      */
     public void push(Object object) {
 
+        if (stackAction != null) {
+               object = stackAction.onPush(this, null, object);
+        }
+
         if (stack.size() == 0) {
             root = object;
         }
         stack.push(object);
-
     }
 
     /**
@@ -2655,6 +2676,10 @@
      * @since 1.6
      */
     public void push(String stackName, Object value) {
+        if (stackAction != null) {
+               value = stackAction.onPush(this, stackName, value);
+        }
+
         ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
         if (namedStack == null) {
             namedStack = new ArrayStack();
@@ -2669,7 +2694,7 @@
      * <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
+     * @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 or or null if the 
stack is either 
      * empty or has not been created yet
      * @throws EmptyStackException if the named stack is empty
@@ -2684,11 +2709,14 @@
                 log.debug("Stack '" + stackName + "' is empty");
             }
             throw new EmptyStackException();
-            
-        } else {
+        }
         
-            result = namedStack.pop();
+        result = namedStack.pop();
+        
+        if (stackAction != null) {
+               result = stackAction.onPop(this, stackName, result);
         }
+
         return result;
     }
     

Added: 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/StackAction.java
URL: 
http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/StackAction.java?rev=398306&view=auto
==============================================================================
--- 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/StackAction.java
 (added)
+++ 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/StackAction.java
 Sun Apr 30 03:19:12 2006
@@ -0,0 +1,75 @@
+/* $Id$
+ *
+ * Copyright 2006 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.digester;
+
+/**
+ * An interface that can be implemented in order to get notifications of
+ * objects being pushed onto a digester stack or popped from one.
+ * <p>
+ * Because objects are pushed onto the main object stack when a rule
+ * has created a new object, this gives the ability to intercept such
+ * operations and perform modifications on created objects.
+ * <p>
+ * One use expected for this interface is to store information about the xml
+ * line that a particular object was created from. An implementation of this
+ * interface can detect whenever an object is pushed onto the digester object
+ * stack, call Digester.getDocumentLocator() to get the location within the
+ * current xml file, and store this either on the object on the stack (if it
+ * supports some user-specific interface for this purpose), or build a map of
+ * (object->locationinfo) separately.
+ * <p>
+ * It is recommended that objects implementing this interface provide
+ * a method to set a "next" action, and invoke it from the callback
+ * methods. This allows multiple actions to be "chained" together.
+ * <p>
+ * See also Digester.setStackAction.
+ */
+public interface StackAction {
+       /**
+        * Invoked just before an object is to be pushed onto a digester stack.
+        * 
+        * @param d is the digester instance.
+        * 
+        * @param stackName is the name of the stack onto which the object
+        * has been pushed. Null is passed to indicate the default stack.
+        * 
+        * @param o is the object that has just been pushed. Calling peek on the
+        * specified stack will return the same object.
+        * 
+        * @return the object to be pushed. Normally, parameter o is returned
+        * but this method could return an alternate object to be pushed
+        * instead (eg a proxy for the provided object).
+        */
+    public Object onPush(Digester d, String stackName, Object o);
+
+    /**
+     * Invoked just after an object has been popped from a digester stack.
+     * 
+        * @param d is the digester instance.
+        * 
+        * @param stackName is the name of the stack from which the object
+        * has been popped. Null is passed to indicate the default stack.
+        * 
+        * @param o is the object that has just been popped.
+        * 
+        * @return the object to be returned to the called. Normally, parameter
+        * o is returned but this method could return an alternate object.
+     */
+    public Object onPop(Digester d, String stackName, Object o);
+}
\ No newline at end of file

Propchange: 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/StackAction.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
jakarta/commons/proper/digester/trunk/src/java/org/apache/commons/digester/StackAction.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: 
jakarta/commons/proper/digester/trunk/src/test/org/apache/commons/digester/DigesterTestCase.java
URL: 
http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/trunk/src/test/org/apache/commons/digester/DigesterTestCase.java?rev=398306&r1=398305&r2=398306&view=diff
==============================================================================
--- 
jakarta/commons/proper/digester/trunk/src/test/org/apache/commons/digester/DigesterTestCase.java
 (original)
+++ 
jakarta/commons/proper/digester/trunk/src/test/org/apache/commons/digester/DigesterTestCase.java
 Sun Apr 30 03:19:12 2006
@@ -18,22 +18,23 @@
 
 package org.apache.commons.digester;
 
+import java.io.StringReader;
 import java.math.BigDecimal;
 import java.net.URL;
-import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.EmptyStackException;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.EmptyStackException;
 
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
 
-import org.xml.sax.ErrorHandler;
 import org.xml.sax.Attributes;
-import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.ErrorHandler;
 import org.xml.sax.InputSource;
+import org.xml.sax.helpers.AttributesImpl;
 
 
 /**
@@ -496,5 +497,99 @@
         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 events = new ArrayList();
+       public Object onPush(Digester d, String stackName, Object o) {
+               String msg = "push:" + stackName + ":" + o.toString();
+               events.add(msg);
+               
+               String str = o.toString();
+               if (str.startsWith("replpush")) {
+                       return new String(str);
+               } else {
+                       return o;
+               }
+       }
+       public Object onPop(Digester d, String stackName, Object o) {
+               String msg = "pop:" + stackName + ":" + o.toString();
+               events.add(msg);
+               String str = o.toString();
+               if (str.startsWith("replpop")) {
+                       return new String(str);
+               } else {
+                       return o;
+               }
+       }
+    }
+
+    /**
+     * Test custom StackAction subclasses.
+     */
+    public void testStackAction() {
+       TrackingStackAction action = new TrackingStackAction();
+       
+       Object obj1 = new String("obj1");
+       Object obj2 = new String("obj2");
+       Object obj3 = new String("replpop.obj3");
+       Object obj4 = new String("replpush.obj4");
+
+       Object obj8 = new String("obj8");
+       Object obj9 = new String("obj9");
+
+       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
+       assertFalse(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));
+
+       Object obj4a = d.pop();
+       Object obj3a = d.pop();
+       Object obj2a = d.pop();
+       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);
+       Object obj9a = d.pop("stack1");
+       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));
     }
 }

Added: 
jakarta/commons/proper/digester/trunk/src/test/org/apache/commons/digester/LocationTrackerTestCase.java
URL: 
http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/trunk/src/test/org/apache/commons/digester/LocationTrackerTestCase.java?rev=398306&view=auto
==============================================================================
--- 
jakarta/commons/proper/digester/trunk/src/test/org/apache/commons/digester/LocationTrackerTestCase.java
 (added)
+++ 
jakarta/commons/proper/digester/trunk/src/test/org/apache/commons/digester/LocationTrackerTestCase.java
 Sun Apr 30 03:19:12 2006
@@ -0,0 +1,95 @@
+/* $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.digester;
+
+
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.xml.sax.Locator;
+
+
+/**
+ * Tests that StackAction can be used to track the source location
+ * of objects created from input xml stream.
+ */
+
+public class LocationTrackerTestCase extends TestCase {
+       
+       private static class LocationTracker implements StackAction {
+               public Map locations = new HashMap();
+
+       public Object onPush(Digester d, String stackName, Object o) {
+               if (stackName == null) {
+                       // we only care about the real object stack
+                       
+                       // note that a Locator object can also provide 
+                       // publicId and systemId info.
+                       Locator l = d.getDocumentLocator();
+                       StringBuffer locn = new StringBuffer();
+                       locn.append("line=");
+                       locn.append(l.getLineNumber());
+                       locations.put(o, locn.toString());
+               }
+               return o;
+       }
+
+       public Object onPop(Digester d, String stackName, Object o) {
+               return o;
+       }
+       }
+
+       public void testAll() throws Exception {
+           final String TEST_XML =
+               "<?xml version='1.0'?>\n"
+               + "<box id='root'>\n"
+               + "  <subBox id='box1'/>\n" 
+               + "  <ignoreme/>\n"
+               + "  <subBox id='box2'/> <subBox id='box3'/>\n" 
+               + "</box>";
+           
+           LocationTracker locnTracker = new LocationTracker();
+
+           Digester digester = new Digester();
+           digester.setStackAction(locnTracker);
+        digester.addObjectCreate("box", Box.class);
+        digester.addSetProperties("box");
+        digester.addObjectCreate("box/subBox", Box.class);
+        digester.addSetProperties("box/subBox");
+        digester.addSetNext("box/subBox", "addChild");
+           
+        Object result = digester.parse(new StringReader(TEST_XML));
+        assertNotNull(result);
+        Box root = (Box) result;
+        List children = root.getChildren();
+        assertEquals(3, children.size());
+        Box box1 = (Box) children.get(0);
+        Box box2 = (Box) children.get(1);
+        Box box3 = (Box) children.get(2);
+        
+        assertEquals("line=2", locnTracker.locations.get(root));
+        assertEquals("line=3", locnTracker.locations.get(box1));
+        assertEquals("line=5", locnTracker.locations.get(box2));
+        assertEquals("line=5", locnTracker.locations.get(box3));
+    }
+}

Propchange: 
jakarta/commons/proper/digester/trunk/src/test/org/apache/commons/digester/LocationTrackerTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
jakarta/commons/proper/digester/trunk/src/test/org/apache/commons/digester/LocationTrackerTestCase.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to