You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2010/02/19 19:53:16 UTC

svn commit: r911910 - in /tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform: BaseEventHandlerMethodInvoker.java EventHandlerMethodInvoker.java EventHandlerMethodParameterSource.java OnEventWorker.java

Author: hlship
Date: Fri Feb 19 18:53:16 2010
New Revision: 911910

URL: http://svn.apache.org/viewvc?rev=911910&view=rev
Log:
Recode OnEventWorker using the new TransformMethod APIs

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BaseEventHandlerMethodInvoker.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodInvoker.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodParameterSource.java   (with props)
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/BaseEventHandlerMethodInvoker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BaseEventHandlerMethodInvoker.java?rev=911910&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BaseEventHandlerMethodInvoker.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BaseEventHandlerMethodInvoker.java Fri Feb 19 18:53:16 2010
@@ -0,0 +1,82 @@
+// Copyright 2010 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.internal.InternalConstants;
+import org.apache.tapestry5.runtime.ComponentEvent;
+import org.apache.tapestry5.services.MethodAccess;
+import org.apache.tapestry5.services.MethodInvocationResult;
+import org.apache.tapestry5.services.TransformMethod;
+
+/**
+ * Base class for invoking event handler methods that also serves when invoking an
+ * event handler method that takes no parameters.
+ * 
+ * @since 5.2.0
+ */
+public class BaseEventHandlerMethodInvoker implements EventHandlerMethodInvoker
+{
+    private final MethodAccess access;
+
+    private final String identifier;
+
+    private final String eventType;
+
+    private final String componentId;
+
+    public BaseEventHandlerMethodInvoker(TransformMethod method, String eventType, String componentId)
+    {
+        this.eventType = eventType;
+        this.componentId = componentId;
+
+        access = method.getAccess();
+        identifier = method.getMethodIdentifier();
+    }
+
+    public void invokeEventHandlerMethod(ComponentEvent event, Object instance)
+    {
+        event.setMethodDescription(identifier);
+
+        MethodInvocationResult result = access.invoke(instance, constructParameters(event));
+
+        result.rethrow();
+
+        event.storeResult(result.getReturnValue());
+    }
+
+    public String getComponentId()
+    {
+        return componentId;
+    }
+
+    public String getEventType()
+    {
+        return eventType;
+    }
+
+    /**
+     * Returns 0 (the event method takes no parameters). Subclasses should override.
+     */
+    public int getMinContextValueCount()
+    {
+        return 0;
+    }
+
+    /** Overridden in subclasses to provide the actual values to be passed to the method. */
+    protected Object[] constructParameters(ComponentEvent event)
+    {
+        return InternalConstants.EMPTY_STRING_ARRAY;
+    }
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BaseEventHandlerMethodInvoker.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodInvoker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodInvoker.java?rev=911910&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodInvoker.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodInvoker.java Fri Feb 19 18:53:16 2010
@@ -0,0 +1,50 @@
+// Copyright 2010 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.runtime.ComponentEvent;
+
+/**
+ * An object that can invoke an event handler method on a component instance.
+ * 
+ * @since 5.2.0
+ */
+public interface EventHandlerMethodInvoker
+{
+    /**
+     * The type of event this method handles, i.e., "action".
+     */
+    String getEventType();
+
+    /**
+     * The id of the component this method should be invoked for, or
+     * the blank string to ignore component id when matching.
+     */
+    String getComponentId();
+
+    /**
+     * The minimum number of of context values needed. The method
+     * should be invoked if there are at least this number of
+     * context values.
+     */
+    int getMinContextValueCount();
+
+    /**
+     * Given an event and a component instance, invoke the component event method. The method
+     * is passed appropriate parameters. If the invocation throws a checked exception, then
+     * the exception is wrapped in a RuntimeException and rethrown.
+     */
+    void invokeEventHandlerMethod(ComponentEvent event, Object instance);
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodInvoker.java
------------------------------------------------------------------------------
    svn:eol-style = native

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=911910&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 Fri Feb 19 18:53:16 2010
@@ -0,0 +1,38 @@
+// Copyright 2010 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.runtime.ComponentEvent;
+import org.apache.tapestry5.services.MethodAccess;
+
+/**
+ * Supplies one parameter value when invoking a component event handler method. In general,
+ * this involves extracting a value form the event's context and coercing it to a type
+ * appropriate to the parameter.
+ * <p>
+ * These values are accumulated and used to invoke the event handler method via
+ * {@link MethodAccess#invoke(Object, Object...)}.
+ * 
+ * @since 5.2.0
+ */
+public interface EventHandlerMethodParameterSource
+{
+    /**
+     * Extract the value and coerce it to the correct type.
+     * 
+     * @return value that can be passed as a method parameter
+     */
+    Object valueForEventHandlerMethodParameter(ComponentEvent event);
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodParameterSource.java
------------------------------------------------------------------------------
    svn:eol-style = native

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=911910&r1=911909&r2=911910&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 Fri Feb 19 18:53:16 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
 //
+// Copyright 2006, 2007, 2008, 2009, 2010 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
+// 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,
@@ -14,228 +14,297 @@
 
 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.annotations.OnEvent;
-import org.apache.tapestry5.ioc.util.BodyBuilder;
+import org.apache.tapestry5.ioc.Predicate;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.model.MutableComponentModel;
-import org.apache.tapestry5.services.*;
-
-import java.util.Arrays;
-import java.util.List;
+import org.apache.tapestry5.runtime.ComponentEvent;
+import org.apache.tapestry5.services.ClassTransformation;
+import org.apache.tapestry5.services.ComponentClassTransformWorker;
+import org.apache.tapestry5.services.ComponentMethodAdvice;
+import org.apache.tapestry5.services.ComponentMethodInvocation;
+import org.apache.tapestry5.services.TransformConstants;
+import org.apache.tapestry5.services.TransformMethod;
 
 /**
- * Provides implementations of the {@link org.apache.tapestry5.runtime.Component#dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)}
+ * Provides implementations of the
+ * {@link org.apache.tapestry5.runtime.Component#dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)}
  * method, based on {@link org.apache.tapestry5.annotations.OnEvent} annotations.
  */
 public class OnEventWorker implements ComponentClassTransformWorker
 {
-    static final String OBJECT_ARRAY_TYPE = "java.lang.Object[]";
+    /**
+     * Stores a couple of special parameter type mappings that are used when matching the entire event context
+     * (either as Object[] or EventContext).
+     */
+    private final Map<String, EventHandlerMethodParameterSource> parameterTypeToSource = CollectionFactory.newMap();
 
-    static final String EVENT_CONTEXT_TYPE = EventContext.class.getName();
-
-    static final String LIST_TYPE = List.class.getName();
-
-    private final static int ANY_NUMBER_OF_PARAMETERS = -1;
-
-    public void transform(final ClassTransformation transformation, MutableComponentModel model)
     {
-        MethodFilter filter = new MethodFilter()
+        // Object[] and List are out-dated and may be deprecated some day
+
+        parameterTypeToSource.put("java.lang.Object[]", new EventHandlerMethodParameterSource()
         {
-            public boolean accept(TransformMethodSignature signature)
-            {
-                return (hasCorrectPrefix(signature) || hasAnnotation(signature)) &&
-                        !transformation.isMethodOverride(signature);
-            }
 
-            private boolean hasCorrectPrefix(TransformMethodSignature signature)
+            public Object valueForEventHandlerMethodParameter(ComponentEvent event)
             {
-                return signature.getMethodName().startsWith("on");
+                return event.getContext();
             }
+        });
+
+        parameterTypeToSource.put(List.class.getName(), new EventHandlerMethodParameterSource()
+        {
 
-            private boolean hasAnnotation(TransformMethodSignature signature)
+            public Object valueForEventHandlerMethodParameter(ComponentEvent event)
             {
-                return transformation.getMethodAnnotation(signature, OnEvent.class) != null;
+                return Arrays.asList(event.getContext());
             }
-        };
-
-        List<TransformMethodSignature> methods = transformation.findMethods(filter);
-
-        // No methods, no work.
+        });
 
-        if (methods.isEmpty()) return;
-
-        BodyBuilder builder = new BodyBuilder();
-        builder.begin();
-
-        builder.addln("if ($1.isAborted()) return $_;");
-
-        builder.addln("try");
-        builder.begin();
+        // This is better, as the EventContext maintains the original objects (or strings)
+        // and gives the event handler method access with coercion
+        parameterTypeToSource.put(EventContext.class.getName(), new EventHandlerMethodParameterSource()
+        {
 
-        for (TransformMethodSignature method : methods)
-            addCodeForMethod(builder, method, transformation, model);
+            public Object valueForEventHandlerMethodParameter(ComponentEvent event)
+            {
+                return event.getEventContext();
+            }
+        });
+    }
 
-        builder.end(); // try
+    public void transform(ClassTransformation transformation, MutableComponentModel model)
+    {
+        List<TransformMethod> methods = matchEventHandlerMethods(transformation);
 
-        // Runtime exceptions pass right through.
+        if (methods.isEmpty())
+            return;
 
-        builder.addln("catch (RuntimeException ex) { throw ex; }");
+        List<EventHandlerMethodInvoker> invokers = toInvokers(methods);
 
-        // Wrap others in a RuntimeException to communicate them up.
+        updateModelWithHandledEvents(model, invokers);
 
-        builder.addln("catch (Exception ex) { throw new RuntimeException(ex); } ");
+        adviseDispatchComponentEventMethod(transformation, invokers);
+    }
 
-        builder.end();
+    private void adviseDispatchComponentEventMethod(ClassTransformation transformation,
+            List<EventHandlerMethodInvoker> invokers)
+    {
+        ComponentMethodAdvice advice = createDispatchComponentEventAdvice(invokers);
 
-        transformation.extendMethod(TransformConstants.DISPATCH_COMPONENT_EVENT, builder.toString());
+        transformation.getMethod(TransformConstants.DISPATCH_COMPONENT_EVENT).addAdvice(advice);
     }
 
-
-    private void addCodeForMethod(BodyBuilder builder, TransformMethodSignature method,
-                                  ClassTransformation transformation, MutableComponentModel model)
+    private ComponentMethodAdvice createDispatchComponentEventAdvice(final List<EventHandlerMethodInvoker> invokers)
     {
-        // $1 is the event
-
-        int parameterCount = getParameterCount(method);
+        return new ComponentMethodAdvice()
+        {
+            public void advise(ComponentMethodInvocation invocation)
+            {
+                // Invoke the super-class implementation first. If no super-class,
+                // this will do nothing and return false.
 
-        OnEvent annotation = transformation.getMethodAnnotation(method, OnEvent.class);
+                invocation.proceed();
 
-        String eventType = extractEventType(method, annotation);
+                ComponentEvent event = (ComponentEvent) invocation.getParameter(0);
 
-        String componentId = extractComponentId(method, annotation);
+                if (invokeEventHandlers(event, invocation.getInstance()))
+                    invocation.overrideResult(true);
+            }
 
+            private boolean invokeEventHandlers(ComponentEvent event, Object instance)
+            {
+                // 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.
 
-        builder.addln("if ($1.matches(\"%s\", \"%s\", %d))", eventType, componentId, parameterCount);
-        builder.begin();
+                if (event.isAborted())
+                    return false;
 
-        // Ensure that we return true, because *some* event handler method was invoked,
-        // even if it chose not to abort the event.
+                boolean didInvokeSomeHandler = false;
 
-        builder.addln("$_ = true;");
+                for (EventHandlerMethodInvoker invoker : invokers)
+                {
+                    if (event.matches(invoker.getEventType(), invoker.getComponentId(), invoker
+                            .getMinContextValueCount()))
+                    {
+                        didInvokeSomeHandler = true;
 
-        builder.addln("$1.setMethodDescription(\"%s\");", transformation.getMethodIdentifier(method));
+                        invoker.invokeEventHandlerMethod(event, instance);
 
-        boolean isNonVoid = !method.getReturnType().equals("void");
+                        if (event.isAborted())
+                            break;
+                    }
+                }
 
-        // Store the result, converting primitives to wrappers automatically.
+                return didInvokeSomeHandler;
+            }
+        };
+    }
 
-        if (isNonVoid) builder.add("if ($1.storeResult(($w) ");
+    private void updateModelWithHandledEvents(MutableComponentModel model,
+            final List<EventHandlerMethodInvoker> invokers)
+    {
+        for (EventHandlerMethodInvoker invoker : invokers)
+        {
+            model.addEventHandler(invoker.getEventType());
+        }
+    }
 
-        builder.add("%s(", method.getMethodName());
+    private List<TransformMethod> matchEventHandlerMethods(ClassTransformation transformation)
+    {
+        return transformation.matchMethods(new Predicate<TransformMethod>()
+        {
+            public boolean accept(TransformMethod method)
+            {
+                return (hasCorrectPrefix(method) || hasAnnotation(method)) && !method.isOverride();
+            }
 
-        buildMethodParameters(builder, method);
+            private boolean hasCorrectPrefix(TransformMethod method)
+            {
+                return method.getSignature().getMethodName().startsWith("on");
+            }
 
-        if (isNonVoid) builder.addln("))) return true;");
-        else builder.addln(");");
+            private boolean hasAnnotation(TransformMethod method)
+            {
+                return method.getAnnotation(OnEvent.class) != null;
+            }
+        });
+    }
 
-        builder.end();
+    private List<EventHandlerMethodInvoker> toInvokers(List<TransformMethod> methods)
+    {
+        List<EventHandlerMethodInvoker> result = CollectionFactory.newList();
 
-        // Indicate that the eventType is handled.
+        for (TransformMethod method : methods)
+        {
+            result.add(toInvoker(method));
+        }
 
-        model.addEventHandler(eventType);
+        return result;
     }
 
-    private String extractComponentId(TransformMethodSignature method, OnEvent annotation)
+    private EventHandlerMethodInvoker toInvoker(TransformMethod method)
     {
-        if (annotation != null) return annotation.component();
-
-        // Method name started with "on". Extract the component id, if present.
-
-        String name = method.getMethodName();
+        OnEvent annotation = method.getAnnotation(OnEvent.class);
 
-        int fromx = name.indexOf("From");
+        String methodName = method.getSignature().getMethodName();
 
-        if (fromx < 0) return "";
+        String eventType = extractEventType(methodName, annotation);
+        String componentId = extractComponentId(methodName, annotation);
 
-        return name.substring(fromx + 4);
-    }
+        String[] parameterTypes = method.getSignature().getParameterTypes();
 
-    private String extractEventType(TransformMethodSignature method, OnEvent annotation)
-    {
-        if (annotation != null) return annotation.value();
+        if (parameterTypes.length == 0)
+            return new BaseEventHandlerMethodInvoker(method, eventType, componentId);
 
-        // Method name started with "on". Extract the event type.
+        final List<EventHandlerMethodParameterSource> sources = CollectionFactory.newList();
 
-        String name = method.getMethodName();
+        // I'd refactor a bit more of this if Java had covariant return types.
 
-        int fromx = name.indexOf("From");
+        int contextIndex = 0;
+        boolean catchAll = false;
 
-        return fromx == -1 ? name.substring(2) : name.substring(2, fromx);
-    }
+        for (final String type : parameterTypes)
+        {
+            EventHandlerMethodParameterSource source = parameterTypeToSource.get(type);
 
-    private int getParameterCount(TransformMethodSignature method)
-    {
-        String[] types = method.getParameterTypes();
+            if (source != null)
+            {
+                sources.add(source);
+                catchAll = true;
+                continue;
+            }
 
-        if (types.length == 0) return 0;
+            // Note: probably safe to do the conversion to Class early (class load time)
+            // as parameters are rarely (if ever) component classes.
 
-        if (types.length == 1)
-        {
-            String soloType = types[0];
+            final int parameterIndex = contextIndex++;
 
-            if (soloType.equals(OBJECT_ARRAY_TYPE) || soloType.equals(EVENT_CONTEXT_TYPE) || soloType.equals(LIST_TYPE))
-                return ANY_NUMBER_OF_PARAMETERS;
+            sources.add(createParameterSource(type, parameterIndex));
         }
 
-        return types.length;
+        int minContextCount = catchAll ? 0 : contextIndex;
+
+        return createInvoker(method, eventType, componentId, minContextCount, sources);
     }
 
-    private void buildMethodParameters(BodyBuilder builder, TransformMethodSignature method)
+    private EventHandlerMethodInvoker createInvoker(TransformMethod method, String eventType, String componentId,
+            final int minContextCount, final List<EventHandlerMethodParameterSource> sources)
     {
-        int contextIndex = 0;
-
-        for (int i = 0; i < method.getParameterTypes().length; i++)
+        return new BaseEventHandlerMethodInvoker(method, eventType, componentId)
         {
-            if (i > 0) builder.add(", ");
-
-            String type = method.getParameterTypes()[i];
+            final int count = sources.size();
 
-            // Type Object[] is a special case, it gets all of the context parameters in one go.
-
-            if (type.equals(OBJECT_ARRAY_TYPE))
+            @Override
+            public int getMinContextValueCount()
             {
-                builder.add("$1.getContext()");
-                continue;
+                return minContextCount;
             }
 
-            // Added for TAPESTRY-2177
-
-            if (type.equals(EVENT_CONTEXT_TYPE))
+            @Override
+            protected Object[] constructParameters(ComponentEvent event)
             {
-                builder.add("$1.getEventContext()");
-                continue;
-            }
+                Object[] parameters = new Object[count];
+
+                for (int i = 0; i < count; i++)
+                {
+                    parameters[i] = sources.get(i).valueForEventHandlerMethodParameter(event);
+                }
 
-            // Added for TAPESTRY-1999
+                return parameters;
+            }
+        };
+    }
 
-            if (type.equals(LIST_TYPE))
+    private EventHandlerMethodParameterSource createParameterSource(final String type, final int parameterIndex)
+    {
+        return new EventHandlerMethodParameterSource()
+        {
+            public Object valueForEventHandlerMethodParameter(ComponentEvent event)
             {
-                builder.add("%s.asList($1.getContext())", Arrays.class.getName());
-                continue;
+                return event.coerceContext(parameterIndex, type);
             }
+        };
+    }
 
-            boolean isPrimitive = TransformUtils.isPrimitive(type);
-            String wrapperType = TransformUtils.getWrapperTypeName(type);
+    /**
+     * Returns the component id to match against, or the empty
+     * string if the component id is not specified. The component id
+     * is provided by the OnEvent annotation or (if that is not present)
+     * by the part of the method name following "From" ("onActionFromFoo").
+     */
+    private String extractComponentId(String methodName, OnEvent annotation)
+    {
+        if (annotation != null)
+            return annotation.component();
 
-            // Add a cast to the wrapper type up front
+        // Method name started with "on". Extract the component id, if present.
 
-            if (isPrimitive) builder.add("(");
+        int fromx = methodName.indexOf("From");
 
-            // A cast is always needed (i.e. from java.lang.Object to, say, java.lang.String, etc.).
-            // The wrapper type will be the actual type unless its a primitive, in which case it
-            // really will be the wrapper type.
+        if (fromx < 0)
+            return "";
 
-            builder.add("(%s)", wrapperType);
+        return methodName.substring(fromx + 4);
+    }
 
-            // The strings for desired type name will likely repeat a bit; it may be
-            // worth it to inject them as final fields. Could increase the number
-            // of constructor parameters pretty dramatically, however, and will reduce
-            // the readability of the output method bodies.
+    /**
+     * Returns the event name to match against, as specified in the annotation
+     * or (if the annotation is not present) extracted from the name of the method.
+     * "onActionFromFoo" or just "onAction".
+     */
+    private String extractEventType(String methodName, OnEvent annotation)
+    {
+        if (annotation != null)
+            return annotation.value();
 
-            builder.add("$1.coerceContext(%d, \"%s\")", contextIndex++, wrapperType);
+        int fromx = methodName.indexOf("From");
 
-            // and invoke a method on the cast value to get back to primitive
-            if (isPrimitive) builder.add(").%s()", TransformUtils.getUnwrapperMethodName(type));
-        }
+        // The first two characters are always "on" as in "onActionFromFoo".
+        return fromx == -1 ? methodName.substring(2) : methodName.substring(2, fromx);
     }
 }