001: // Copyright 2006, 2007 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.tapestry.internal.services;
016:
017: import java.lang.annotation.Annotation;
018: import java.util.Iterator;
019: import java.util.List;
020:
021: import org.apache.tapestry.MarkupWriter;
022: import org.apache.tapestry.internal.util.MethodInvocationBuilder;
023: import org.apache.tapestry.ioc.internal.util.InternalUtils;
024: import org.apache.tapestry.ioc.util.BodyBuilder;
025: import org.apache.tapestry.model.MutableComponentModel;
026: import org.apache.tapestry.services.ClassTransformation;
027: import org.apache.tapestry.services.ComponentClassTransformWorker;
028: import org.apache.tapestry.services.MethodFilter;
029: import org.apache.tapestry.services.MethodSignature;
030:
031: /**
032: * Converts one of the methods of {@link org.apache.tapestry.runtime.Component} into a chain of
033: * command that, itself, invokes certain methods (render phase methods) marked with an annotation,
034: * or named in a specific way.
035: */
036: public class ComponentLifecycleMethodWorker implements
037: ComponentClassTransformWorker {
038: private static final String CHECK_ABORT_FLAG = "if ($2.isAborted()) return;";
039:
040: private final Class<? extends Annotation> _methodAnnotation;
041:
042: private final MethodSignature _lifecycleMethodSignature;
043:
044: private final String _lifecycleMethodName;
045:
046: private final boolean _reverse;
047:
048: private final MethodInvocationBuilder _invocationBuilder = new MethodInvocationBuilder();
049:
050: /**
051: * Normal method invocation: parent class, then methods in ascending alphabetical order. Reverse
052: * order: method in descending alphabetical order, then parent class.
053: *
054: * @param lifecycleMethodSignature
055: * the signature of the method to be implemented in the component class
056: * @param methodAnnotation
057: * the class of the corresponding annotation
058: * @param reverse
059: * if true, the normal method invocation order is reversed
060: */
061: public ComponentLifecycleMethodWorker(
062: MethodSignature lifecycleMethodSignature,
063: Class<? extends Annotation> methodAnnotation,
064: boolean reverse) {
065: _lifecycleMethodSignature = lifecycleMethodSignature;
066: _methodAnnotation = methodAnnotation;
067: _reverse = reverse;
068: _lifecycleMethodName = lifecycleMethodSignature.getMethodName();
069:
070: // If we ever add more parameters to the methods, then we can add more to the invocation
071: // builder.
072: // *Never* expose the Event parameter ($2), it is for internal use only.
073:
074: _invocationBuilder.addParameter(MarkupWriter.class.getName(),
075: "$1");
076: }
077:
078: @Override
079: public String toString() {
080: return String.format("ComponentLifecycleMethodWorker[%s]",
081: _methodAnnotation.getName());
082: }
083:
084: public void transform(final ClassTransformation transformation,
085: MutableComponentModel model) {
086: MethodFilter filter = new MethodFilter() {
087: public boolean accept(MethodSignature signature) {
088: // These methods get added to base classes and otherwise fall into this filter. If
089: // we don't
090: // include this filter, then we get endless loops.
091:
092: if (signature.equals(_lifecycleMethodSignature))
093: return false;
094:
095: // A degenerate case would be a method, say beginRender(), with an conflicting
096: // annotation, say @AfterRender. In that case, this code is broken, as the method
097: // will be invoked for both phases!
098:
099: return signature.getMethodName().equals(
100: _lifecycleMethodName)
101: || transformation.getMethodAnnotation(
102: signature, _methodAnnotation) != null;
103: }
104: };
105:
106: List<MethodSignature> methods = transformation
107: .findMethods(filter);
108:
109: // Except in the root class, don't bother to add a new method unless there's something to
110: // call (beside super).
111:
112: if (methods.isEmpty())
113: return;
114:
115: BodyBuilder builder = new BodyBuilder();
116: builder.begin();
117:
118: // If in a subclass, and in normal order mode, invoke the super class version first.
119:
120: if (!(_reverse || model.isRootClass())) {
121: builder.addln("super.%s($$);", _lifecycleMethodName);
122: builder.addln(CHECK_ABORT_FLAG);
123: }
124:
125: Iterator<MethodSignature> i = _reverse ? InternalUtils
126: .reverseIterator(methods) : methods.iterator();
127:
128: while (i.hasNext())
129: addMethodCallToBody(builder, i.next(), transformation);
130:
131: // In reverse order in a a subclass, invoke the super method last.
132:
133: if (_reverse && !model.isRootClass())
134: builder.addln("super.%s($$);", _lifecycleMethodName);
135:
136: builder.end();
137:
138: // Let's see if this works; for base classes, we are adding an empty method the adding a
139: // non-empty
140: // method "on top of it".
141:
142: transformation.addMethod(_lifecycleMethodSignature, builder
143: .toString());
144: }
145:
146: private void addMethodCallToBody(BodyBuilder builder,
147: MethodSignature sig, ClassTransformation transformation) {
148: boolean isVoid = sig.getReturnType().equals("void");
149:
150: if (!isVoid) {
151: // If we're not going to invoke storeResult(), then there's no reason to invoke
152: // setSource().
153:
154: builder.addln("$2.setSource(this, \"%s\");", transformation
155: .getMethodIdentifier(sig));
156: builder.add("if ($2.storeResult(($w) ");
157: }
158:
159: // This is the best part; the method can even be private and this still works. It's a lot
160: // like how javac enables access to private members for inner classes (by introducing
161: // synthetic, static methods).
162:
163: builder.add(_invocationBuilder.buildMethodInvocation(sig,
164: transformation));
165:
166: // Now, if non void ...
167:
168: if (!isVoid) {
169: // Complete the call to storeResult(). If storeResult() returns true, then
170: // the event is aborted and no further processing is required.
171:
172: builder.addln(")) return;");
173: } else
174: builder.addln(";");
175: }
176:
177: }
|