Coverage Report - org.apache.tapestry5.internal.transform.OnEventWorker
 
Classes in this File Line Coverage Branch Coverage Complexity
OnEventWorker
100%
72/72
100%
42/42
0
OnEventWorker$1
100%
4/4
100%
8/8
0
 
 1  
 // Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
 2  
 //
 3  
 // Licensed under the Apache License, Version 2.0 (the "License");
 4  
 // you may not use this file except in compliance with the License.
 5  
 // You may obtain a copy of the License at
 6  
 //
 7  
 //     http://www.apache.org/licenses/LICENSE-2.0
 8  
 //
 9  
 // Unless required by applicable law or agreed to in writing, software
 10  
 // distributed under the License is distributed on an "AS IS" BASIS,
 11  
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  
 // See the License for the specific language governing permissions and
 13  
 // limitations under the License.
 14  
 
 15  
 package org.apache.tapestry5.internal.transform;
 16  
 
 17  
 import org.apache.tapestry5.EventContext;
 18  
 import org.apache.tapestry5.annotations.OnEvent;
 19  
 import org.apache.tapestry5.ioc.util.BodyBuilder;
 20  
 import org.apache.tapestry5.model.MutableComponentModel;
 21  
 import org.apache.tapestry5.services.*;
 22  
 
 23  
 import java.util.Arrays;
 24  
 import java.util.List;
 25  
 
 26  
 /**
 27  
  * Provides implementations of the {@link org.apache.tapestry5.runtime.Component#dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)}
 28  
  * method, based on {@link org.apache.tapestry5.annotations.OnEvent} annotations.
 29  
  */
 30  58
 public class OnEventWorker implements ComponentClassTransformWorker
 31  
 {
 32  
     static final String OBJECT_ARRAY_TYPE = "java.lang.Object[]";
 33  
 
 34  2
     static final String EVENT_CONTEXT_TYPE = EventContext.class.getName();
 35  
 
 36  2
     static final String LIST_TYPE = List.class.getName();
 37  
 
 38  
     private final static int ANY_NUMBER_OF_PARAMETERS = -1;
 39  
 
 40  
     public void transform(final ClassTransformation transformation, MutableComponentModel model)
 41  
     {
 42  814
         MethodFilter filter = new MethodFilter()
 43  
         {
 44  
             public boolean accept(TransformMethodSignature signature)
 45  
             {
 46  14472
                 return (hasCorrectPrefix(signature) || hasAnnotation(signature)) &&
 47  
                         !transformation.isMethodOverride(signature);
 48  
             }
 49  
 
 50  
             private boolean hasCorrectPrefix(TransformMethodSignature signature)
 51  
             {
 52  14472
                 return signature.getMethodName().startsWith("on");
 53  
             }
 54  
 
 55  814
             private boolean hasAnnotation(TransformMethodSignature signature)
 56  
             {
 57  14094
                 return transformation.getMethodAnnotation(signature, OnEvent.class) != null;
 58  
             }
 59  
         };
 60  
 
 61  814
         List<TransformMethodSignature> methods = transformation.findMethods(filter);
 62  
 
 63  
         // No methods, no work.
 64  
 
 65  814
         if (methods.isEmpty()) return;
 66  
 
 67  208
         BodyBuilder builder = new BodyBuilder();
 68  208
         builder.begin();
 69  
 
 70  208
         builder.addln("if ($1.isAborted()) return $_;");
 71  
 
 72  208
         builder.addln("try");
 73  208
         builder.begin();
 74  
 
 75  208
         for (TransformMethodSignature method : methods)
 76  410
             addCodeForMethod(builder, method, transformation, model);
 77  
 
 78  208
         builder.end(); // try
 79  
 
 80  
         // Runtime exceptions pass right through.
 81  
 
 82  208
         builder.addln("catch (RuntimeException ex) { throw ex; }");
 83  
 
 84  
         // Wrap others in a RuntimeException to communicate them up.
 85  
 
 86  208
         builder.addln("catch (Exception ex) { throw new RuntimeException(ex); } ");
 87  
 
 88  208
         builder.end();
 89  
 
 90  208
         transformation.extendMethod(TransformConstants.DISPATCH_COMPONENT_EVENT, builder.toString());
 91  208
     }
 92  
 
 93  
 
 94  
     private void addCodeForMethod(BodyBuilder builder, TransformMethodSignature method,
 95  
                                   ClassTransformation transformation, MutableComponentModel model)
 96  
     {
 97  
         // $1 is the event
 98  
 
 99  410
         int parameterCount = getParameterCount(method);
 100  
 
 101  410
         OnEvent annotation = transformation.getMethodAnnotation(method, OnEvent.class);
 102  
 
 103  410
         String eventType = extractEventType(method, annotation);
 104  
 
 105  410
         String componentId = extractComponentId(method, annotation);
 106  
 
 107  
 
 108  410
         builder.addln("if ($1.matches(\"%s\", \"%s\", %d))", eventType, componentId, parameterCount);
 109  410
         builder.begin();
 110  
 
 111  
         // Ensure that we return true, because *some* event handler method was invoked,
 112  
         // even if it chose not to abort the event.
 113  
 
 114  410
         builder.addln("$_ = true;");
 115  
 
 116  410
         builder.addln("$1.setMethodDescription(\"%s\");", transformation.getMethodIdentifier(method));
 117  
 
 118  410
         boolean isNonVoid = !method.getReturnType().equals("void");
 119  
 
 120  
         // Store the result, converting primitives to wrappers automatically.
 121  
 
 122  410
         if (isNonVoid) builder.add("if ($1.storeResult(($w) ");
 123  
 
 124  410
         builder.add("%s(", method.getMethodName());
 125  
 
 126  410
         buildMethodParameters(builder, method);
 127  
 
 128  410
         if (isNonVoid) builder.addln("))) return true;");
 129  228
         else builder.addln(");");
 130  
 
 131  410
         builder.end();
 132  
 
 133  
         // Indicate that the eventType is handled.
 134  
 
 135  410
         model.addEventHandler(eventType);
 136  410
     }
 137  
 
 138  
     private String extractComponentId(TransformMethodSignature method, OnEvent annotation)
 139  
     {
 140  410
         if (annotation != null) return annotation.component();
 141  
 
 142  
         // Method name started with "on". Extract the component id, if present.
 143  
 
 144  370
         String name = method.getMethodName();
 145  
 
 146  370
         int fromx = name.indexOf("From");
 147  
 
 148  370
         if (fromx < 0) return "";
 149  
 
 150  196
         return name.substring(fromx + 4);
 151  
     }
 152  
 
 153  
     private String extractEventType(TransformMethodSignature method, OnEvent annotation)
 154  
     {
 155  410
         if (annotation != null) return annotation.value();
 156  
 
 157  
         // Method name started with "on". Extract the event type.
 158  
 
 159  370
         String name = method.getMethodName();
 160  
 
 161  370
         int fromx = name.indexOf("From");
 162  
 
 163  370
         return fromx == -1 ? name.substring(2) : name.substring(2, fromx);
 164  
     }
 165  
 
 166  
     private int getParameterCount(TransformMethodSignature method)
 167  
     {
 168  410
         String[] types = method.getParameterTypes();
 169  
 
 170  410
         if (types.length == 0) return 0;
 171  
 
 172  116
         if (types.length == 1)
 173  
         {
 174  110
             String soloType = types[0];
 175  
 
 176  110
             if (soloType.equals(OBJECT_ARRAY_TYPE) || soloType.equals(EVENT_CONTEXT_TYPE) || soloType.equals(LIST_TYPE))
 177  32
                 return ANY_NUMBER_OF_PARAMETERS;
 178  
         }
 179  
 
 180  84
         return types.length;
 181  
     }
 182  
 
 183  
     private void buildMethodParameters(BodyBuilder builder, TransformMethodSignature method)
 184  
     {
 185  410
         int contextIndex = 0;
 186  
 
 187  532
         for (int i = 0; i < method.getParameterTypes().length; i++)
 188  
         {
 189  122
             if (i > 0) builder.add(", ");
 190  
 
 191  122
             String type = method.getParameterTypes()[i];
 192  
 
 193  
             // Type Object[] is a special case, it gets all of the context parameters in one go.
 194  
 
 195  122
             if (type.equals(OBJECT_ARRAY_TYPE))
 196  
             {
 197  6
                 builder.add("$1.getContext()");
 198  6
                 continue;
 199  
             }
 200  
 
 201  
             // Added for TAPESTRY-2177
 202  
 
 203  116
             if (type.equals(EVENT_CONTEXT_TYPE))
 204  
             {
 205  24
                 builder.add("$1.getEventContext()");
 206  24
                 continue;
 207  
             }
 208  
 
 209  
             // Added for TAPESTRY-1999
 210  
 
 211  92
             if (type.equals(LIST_TYPE))
 212  
             {
 213  2
                 builder.add("%s.asList($1.getContext())", Arrays.class.getName());
 214  2
                 continue;
 215  
             }
 216  
 
 217  90
             boolean isPrimitive = TransformUtils.isPrimitive(type);
 218  90
             String wrapperType = TransformUtils.getWrapperTypeName(type);
 219  
 
 220  
             // Add a cast to the wrapper type up front
 221  
 
 222  90
             if (isPrimitive) builder.add("(");
 223  
 
 224  
             // A cast is always needed (i.e. from java.lang.Object to, say, java.lang.String, etc.).
 225  
             // The wrapper type will be the actual type unless its a primitive, in which case it
 226  
             // really will be the wrapper type.
 227  
 
 228  90
             builder.add("(%s)", wrapperType);
 229  
 
 230  
             // The strings for desired type name will likely repeat a bit; it may be
 231  
             // worth it to inject them as final fields. Could increase the number
 232  
             // of constructor parameters pretty dramatically, however, and will reduce
 233  
             // the readability of the output method bodies.
 234  
 
 235  90
             builder.add("$1.coerceContext(%d, \"%s\")", contextIndex++, wrapperType);
 236  
 
 237  
             // and invoke a method on the cast value to get back to primitive
 238  90
             if (isPrimitive) builder.add(").%s()", TransformUtils.getUnwrapperMethodName(type));
 239  
         }
 240  410
     }
 241  
 }