Revision: 431
          http://svn.sourceforge.net/stripes/?rev=431&view=rev
Author:   tfenne
Date:     2006-10-07 10:50:55 -0700 (Sat, 07 Oct 2006)

Log Message:
-----------
Fix for STS-186: before/after methods should be able to specify which events to 
execute on.

Modified Paths:
--------------
    trunk/stripes/src/net/sourceforge/stripes/action/After.java
    trunk/stripes/src/net/sourceforge/stripes/action/Before.java
    
trunk/stripes/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptor.java
    trunk/stripes/src/net/sourceforge/stripes/controller/DispatcherHelper.java
    trunk/stripes/src/net/sourceforge/stripes/util/CollectionUtil.java
    
trunk/tests/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptorTests.java
    trunk/tests/src/net/sourceforge/stripes/controller/MapBindingTests.java
    trunk/tests/src/net/sourceforge/stripes/util/CollectionUtilTest.java

Modified: trunk/stripes/src/net/sourceforge/stripes/action/After.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/action/After.java 2006-10-06 
02:16:47 UTC (rev 430)
+++ trunk/stripes/src/net/sourceforge/stripes/action/After.java 2006-10-07 
17:50:55 UTC (rev 431)
@@ -66,5 +66,13 @@
 @Documented
 public @interface After {
        /** One or more lifecycle stages after which the method should be 
called. */
-       LifecycleStage[] value() default LifecycleStage.EventHandling; 
+       LifecycleStage[] stages() default LifecycleStage.EventHandling;
+
+    /**
+     * Allows the method to be restricted to one or more events. By default 
the method will
+     * be executed on all events. Can be used to specify one or more events to 
apply the method
+     * to (e.g. on={"save", "update"}),  or to specify one or more events 
<i>not</i> to apply
+     * the method to (e.g. on="!delete").
+     */
+    String[] on() default {};
 }

Modified: trunk/stripes/src/net/sourceforge/stripes/action/Before.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/action/Before.java        
2006-10-06 02:16:47 UTC (rev 430)
+++ trunk/stripes/src/net/sourceforge/stripes/action/Before.java        
2006-10-07 17:50:55 UTC (rev 431)
@@ -67,5 +67,13 @@
 @Documented
 public @interface Before {
        /** One or more lifecycle stages before which the method should be 
called. */
-       LifecycleStage[] value() default LifecycleStage.EventHandling; 
+       LifecycleStage[] stages() default LifecycleStage.EventHandling;
+
+    /**
+     * Allows the method to be restricted to one or more events. By default 
the method will
+     * be executed on all events. Can be used to specify one or more events to 
apply the method
+     * to (e.g. on={"save", "update"}),  or to specify one or more events 
<i>not</i> to apply
+     * the method to (e.g. on="!delete").
+     */
+    String[] on() default {};
 }

Modified: 
trunk/stripes/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptor.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptor.java
      2006-10-06 02:16:47 UTC (rev 430)
+++ 
trunk/stripes/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptor.java
      2006-10-07 17:50:55 UTC (rev 431)
@@ -18,8 +18,10 @@
 import net.sourceforge.stripes.action.After;
 import net.sourceforge.stripes.action.Before;
 import net.sourceforge.stripes.action.Resolution;
+import net.sourceforge.stripes.action.ActionBeanContext;
 import net.sourceforge.stripes.util.Log;
 import net.sourceforge.stripes.util.ReflectUtil;
+import net.sourceforge.stripes.util.CollectionUtil;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationTargetException;
@@ -87,6 +89,8 @@
      */
        public Resolution intercept(ExecutionContext context) throws Exception {
                LifecycleStage stage = context.getLifecycleStage();
+        ActionBeanContext abc = context.getActionBeanContext();
+        String event = abc == null ? null : abc.getEventName();
         Resolution resolution = null;
 
                // Run @Before methods, as long as there's a bean to run them on
@@ -96,9 +100,12 @@
                        List<Method> beforeMethods = 
filterMethods.getBeforeMethods(stage);
 
             for (Method method : beforeMethods) {
-                resolution = invoke(bean, method, stage, Before.class);
-                if (resolution != null) {
-                    return resolution;
+                String[] on = method.getAnnotation(Before.class).on();
+                if (event == null || CollectionUtil.applies(on, event)) {
+                    resolution = invoke(bean, method, stage, Before.class);
+                    if (resolution != null) {
+                        return resolution;
+                    }
                 }
             }
         }
@@ -113,9 +120,12 @@
 
         Resolution overrideResolution = null;
         for (Method method : afterMethods) {
-            overrideResolution = invoke(bean, method, stage, After.class);
-            if (overrideResolution != null) {
-                return overrideResolution;
+            String[] on = method.getAnnotation(After.class).on();
+            if (event == null || CollectionUtil.applies(on, event)) {
+                overrideResolution = invoke(bean, method, stage, After.class);
+                if (overrideResolution != null) {
+                    return overrideResolution;
+                }
             }
         }
 
@@ -215,12 +225,12 @@
 
                     if (method.isAnnotationPresent(Before.class)) {
                             Before annotation = 
method.getAnnotation(Before.class);
-                            filterMethods.addBeforeMethod(annotation.value(), 
method);
+                            filterMethods.addBeforeMethod(annotation.stages(), 
method);
                     }
 
                     if (method.isAnnotationPresent(After.class)) {
                         After annotation = method.getAnnotation(After.class);
-                        filterMethods.addAfterMethod(annotation.value(), 
method);
+                        filterMethods.addAfterMethod(annotation.stages(), 
method);
                     }
                 }
             }

