Author: hlship
Date: Sat Jul 23 16:57:20 2011
New Revision: 1150146

URL: http://svn.apache.org/viewvc?rev=1150146&view=rev
Log:
TAP5-1508: Convert OnEventWorker from using advice on dispatchComponentEvent() 
to directly invoking methods

Added:
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodParameterSource.java
Removed:
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BaseEventHandlerMethodInvoker.java
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodInvoker.java
Modified:
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java

Added: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodParameterSource.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodParameterSource.java?rev=1150146&view=auto
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodParameterSource.java
 (added)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodParameterSource.java
 Sat Jul 23 16:57:20 2011
@@ -0,0 +1,58 @@
+//
+// Copyright 2011 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.tapestry5.internal.transform;
+
+import org.apache.tapestry5.ioc.Invokable;
+import org.apache.tapestry5.ioc.OperationTracker;
+import org.apache.tapestry5.runtime.ComponentEvent;
+
+/**
+ * Used  to encapsulate the list of {@link 
EventHandlerMethodParameterProvider}s for a particular
+ * method of a particular component, providing {@link OperationTracker} 
behavior as parameter values
+ * are obtained/computed/coerced.
+ *
+ * @since 5.3
+ */
+public class EventHandlerMethodParameterSource
+{
+    private final String methodIdentifier;
+
+    private final OperationTracker operationTracker;
+
+    private final EventHandlerMethodParameterProvider[] providers;
+
+    public EventHandlerMethodParameterSource(String methodIdentifier, 
OperationTracker operationTracker, EventHandlerMethodParameterProvider[] 
providers)
+    {
+
+        this.methodIdentifier = methodIdentifier;
+        this.operationTracker = operationTracker;
+        this.providers = providers;
+    }
+
+    public Object get(final ComponentEvent event, final int index)
+    {
+        // Hopefully this will not be too much overhead; it's really nice to 
be able to track what parameter
+        // caused a failure.
+
+        return operationTracker.invoke(String.format("Obtaining value for 
parameter #%d of %s", index + 1, methodIdentifier),
+                new Invokable<Object>()
+                {
+                    public Object invoke()
+                    {
+                        return 
providers[index].valueForEventHandlerMethodParameter(event);
+                    }
+                });
+    }
+}

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java?rev=1150146&r1=1150145&r2=1150146&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
 Sat Jul 23 16:57:20 2011
@@ -14,35 +14,31 @@
 
 package org.apache.tapestry5.internal.transform;
 
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
 import org.apache.tapestry5.EventContext;
 import org.apache.tapestry5.ValueEncoder;
 import org.apache.tapestry5.annotations.OnEvent;
 import org.apache.tapestry5.annotations.RequestParameter;
 import org.apache.tapestry5.func.F;
 import org.apache.tapestry5.func.Flow;
-import org.apache.tapestry5.func.Mapper;
 import org.apache.tapestry5.func.Predicate;
-import org.apache.tapestry5.func.Worker;
 import org.apache.tapestry5.internal.services.ComponentClassCache;
+import org.apache.tapestry5.ioc.OperationTracker;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.model.MutableComponentModel;
-import org.apache.tapestry5.plastic.MethodAdvice;
-import org.apache.tapestry5.plastic.MethodDescription;
-import org.apache.tapestry5.plastic.MethodInvocation;
-import org.apache.tapestry5.plastic.PlasticClass;
-import org.apache.tapestry5.plastic.PlasticMethod;
+import org.apache.tapestry5.plastic.*;
 import org.apache.tapestry5.runtime.ComponentEvent;
+import org.apache.tapestry5.runtime.Event;
 import org.apache.tapestry5.services.Request;
 import org.apache.tapestry5.services.TransformConstants;
 import org.apache.tapestry5.services.ValueEncoderSource;
 import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
 import org.apache.tapestry5.services.transform.TransformationSupport;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
 /**
  * Provides implementations of the
  * {@link 
org.apache.tapestry5.runtime.Component#dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)}
@@ -56,6 +52,150 @@ public class OnEventWorker implements Co
 
     private final ComponentClassCache classCache;
 
+    private final OperationTracker operationTracker;
+
+    private final InstructionBuilderCallback RETURN_TRUE = new 
InstructionBuilderCallback()
+    {
+        public void doBuild(InstructionBuilder builder)
+        {
+            builder.loadConstant(true).returnResult();
+        }
+    };
+
+    /**
+     * Encapsulates information needed to invoke a method as an event handler 
method, including the logic
+     * to construct parameter values, and match the method against the {@link 
ComponentEvent}.
+     */
+    class EventHandlerMethod
+    {
+        final PlasticMethod method;
+
+        final MethodDescription description;
+
+        final String eventType, componentId;
+
+        final EventHandlerMethodParameterSource parameterSource;
+
+        int minContextValues = 0;
+
+        EventHandlerMethod(PlasticMethod method)
+        {
+            this.method = method;
+            description = method.getDescription();
+
+            parameterSource = buildSource();
+
+            String methodName = method.getDescription().methodName;
+
+            OnEvent onEvent = method.getAnnotation(OnEvent.class);
+
+            eventType = extractEventType(methodName, onEvent);
+            componentId = extractComponentId(methodName, onEvent);
+        }
+
+        void buildMatchAndInvocation(InstructionBuilder builder, final 
LocalVariable resultVariable)
+        {
+            final PlasticField sourceField =
+                    parameterSource == null ? null
+                            : 
method.getPlasticClass().introduceField(EventHandlerMethodParameterSource.class,
 description.methodName + "$parameterSource").inject(parameterSource);
+
+            
builder.loadArgument(0).loadConstant(eventType).loadConstant(componentId).loadConstant(minContextValues);
+            builder.invoke(ComponentEvent.class, boolean.class, "matches", 
String.class, String.class, int.class);
+
+            builder.when(Condition.NON_ZERO, new InstructionBuilderCallback()
+            {
+                public void doBuild(InstructionBuilder builder)
+                {
+                    
builder.loadArgument(0).loadConstant(method.getMethodIdentifier()).invoke(Event.class,
 void.class, "setMethodDescription", String.class);
+
+                    builder.loadThis();
+
+                    int count = description.argumentTypes.length;
+
+                    for (int i = 0; i < count; i++)
+                    {
+                        
builder.loadThis().getField(sourceField).loadArgument(0).loadConstant(i);
+
+                        
builder.invoke(EventHandlerMethodParameterSource.class, Object.class, "get",
+                                ComponentEvent.class, int.class);
+
+                        builder.castOrUnbox(description.argumentTypes[i]);
+                    }
+
+                    builder.invokeVirtual(method);
+
+                    if (!method.isVoid())
+                    {
+                        builder.boxPrimitive(description.returnType);
+                        builder.loadArgument(0).swap();
+
+                        builder.invoke(Event.class, boolean.class, 
"storeResult", Object.class);
+
+                        // storeResult() returns true if the method is 
aborted. Return true since, certainly,
+                        // a method was invoked.
+                        builder.when(Condition.NON_ZERO, RETURN_TRUE);
+                    }
+
+                    // Set the result to true, to indicate that some method 
was invoked.
+
+                    builder.loadConstant(true).storeVariable(resultVariable);
+                }
+            });
+        }
+
+
+        private EventHandlerMethodParameterSource buildSource()
+        {
+            final String[] parameterTypes = 
method.getDescription().argumentTypes;
+
+            if (parameterTypes.length == 0)
+            {
+                return null;
+            }
+
+            final List<EventHandlerMethodParameterProvider> providers = 
CollectionFactory.newList();
+
+            int contextIndex = 0;
+
+            for (int i = 0; i < parameterTypes.length; i++)
+            {
+                String type = parameterTypes[i];
+
+                EventHandlerMethodParameterProvider provider = 
parameterTypeToProvider.get(type);
+
+                if (provider != null)
+                {
+                    providers.add(provider);
+                    continue;
+                }
+
+                RequestParameter parameterAnnotation = 
method.getParameters().get(i).getAnnotation(RequestParameter.class);
+
+                if (parameterAnnotation != null)
+                {
+                    String parameterName = parameterAnnotation.value();
+
+                    providers.add(createQueryParameterProvider(method, i, 
parameterName, type,
+                            parameterAnnotation.allowBlank()));
+                    continue;
+                }
+
+                // Note: probably safe to do the conversion to Class early 
(class load time)
+                // as parameters are rarely (if ever) component classes.
+
+                providers.add(createEventContextProvider(type, 
contextIndex++));
+            }
+
+
+            minContextValues = contextIndex;
+
+            EventHandlerMethodParameterProvider[] providerArray = 
providers.toArray(new EventHandlerMethodParameterProvider[providers.size()]);
+
+            return new 
EventHandlerMethodParameterSource(method.getMethodIdentifier(), 
operationTracker, providerArray);
+        }
+    }
+
+
     /**
      * Stores a couple of special parameter type mappings that are used when 
matching the entire event context
      * (either as Object[] or EventContext).
@@ -94,11 +234,12 @@ public class OnEventWorker implements Co
         });
     }
 
-    public OnEventWorker(Request request, ValueEncoderSource 
valueEncoderSource, ComponentClassCache classCache)
+    public OnEventWorker(Request request, ValueEncoderSource 
valueEncoderSource, ComponentClassCache classCache, OperationTracker 
operationTracker)
     {
         this.request = request;
         this.valueEncoderSource = valueEncoderSource;
         this.classCache = classCache;
+        this.operationTracker = operationTracker;
     }
 
     public void transform(PlasticClass plasticClass, TransformationSupport 
support, MutableComponentModel model)
@@ -106,78 +247,52 @@ public class OnEventWorker implements Co
         Flow<PlasticMethod> methods = matchEventHandlerMethods(plasticClass);
 
         if (methods.isEmpty())
+        {
             return;
+        }
 
-        Flow<EventHandlerMethodInvoker> invokers = 
toInvokers(plasticClass.getClassName(), methods);
-
-        updateModelWithHandledEvents(model, invokers);
-
-        adviseDispatchComponentEventMethod(plasticClass, invokers);
+        implementDispatchComponentEvent(plasticClass, 
support.isRootTransformation(), methods, model);
     }
 
-    private void adviseDispatchComponentEventMethod(PlasticClass plasticClass, 
Flow<EventHandlerMethodInvoker> invokers)
-    {
-        MethodAdvice advice = createDispatchComponentEventAdvice(invokers);
-
-        
plasticClass.introduceMethod(TransformConstants.DISPATCH_COMPONENT_EVENT_DESCRIPTION).addAdvice(advice);
-    }
 
-    private MethodAdvice 
createDispatchComponentEventAdvice(Flow<EventHandlerMethodInvoker> invokers)
+    private void implementDispatchComponentEvent(final PlasticClass 
plasticClass, final boolean isRoot, final Flow<PlasticMethod> eventMethods, 
final MutableComponentModel model)
     {
-        final EventHandlerMethodInvoker[] invokersArray = 
invokers.toArray(EventHandlerMethodInvoker.class);
-
-        return new MethodAdvice()
+        
plasticClass.introduceMethod(TransformConstants.DISPATCH_COMPONENT_EVENT_DESCRIPTION).changeImplementation(new
 InstructionBuilderCallback()
         {
-            public void advise(MethodInvocation invocation)
-            {
-                // Invoke the super-class implementation first. If no 
super-class,
-                // this will do nothing and return false.
-
-                invocation.proceed();
-
-                ComponentEvent event = (ComponentEvent) 
invocation.getParameter(0);
-
-                if (invokeEventHandlers(event, invocation.getInstance()))
-                    invocation.setReturnValue(true);
-            }
-
-            private boolean invokeEventHandlers(ComponentEvent event, Object 
instance)
+            public void doBuild(InstructionBuilder builder)
             {
-                // If the super-class aborted the event (some super-class 
method return non-null),
-                // then it's all over, don't even check for handlers in this 
class.
-
-                if (event.isAborted())
-                    return false;
-
-                boolean didInvokeSomeHandler = false;
-
-                for (EventHandlerMethodInvoker invoker : invokersArray)
+                builder.startVariable("boolean", new LocalVariableCallback()
                 {
-                    if (event.matches(invoker.getEventType(), 
invoker.getComponentId(),
-                            invoker.getMinContextValueCount()))
+                    public void doBuild(LocalVariable resultVariable, 
InstructionBuilder builder)
                     {
-                        didInvokeSomeHandler = true;
+                        if (!isRoot)
+                        {
+                            // As a subclass, there will be a base class 
implementation (possibly empty).
+
+                            
builder.loadThis().loadArguments().invokeSpecial(plasticClass.getSuperClassName(),
 TransformConstants.DISPATCH_COMPONENT_EVENT_DESCRIPTION);
+
+                            // First store the result of the super() call into 
the variable.
+                            builder.storeVariable(resultVariable);
+                            builder.loadArgument(0).invoke(Event.class, 
boolean.class, "isAborted");
+                            builder.when(Condition.NON_ZERO, RETURN_TRUE);
+                        } else
+                        {
+                            // No event handler method has yet been invoked.
+                            
builder.loadConstant(false).storeVariable(resultVariable);
+                        }
+
+                        for (PlasticMethod method : eventMethods)
+                        {
+                            EventHandlerMethod eventHandlerMethod = new 
EventHandlerMethod(method);
 
-                        invoker.invokeEventHandlerMethod(event, instance);
+                            
eventHandlerMethod.buildMatchAndInvocation(builder, resultVariable);
 
-                        if (event.isAborted())
-                            break;
-                    }
-                }
+                            
model.addEventHandler(eventHandlerMethod.eventType);
+                        }
 
-                return didInvokeSomeHandler;
-            }
-        };
-    }
-
-    private void updateModelWithHandledEvents(final MutableComponentModel 
model,
-            Flow<EventHandlerMethodInvoker> invokers)
-    {
-        invokers.each(new Worker<EventHandlerMethodInvoker>()
-        {
-            public void work(EventHandlerMethodInvoker value)
-            {
-                model.addEventHandler(value.getEventType());
+                        builder.loadVariable(resultVariable).returnResult();
+                    }
+                });
             }
         });
     }
@@ -200,80 +315,12 @@ public class OnEventWorker implements Co
             {
                 return method.hasAnnotation(OnEvent.class);
             }
-
         });
     }
 
-    private Flow<EventHandlerMethodInvoker> toInvokers(final String 
componentClassName, Flow<PlasticMethod> methods)
-    {
-        return methods.map(new Mapper<PlasticMethod, 
EventHandlerMethodInvoker>()
-        {
-            public EventHandlerMethodInvoker map(PlasticMethod element)
-            {
-                return toInvoker(componentClassName, element);
-            }
-        });
-    }
-
-    private EventHandlerMethodInvoker toInvoker(final String 
componentClassName, PlasticMethod method)
-    {
-        OnEvent annotation = method.getAnnotation(OnEvent.class);
-
-        final MethodDescription description = method.getDescription();
-
-        String methodName = description.methodName;
 
-        String eventType = extractEventType(methodName, annotation);
-        String componentId = extractComponentId(methodName, annotation);
-
-        String[] parameterTypes = description.argumentTypes;
-
-        if (parameterTypes.length == 0)
-            return new BaseEventHandlerMethodInvoker(method, eventType, 
componentId);
-
-        final List<EventHandlerMethodParameterProvider> providers = 
CollectionFactory.newList();
-
-        // I'd refactor a bit more of this if Java had covariant return types.
-
-        int contextIndex = 0;
-
-        for (int i = 0; i < parameterTypes.length; i++)
-        {
-            String type = parameterTypes[i];
-
-            EventHandlerMethodParameterProvider provider = 
parameterTypeToProvider.get(type);
-
-            if (provider != null)
-            {
-                providers.add(provider);
-                continue;
-            }
-
-            RequestParameter parameterAnnotation = 
method.getParameters().get(i).getAnnotation(RequestParameter.class);
-
-            if (parameterAnnotation != null)
-            {
-                String parameterName = parameterAnnotation.value();
-
-                providers.add(createQueryParameterSource(componentClassName, 
description, i, parameterName, type,
-                        parameterAnnotation.allowBlank()));
-                continue;
-            }
-
-            // Note: probably safe to do the conversion to Class early (class 
load time)
-            // as parameters are rarely (if ever) component classes.
-
-            final int parameterIndex = contextIndex++;
-
-            providers.add(createEventContextSource(type, parameterIndex));
-        }
-
-        return createInvoker(method, eventType, componentId, contextIndex, 
providers);
-    }
-
-    private EventHandlerMethodParameterProvider 
createQueryParameterSource(final String componentClassName,
-            final MethodDescription description, final int parameterIndex, 
final String parameterName,
-            final String parameterTypeName, final boolean allowBlank)
+    private EventHandlerMethodParameterProvider 
createQueryParameterProvider(final PlasticMethod method, final int 
parameterIndex, final String parameterName,
+                                                                             
final String parameterTypeName, final boolean allowBlank)
     {
         return new EventHandlerMethodParameterProvider()
         {
@@ -302,48 +349,19 @@ public class OnEventWorker implements Co
                                         parameterName, 
parameterType.getName()));
 
                     return value;
-                }
-                catch (Exception ex)
+                } catch (Exception ex)
                 {
                     throw new RuntimeException(
                             String.format(
-                                    "Unable process query parameter '%s' as 
parameter #%d of event handler method %s (in class %s): %s",
-                                    parameterName, parameterIndex + 1, 
description, componentClassName,
+                                    "Unable process query parameter '%s' as 
parameter #%d of event handler method %s: %s",
+                                    parameterName, parameterIndex + 1, 
method.getMethodIdentifier(),
                                     InternalUtils.toMessage(ex)), ex);
                 }
             }
         };
     }
 
-    private EventHandlerMethodInvoker createInvoker(PlasticMethod method, 
String eventType, String componentId,
-            final int minContextCount, final 
List<EventHandlerMethodParameterProvider> providers)
-    {
-        return new BaseEventHandlerMethodInvoker(method, eventType, 
componentId)
-        {
-            final int count = providers.size();
-
-            @Override
-            public int getMinContextValueCount()
-            {
-                return minContextCount;
-            }
-
-            @Override
-            protected Object[] constructParameters(ComponentEvent event)
-            {
-                Object[] parameters = new Object[count];
-
-                for (int i = 0; i < count; i++)
-                {
-                    parameters[i] = 
providers.get(i).valueForEventHandlerMethodParameter(event);
-                }
-
-                return parameters;
-            }
-        };
-    }
-
-    private EventHandlerMethodParameterProvider createEventContextSource(final 
String type, final int parameterIndex)
+    private EventHandlerMethodParameterProvider 
createEventContextProvider(final String type, final int parameterIndex)
     {
         return new EventHandlerMethodParameterProvider()
         {


Reply via email to