You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2010/01/22 17:31:09 UTC
svn commit: r902155 - in
/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform:
ParameterConduit.java ParameterWorker.java ParameterWorker2.java
Author: hlship
Date: Fri Jan 22 16:31:08 2010
New Revision: 902155
URL: http://svn.apache.org/viewvc?rev=902155&view=rev
Log:
Build a new ComponentClassTransformWorker for handling the @Parameter annotation
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterConduit.java (with props)
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker2.java (with props)
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterConduit.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterConduit.java?rev=902155&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterConduit.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterConduit.java Fri Jan 22 16:31:08 2010
@@ -0,0 +1,80 @@
+// 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.Binding;
+import org.apache.tapestry5.annotations.Parameter;
+import org.apache.tapestry5.internal.InternalComponentResources;
+import org.apache.tapestry5.internal.bindings.LiteralBinding;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.runtime.Component;
+
+/**
+ * A facade around {@link Binding} and {@link InternalComponentResources} that is used to instrument
+ * fields with the {@link Parameter} annotation.
+ *
+ * @since 5.2.0
+ */
+public interface ParameterConduit
+{
+ /**
+ * Sets the default value for the parameter based on either the current value of the field,
+ * or on result from a default method. This is only used if the parameter is not otherwise
+ * bound.
+ *
+ * @param defaultValue
+ * an object (which will be wrapped as a {@link LiteralBinding}, or
+ * a {@link Binding} instance
+ */
+ void setDefault(Object defaultValue);
+
+ /**
+ * Reads the current value of the parameter (via the {@link Binding}) and uses the
+ * {@link TypeCoercer} to convert the actual value to one assignable to the underlying field.
+ * The actual read value may be cached.
+ *
+ * @throws RuntimeException
+ * if the parameter does not allow null but the current value is null
+ * @return current value (possibly null)
+ */
+ Object get();
+
+ /**
+ * Sets the value of the parameter, pushing it through the {@link Binding}.
+ *
+ * @param newValue
+ */
+ void set(Object newValue);
+
+ /**
+ * Determines if the parameter is actually bound.
+ *
+ * @return
+ */
+ boolean isBound();
+
+ /**
+ * Resets the conduit, clearing any <em>temporarily</em> cached data (from a non-invariant
+ * {@link Binding}).
+ */
+ void reset();
+
+ /**
+ * Invoked from the component's {@link Component#containingPageDidLoad()} lifecycle method, to
+ * finishing initializing
+ * the conduit prior to real use.
+ */
+ void load();
+}
Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterConduit.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java?rev=902155&r1=902154&r2=902155&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java Fri Jan 22 16:31:08 2010
@@ -1,4 +1,4 @@
-// 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.
@@ -14,19 +14,29 @@
package org.apache.tapestry5.internal.transform;
+import java.lang.reflect.Modifier;
+import java.util.Iterator;
+import java.util.List;
+
import org.apache.tapestry5.Binding;
import org.apache.tapestry5.annotations.Parameter;
-import org.apache.tapestry5.annotations.BindParameter;
-import org.apache.tapestry5.internal.*;
+import org.apache.tapestry5.internal.InternalComponentResources;
+import org.apache.tapestry5.internal.ParameterAccess;
+import org.apache.tapestry5.internal.ParameterChangeListener;
+import org.apache.tapestry5.internal.ParameterChangedEvent;
+import org.apache.tapestry5.internal.TapestryInternalUtils;
import org.apache.tapestry5.internal.bindings.LiteralBinding;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.util.BodyBuilder;
import org.apache.tapestry5.model.MutableComponentModel;
-import org.apache.tapestry5.services.*;
-
-import java.lang.reflect.Modifier;
-import java.util.Iterator;
-import java.util.List;
+import org.apache.tapestry5.services.BindingSource;
+import org.apache.tapestry5.services.ClassTransformation;
+import org.apache.tapestry5.services.ComponentClassTransformWorker;
+import org.apache.tapestry5.services.ComponentDefaultProvider;
+import org.apache.tapestry5.services.MethodFilter;
+import org.apache.tapestry5.services.TransformConstants;
+import org.apache.tapestry5.services.TransformMethodSignature;
+import org.apache.tapestry5.services.TransformUtils;
/**
* Responsible for identifying parameters via the {@link org.apache.tapestry5.annotations.Parameter} annotation on
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker2.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker2.java?rev=902155&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker2.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker2.java Fri Jan 22 16:31:08 2010
@@ -0,0 +1,400 @@
+// 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 java.lang.reflect.Modifier;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tapestry5.Binding;
+import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.annotations.Parameter;
+import org.apache.tapestry5.internal.InternalComponentResources;
+import org.apache.tapestry5.internal.ParameterAccess;
+import org.apache.tapestry5.internal.bindings.LiteralBinding;
+import org.apache.tapestry5.internal.services.ComponentClassCache;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.model.MutableComponentModel;
+import org.apache.tapestry5.services.BindingSource;
+import org.apache.tapestry5.services.ClassTransformation;
+import org.apache.tapestry5.services.ComponentClassTransformWorker;
+import org.apache.tapestry5.services.ComponentDefaultProvider;
+import org.apache.tapestry5.services.ComponentValueProvider;
+import org.apache.tapestry5.services.MethodFilter;
+import org.apache.tapestry5.services.TransformConstants;
+import org.apache.tapestry5.services.TransformMethodSignature;
+import org.apache.tapestry5.services.TransformUtils;
+
+public class ParameterWorker2 implements ComponentClassTransformWorker
+{
+ private final ComponentClassCache classCache;
+
+ private final BindingSource bindingSource;
+
+ private final ComponentDefaultProvider defaultProvider;
+
+ private final TypeCoercer typeCoercer;
+
+ public ParameterWorker2(ComponentClassCache classCache, BindingSource bindingSource,
+ ComponentDefaultProvider defaultProvider, TypeCoercer typeCoercer)
+ {
+ this.classCache = classCache;
+ this.bindingSource = bindingSource;
+ this.defaultProvider = defaultProvider;
+ this.typeCoercer = typeCoercer;
+ }
+
+ @Override
+ public void transform(ClassTransformation transformation, MutableComponentModel model)
+ {
+ List<String> fieldNames = transformation.findFieldsWithAnnotation(Parameter.class);
+
+ for (int pass = 0; pass < 2; pass++)
+ {
+ Iterator<String> i = fieldNames.iterator();
+
+ while (i.hasNext())
+ {
+ String fieldName = i.next();
+
+ Parameter annotation = transformation
+ .getFieldAnnotation(fieldName, Parameter.class);
+
+ // Process the principal annotations on the first pass, handle the others
+ // on the second pass.
+
+ boolean process = pass == 0 ? annotation.principal() : true;
+
+ if (process)
+ {
+ convertFieldIntoParameter(fieldName, annotation, transformation, model);
+
+ i.remove();
+ }
+ }
+ }
+
+ }
+
+ private void convertFieldIntoParameter(String fieldName, final Parameter annotation,
+ ClassTransformation transformation, MutableComponentModel model)
+ {
+ final String fieldTypeName = transformation.getFieldType(fieldName);
+
+ final String parameterName = getParameterName(fieldName, annotation.name());
+
+ final boolean enableCaching = annotation.cache();
+
+ model.addParameter(parameterName, annotation.required(), annotation.allowNull(), annotation
+ .defaultPrefix(), enableCaching);
+
+ ComponentValueProvider<ParameterConduit> provider = new ComponentValueProvider<ParameterConduit>()
+ {
+ // Invoked from the components' constructor. This causes a few issues (it would be
+ // better
+ // if there was a way to defer until the component's page loaded lifecycle method). The
+ // issues
+ // are addressed by deferring some behaviors until the reset() method.
+
+ @Override
+ public ParameterConduit get(ComponentResources resources)
+ {
+ final InternalComponentResources icr = (InternalComponentResources) resources;
+
+ final Class fieldType = classCache.forName(fieldTypeName);
+
+ // final ParameterAccess parameterAccess = icr.getParameterAccess(parameterName);
+
+ // Rely on some code generation in the component to set the default binding from
+ // the field, or from a default method.
+
+ return new ParameterConduit()
+ {
+ // Current cached value for the parameter.
+ private Object value;
+
+ // Default value for parameter, computed *once* at
+ // page load time.
+
+ private Object defaultValue;
+
+ private ParameterAccess parameterAccess;
+
+ private Binding defaultBinding;
+
+ boolean loaded = false;
+
+ // Is the current value of the binding cached in the
+ // value field?
+ private boolean cached = false;
+
+ // If the field is a primitive type, set its default value to false
+ // or zero. For non-primitives, null until we know better.
+
+ {
+ Class javaType = classCache.forName(fieldTypeName);
+
+ if (javaType.isPrimitive())
+ {
+ if (javaType == boolean.class)
+ {
+ defaultValue = false;
+ }
+ else
+ {
+ defaultValue = typeCoercer.coerce(0l, javaType);
+ }
+ }
+ }
+
+ private boolean isInvariant()
+ {
+ return parameterAccess.isInvariant();
+ }
+
+ private boolean isLoaded()
+ {
+ return loaded;
+ }
+
+ @Override
+ public void set(Object newValue)
+ {
+ // Assignments before the page is loaded ultimately exist to set the
+ // default value for the field. Often this is from the (original)
+ // constructor method,
+ // which is converted to a real method as part of the transformation.
+
+ if (!loaded)
+ {
+ defaultValue = newValue;
+ return;
+ }
+
+ // TODO: Disable parameter change listener before updating PA
+
+ // This will catch read-only or unbound parameters.
+
+ parameterAccess.write(newValue);
+
+ // TODO: Re-enable parameter change listener after updating PA
+
+ value = newValue;
+
+ // If caching is enabled for the parameter (the typical case) and the
+ // component is currently rendering, then the result
+ // can be cached in the ParameterConduit (until the component finished
+ // rendering).
+
+ cached = enableCaching && icr.isRendering();
+ }
+
+ @Override
+ public void reset()
+ {
+ if (!isInvariant())
+ {
+ value = defaultValue;
+ cached = false;
+ }
+ }
+
+ @Override
+ public void load()
+ {
+ boolean isBound = icr.isBound(parameterName);
+
+
+ // If it's bound at this point, that's because of an explicit binding
+ // in the template or @Component annotation.
+
+ if (!icr.isBound(parameterName))
+ {
+ // Otherwise, construct a default binding, or use one provided from
+ // the component.
+
+ Binding binding = getDefaultBindingForParameter();
+
+ if (binding != null)
+ icr.bindParameter(parameterName, binding);
+
+ defaultBinding = null;
+ }
+
+ parameterAccess = icr.getParameterAccess(parameterName);
+
+ loaded = true;
+
+ value = defaultValue;
+ }
+
+ private Binding getDefaultBindingForParameter()
+ {
+ if (InternalUtils.isNonBlank(annotation.value()))
+ return bindingSource.newBinding("default " + parameterName, icr,
+ annotation.defaultPrefix(), annotation.value());
+
+ if (annotation.autoconnect())
+ return defaultProvider.defaultBinding(parameterName, icr);
+
+ // Return (if not null) the binding from the setDefault() method which is set
+ // via a default method on the component, or from the field's initial value.
+
+ return defaultBinding;
+ }
+
+ @Override
+ public boolean isBound()
+ {
+ return parameterAccess.isBound();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object get()
+ {
+ if (!isLoaded()) { return defaultValue; }
+
+ if (cached || !isBound()) { return value; }
+
+ // Read the parameter's binding and cast it to the
+ // field's type.
+ Object result = parameterAccess.read(fieldType);
+
+ // If the value is invariant, we can cache it forever. Otherwise, we
+ // we may want to cache it for the remainder of the component render (if the
+ // component is currently rendering).
+
+ if (isInvariant() || (enableCaching && icr.isRendering()))
+ {
+ value = result;
+ cached = true;
+ }
+
+ return result;
+ }
+
+ @Override
+ public void setDefault(Object value)
+ {
+ if (value == null)
+ return;
+
+ if (value instanceof Binding)
+ {
+ defaultBinding = (Binding) value;
+ return;
+ }
+
+ defaultBinding = new LiteralBinding(null, "default " + parameterName, value);
+ }
+ };
+ }
+
+ };
+
+ // This has to be done in the constructor, to handle any field initializations
+
+ String conduitFieldName = transformation.addIndirectInjectedField(ParameterConduit.class,
+ parameterName + "$conduit", provider);
+
+ addCodeForParameterDefaultMethod(transformation, parameterName, conduitFieldName);
+
+ addParameterWriteMethod(transformation, fieldName, fieldTypeName, conduitFieldName);
+
+ addParameterReadMethod(transformation, fieldName, fieldTypeName, conduitFieldName);
+
+ transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, String
+ .format("%s.load();", conduitFieldName));
+
+ transformation.extendMethod(TransformConstants.POST_RENDER_CLEANUP_SIGNATURE, String
+ .format("%s.reset();", conduitFieldName));
+
+ transformation.removeField(fieldName);
+ }
+
+ private void addParameterReadMethod(ClassTransformation transformation, String fieldName,
+ final String fieldTypeName, String conduitFieldName)
+ {
+ String readMethodName = transformation.newMemberName("read_parameter", fieldName);
+
+ TransformMethodSignature readSig = new TransformMethodSignature(Modifier.PRIVATE,
+ fieldTypeName, readMethodName, null, null);
+
+ String cast = TransformUtils.getWrapperTypeName(fieldTypeName);
+
+ // The ($r) cast will convert the result to the method return type; generally
+ // this does nothing. but for primitive types, it will unwrap
+ // the wrapper type back to a primitive. We pass the desired type name
+ // to readParameter(), since its easier to convert it properly to
+ // a type on that end than in the generated code.
+
+ transformation.addMethod(readSig, String.format("return ($r) ((%s) %s.get());", cast,
+ conduitFieldName));
+
+ transformation.replaceReadAccess(fieldName, readMethodName);
+ }
+
+ private void addParameterWriteMethod(ClassTransformation transformation, String fieldName,
+ String fieldTypeName, String conduitFieldName)
+ {
+ String writeMethodName = transformation.newMemberName("update_parameter", fieldName);
+
+ TransformMethodSignature writeSig = new TransformMethodSignature(Modifier.PRIVATE, "void",
+ writeMethodName, new String[]
+ { fieldTypeName }, null);
+
+ transformation.addMethod(writeSig, String.format("%s.set(($w) $1);", conduitFieldName));
+
+ transformation.replaceWriteAccess(fieldName, writeMethodName);
+ }
+
+ private void addCodeForParameterDefaultMethod(ClassTransformation transformation,
+ final String parameterName, String conduitFieldName)
+ {
+ final String methodName = "default" + parameterName;
+
+ MethodFilter filter = new MethodFilter()
+ {
+ public boolean accept(TransformMethodSignature signature)
+ {
+ return signature.getParameterTypes().length == 0
+ && signature.getMethodName().equalsIgnoreCase(methodName);
+ }
+ };
+
+ // This will match exactly 0 or 1 methods, and if it matches, we know the name
+ // of the method.
+
+ List<TransformMethodSignature> signatures = transformation.findMethods(filter);
+
+ if (signatures.isEmpty())
+ return;
+
+ String actualMethodName = signatures.get(0).getMethodName();
+
+ transformation.extendExistingMethod(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE,
+ String.format("%s.setDefault(($w) %s());", conduitFieldName, actualMethodName));
+ }
+
+ private static String getParameterName(String fieldName, String annotatedName)
+ {
+ if (InternalUtils.isNonBlank(annotatedName))
+ return annotatedName;
+
+ return InternalUtils.stripMemberName(fieldName);
+ }
+}
Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker2.java
------------------------------------------------------------------------------
svn:eol-style = native