Modified: 
trunk/stripes/src/net/sourceforge/stripes/controller/DispatcherHelper.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/controller/DispatcherHelper.java  
2006-10-06 02:16:47 UTC (rev 430)
+++ trunk/stripes/src/net/sourceforge/stripes/controller/DispatcherHelper.java  
2006-10-07 17:50:55 UTC (rev 431)
@@ -22,6 +22,7 @@
 import net.sourceforge.stripes.exception.StripesServletException;
 import net.sourceforge.stripes.util.HtmlUtil;
 import net.sourceforge.stripes.util.Log;
+import net.sourceforge.stripes.util.CollectionUtil;
 import net.sourceforge.stripes.validation.Validatable;
 import net.sourceforge.stripes.validation.ValidationError;
 import net.sourceforge.stripes.validation.ValidationErrorHandler;
@@ -269,30 +270,9 @@
      * @return true if the custom validation should be applied to this event, 
false otherwise
      */
     public static boolean applies(ValidationMethod info, String event) {
-        final String[] events = info.on();
-
-        if (events.length == 0 || event == null) {
-            return true;
-        }
-        else if (events[0].startsWith("!")) {
-            return !contains(events, "!" + event);
-        }
-        else {
-            return contains(events, event);
-        }
+        return CollectionUtil.applies(info.on(), event);
     }
 
