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 2006/04/30 12:19:15 UTC

svn commit: r398306 - in /jakarta/commons/proper/digester/trunk/src: java/org/apache/commons/digester/ test/org/apache/commons/digester/

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: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org