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