-    /** A quick utility method for linear searching an array of Strings. */
-    private static boolean contains(String[] arr, String target) {
-        for (String item : arr) {
-            if (item.equals(target)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
     /**
      * Finds and returns all methods in the ActionBean class and it's 
superclasses that
      * are marked with the ValidationMethod annotation and returns them 
ordered by

Modified: trunk/stripes/src/net/sourceforge/stripes/util/CollectionUtil.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/util/CollectionUtil.java  
2006-10-06 02:16:47 UTC (rev 430)
+++ trunk/stripes/src/net/sourceforge/stripes/util/CollectionUtil.java  
2006-10-07 17:50:55 UTC (rev 431)
@@ -55,4 +55,26 @@
 
         return true;
     }
+
+    /**
+     * <p>Checks to see if an event is applicable given an array of event 
names. The array is
+     * usually derived from the <tt>on</tt> attribute of one of the Stripes 
annotations
+     * (e.g. [EMAIL PROTECTED] 
net.sourceforge.stripes.validation.ValidationMethod}). The array can
+     * be composed of <i>positive</i> event names (e.g. {"foo", "bar"}) in 
which case the event
+     * must be contained in the array, or negative event names (e.g. 
{"!splat", "!whee"}) in
+     * which case the event must not be contained in the array.</p>
+     *
+     * <p>Calling this method with a null or zero length array will always 
return true.</p>
+     *
+     * @param events an array containing event names or event names prefixed 
with bangs
+     * @param event the event name to check for applicability given the array
+     * @return true if the array indicates the event is applicable, false 
otherwise
+     */
+    public static boolean applies(String events[], String event) {
+        if (events == null || events.length == 0) return true;
+        boolean isPositive = events[0].charAt(0) != '!';
+
+        if (isPositive) return contains(events, event);
+        else return !contains(events, "!" + event);
+    }
 }

Modified: 
trunk/tests/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptorTests.java
===================================================================
--- 
trunk/tests/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptorTests.java
   2006-10-06 02:16:47 UTC (rev 430)
+++ 
trunk/tests/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptorTests.java
   2006-10-07 17:50:55 UTC (rev 431)
@@ -166,6 +166,44 @@
         Assert.assertEquals(actionBean.getHasCalledProtectedBeforeMethod(), 0);
     }
 
+    @Test(groups="fast")
+    public void testIntercept_withEventSpecifier() throws Exception {
+        ExecutionContext context = new TestExecutionContext();
+        BeforeAfterMethodInterceptor interceptor = new 
BeforeAfterMethodInterceptor();
+        TestActionBean2 actionBean = new TestActionBean2();
+        context.setActionBean(actionBean);
+        context.setActionBeanContext(new ActionBeanContext());
+
+        context.getActionBeanContext().setEventName("edit");
+        context.setLifecycleStage(LifecycleStage.EventHandling); // default
+        Assert.assertNotNull(interceptor.intercept(context));
+
+        context.getActionBeanContext().setEventName("save");
+        context.setLifecycleStage(LifecycleStage.EventHandling); // default
+        Assert.assertNotNull(interceptor.intercept(context));
+
+        Assert.assertEquals(actionBean.getHasCalledAfterDefaultStage(), 2);
+        Assert.assertEquals(actionBean.getHasCalledBeforeWithReturn(), 2);
+        Assert.assertEquals(actionBean.getHasCalledBeforeDefaultStage(), 2);
+        Assert.assertEquals(actionBean.getHasCalledAfterWithReturn(), 2);
+        Assert.assertEquals(actionBean.getHasCalledBeforeAfterDefaultStage(), 
4);
+        Assert.assertEquals(actionBean.getHasCalledBeforeAfterOnSingleEvent(), 
2);
+        Assert.assertEquals(actionBean.getHasCalledProtectedAfterMethod(), 2);
+        Assert.assertEquals(actionBean.getHasCalledProtectedBeforeMethod(), 2);
+
+        Assert.assertEquals(actionBean.getHasCalledAfterSpecificStage(), 0);
+        Assert.assertEquals(actionBean.getHasCalledAfterTwoStages(), 0);
+        Assert.assertEquals(actionBean.getHasCalledAfterWithParameter(), 0);
+        
Assert.assertEquals(actionBean.getHasCalledAfterWithReturnAndParameter(), 0);
+        Assert.assertEquals(actionBean.getHasCalledBeforeSpecificStage(), 0);
+        Assert.assertEquals(actionBean.getHasCalledBeforeTwoStages(), 0);
+        Assert.assertEquals(actionBean.getHasCalledBeforeAfterSpecificStage(), 
0);
+        Assert.assertEquals(actionBean.getHasCalledBeforeAfterWithParameter(), 
0);
+        Assert.assertEquals(actionBean.getHasCalledBeforeWithParameter(), 0);
+        
Assert.assertEquals(actionBean.getHasCalledBeforeWithReturnAndParameter(), 0);
+        Assert.assertEquals(actionBean.getHasCalledDummyMethod(), 0);
+    }
+
     /**
      * Test ActionBean class
      * @author Jeppe Cramon
@@ -190,6 +228,7 @@
         private int hasCalledBeforeAfterWithParameter;
         private int hasCalledBeforeAfterSpecificStage;
         private int hasCalledBeforeAfterDefaultStage;
+        private int hasCalledBeforeAfterOnSingleEvent;
 
         public void setContext(ActionBeanContext context) {
         }
@@ -198,7 +237,7 @@
             return null;
         }
 
-        @Before(LifecycleStage.ActionBeanResolution)
+        @Before(stages=LifecycleStage.ActionBeanResolution)
         public void beforeActionBeanResolutionWillNeverBeCalled() {
             hasCalledBeforeActionBeanResolutionWillNeverBeCalled++;
         }
@@ -208,12 +247,12 @@
             hasCalledBeforeDefaultStage++;
         }
 
-        @Before(LifecycleStage.HandlerResolution)
+        @Before(stages=LifecycleStage.HandlerResolution)
         public void beforeSpecificStage() {
             hasCalledBeforeSpecificStage++;
         }
 
-        @Before({LifecycleStage.BindingAndValidation, 
LifecycleStage.CustomValidation})
+        @Before(stages={LifecycleStage.BindingAndValidation, 
LifecycleStage.CustomValidation})
         public void beforeTwoStages() {
             hasCalledBeforeTwoStages++;
         }
@@ -237,7 +276,7 @@
             return null;
         }
 
-        /** Intercept methods must be public. */
+        /** Should work just like a public method. */
         @Before
         protected void protectedBeforeMethod() {
             hasCalledProtectedBeforeMethod++;
@@ -253,12 +292,12 @@
             hasCalledAfterDefaultStage++;
         }
 
-        @After(LifecycleStage.ActionBeanResolution)
+        @After(stages=LifecycleStage.ActionBeanResolution)
         public void afterSpecificStage() {
             hasCalledAfterSpecificStage++;
         }
 
-        @After({LifecycleStage.HandlerResolution, 
LifecycleStage.CustomValidation})
+        @After(stages={LifecycleStage.HandlerResolution, 
LifecycleStage.CustomValidation})
         public void afterTwoStages() {
             hasCalledAfterTwoStages++;
         }
@@ -283,7 +322,7 @@
             return null;
         }
 
-        /** Not included because methods must be public. */
+        /** Should work just like a public method. */
         @After
         protected void protectedAfterMethod() {
             hasCalledProtectedAfterMethod++;
@@ -298,8 +337,8 @@
         }
 
         /** Invoked only at those stages listed. */
-        @Before(LifecycleStage.BindingAndValidation)
-        @After(LifecycleStage.CustomValidation)
+        @Before(stages=LifecycleStage.BindingAndValidation)
+        @After(stages=LifecycleStage.CustomValidation)
         public void beforeAfterSpecificStage() {
             hasCalledBeforeAfterSpecificStage++;
         }
@@ -310,6 +349,12 @@
             hasCalledBeforeAfterDefaultStage++;
         }
 
+        /** Invoked only at default EventHandling stage. */
+        @Before(on="edit") @After(on="save")
+        public void beforeAfterOnSingleEvent() {
+            hasCalledBeforeAfterOnSingleEvent++;
+        }
+
         // -- Unit test properties --
         public int getHasCalledAfterDefaultStage() { return 
hasCalledAfterDefaultStage; }
         public int getHasCalledAfterSpecificStage() { return 
hasCalledAfterSpecificStage; }
@@ -330,6 +375,7 @@
         public int getHasCalledProtectedAfterMethod() { return 
hasCalledProtectedAfterMethod; }
         public int getHasCalledProtectedBeforeMethod() { return 
hasCalledProtectedBeforeMethod; }
         public int getHasCalledBeforeAfterDefaultStage() { return 
hasCalledBeforeAfterDefaultStage; }
+        public int getHasCalledBeforeAfterOnSingleEvent() { return 
hasCalledBeforeAfterOnSingleEvent; }
     }
 
     private static class TestExecutionContext extends ExecutionContext {
@@ -337,7 +383,5 @@
         public Resolution proceed() throws Exception {
             return new ForwardResolution("wakker");
         }
-
     }
-
 }

Modified: 
trunk/tests/src/net/sourceforge/stripes/controller/MapBindingTests.java
===================================================================
--- trunk/tests/src/net/sourceforge/stripes/controller/MapBindingTests.java     
2006-10-06 02:16:47 UTC (rev 430)
+++ trunk/tests/src/net/sourceforge/stripes/controller/MapBindingTests.java     
2006-10-07 17:50:55 UTC (rev 431)
@@ -239,7 +239,7 @@
         Assert.assertNotNull(bean.getMapDateDate().get(key));
     }
 
-    @Before(LifecycleStage.BindingAndValidation)
+    @Before(stages=LifecycleStage.BindingAndValidation)
     public void populateTypelessMap() {
         this.typelessMap = new HashMap();
         this.typelessMap.put(1, new TestBean());

Modified: trunk/tests/src/net/sourceforge/stripes/util/CollectionUtilTest.java
===================================================================
--- trunk/tests/src/net/sourceforge/stripes/util/CollectionUtilTest.java        
2006-10-06 02:16:47 UTC (rev 430)
+++ trunk/tests/src/net/sourceforge/stripes/util/CollectionUtilTest.java        
2006-10-07 17:50:55 UTC (rev 431)
@@ -43,4 +43,14 @@
     public void testEmptyOnNonEmptyCollection3() {
         Assert.assertFalse(CollectionUtil.empty(new String[] {"bar", "splat", 
"foo"}));
     }
+
+    @Test(groups="fast")
+    public void testApplies() {
+        Assert.assertTrue(CollectionUtil.applies(null, "foo"));
+        Assert.assertTrue(CollectionUtil.applies(new String[] {}, "foo"));
+        Assert.assertTrue(CollectionUtil.applies(new String[] {"bar", "foo"}, 
"foo"));
+        Assert.assertFalse(CollectionUtil.applies(new String[] {"bar", "f00"}, 
"foo"));
+        Assert.assertFalse(CollectionUtil.applies(new String[] {"!bar", 
"!foo"}, "foo"));
+        Assert.assertTrue(CollectionUtil.applies(new String[] {"!bar", 
"!f00"}, "foo"));
+    }
 }


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development

Reply via email to