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:49 UTC
svn commit: r902157 [1/2] - in /tapestry/tapestry5/trunk/tapestry-core/src:
main/java/org/apache/tapestry5/ main/java/org/apache/tapestry5/annotations/
main/java/org/apache/tapestry5/internal/
main/java/org/apache/tapestry5/internal/model/ main/java/or...
Author: hlship
Date: Fri Jan 22 16:31:47 2010
New Revision: 902157
URL: http://svn.apache.org/viewvc?rev=902157&view=rev
Log:
Rewrite BindParameterWorker using the new ClassTransformation APIs
Removed:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangeListener.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangedEvent.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker2.java
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BindParameterWorker.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/TransformMessages.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/ComponentModel.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableComponentModel.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/structure/StructureStrings.properties
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/transform/TransformStrings.properties
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/mixins/TextOnlyOnDisabled.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImplTest.java
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java Fri Jan 22 16:31:47 2010
@@ -4,7 +4,7 @@
// 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,
@@ -76,9 +76,11 @@
/**
* Returns an embedded component, given the component's id.
- *
- * @param embeddedId selects the embedded component (case is ignored)
- * @throws IllegalArgumentException if this component does not contain a component with the given id
+ *
+ * @param embeddedId
+ * selects the embedded component (case is ignored)
+ * @throws IllegalArgumentException
+ * if this component does not contain a component with the given id
*/
Component getEmbeddedComponent(String embeddedId);
@@ -90,9 +92,11 @@
/**
* Obtains an annotation provided by a parameter.
- *
- * @param parameterName name of parameter to search for the annotation
- * @param annotationType the type of annotation
+ *
+ * @param parameterName
+ * name of parameter to search for the annotation
+ * @param annotationType
+ * the type of annotation
* @return the annotation if found or null otherwise
*/
<T extends Annotation> T getParameterAnnotation(String parameterName, Class<T> annotationType);
@@ -100,8 +104,9 @@
/**
* Indentifies all parameters that are not formal parameters and writes each as a attribute/value pair into the
* current element of the markup writer.
- *
- * @param writer to which {@link MarkupWriter#attributes(Object[]) attributes} will be written
+ *
+ * @param writer
+ * to which {@link MarkupWriter#attributes(Object[]) attributes} will be written
*/
void renderInformalParameters(MarkupWriter writer);
@@ -115,8 +120,9 @@
* with property bindings, and is used to determine the actual type of the property, rather than the type of
* parameter (remember that type coercion automatically occurs, which can mask significant differences between the
* parameter type and the bound property type).
- *
- * @param parameterName used to select the parameter (case is ignored)
+ *
+ * @param parameterName
+ * used to select the parameter (case is ignored)
* @return the type of the bound parameter, or null if the parameter is not bound
* @see Binding#getBindingType()
*/
@@ -124,35 +130,42 @@
/**
* Returns an annotation provider, used to obtain annotations related to the parameter.
- *
- * @param parameterName used to select the parameter (case is ignored)
+ *
+ * @param parameterName
+ * used to select the parameter (case is ignored)
* @return the annotation provider, or null if the parameter is not bound
*/
AnnotationProvider getAnnotationProvider(String parameterName);
/**
* Used to access an informal parameter that's a Block.
- *
- * @param parameterName the name of the informal parameter (case is ignored)
+ *
+ * @param parameterName
+ * the name of the informal parameter (case is ignored)
* @return the informal Block parameter, or null if not bound
*/
Block getBlockParameter(String parameterName);
/**
* Returns a previously stored render variable.
- *
- * @param name of the variable (case will be ignored)
+ *
+ * @param name
+ * of the variable (case will be ignored)
* @return the variable's value
- * @throws IllegalArgumentException if the name doesn't correspond to a stored value
+ * @throws IllegalArgumentException
+ * if the name doesn't correspond to a stored value
*/
Object getRenderVariable(String name);
/**
* Stores a render variable, accessible with the provided name.
- *
- * @param name of value to store
- * @param value value to store (may not be null)
- * @throws IllegalStateException if the component is not currently rendering
+ *
+ * @param name
+ * of value to store
+ * @param value
+ * value to store (may not be null)
+ * @throws IllegalStateException
+ * if the component is not currently rendering
*/
void storeRenderVariable(String name, Object value);
@@ -161,9 +174,8 @@
*/
void addPageLifecycleListener(PageLifecycleListener listener);
-
/**
- * Discards all persistent field changes for the page containing the component. Changes are eliminated from
+ * Discards all persistent field changes for the page containing the component. Changes are eliminated from
* persistent storage (such as the {@link org.apache.tapestry5.services.Session}) which will take effect in the
* <em>next</em> request (the attached page instance is not affected).
*/
@@ -171,20 +183,20 @@
/**
* Returns the name of element that represents the component in its template, or null if not known.
- *
+ *
* @return the element name or null
*/
String getElementName();
/**
* Returns a list of the names of any informal parameters bound to this component.
- *
+ *
* @return the name sorted alphabetically
* @see org.apache.tapestry5.annotations.SupportsInformalParameters
*/
List<String> getInformalParameterNames();
- /**
+/**
* Reads an informal parameter and {@linkplain org.apache.tapestry5.ioc.services.TypeCoercer coercers) the bound
* value to the indicated type.
*
@@ -193,4 +205,12 @@
* @return instance of type
*/
<T> T getInformalParameter(String name, Class<T> type);
+
+ /**
+ * Returns true if these resources represent a mixin to another component. The component is the
+ * {@linkplain #getContainerResources() container} of this resources.
+ *
+ * @since 5.2.0
+ */
+ boolean isMixin();
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java Fri Jan 22 16:31:47 2010
@@ -4,7 +4,7 @@
// 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,
@@ -24,18 +24,16 @@
import org.apache.tapestry5.internal.transform.BindParameterWorker;
import org.apache.tapestry5.ioc.annotations.UseWith;
-
/**
* Designates a field in a mixin which is bound to the parameter of the containing
* component corresponding to the value of the annotation. If no value is specified,
* the bound parameter name is assumed to match the field name of the mixin.
- *
* For example, a mixin intended to work with form fields would define a field named
- * "value", marked by this annotation. The user-variable bound to the component's value
+ * "value", marked by this annotation. The user-variable bound to the component's value
* parameter would ultimately be bound in a chain:
* uservariable <=> mixin.value <=> component.value.
* Changes to any one value in the chain will be propagated accordingly.
- *
+ *
* @since 5.2.0
* @see BindParameterWorker
*/
@@ -49,9 +47,13 @@
* @return the name of the mixin bound-parameter, exactly as for the Parameter annotation.
*/
String name() default "";
+
/**
- * @return the name(s) of the parent parameter to bind. Defaults to the name of the mixin field. If more than one
- * name is specified, the first name matching a declared parameter of the core component will be used.
+ * @return the name(s) of the parent parameter to bind. Defaults to the name of the mixin field.
+ * If more than one
+ * name is specified, the first name matching a declared parameter of the core component
+ * will be used.
*/
- String[] value() default {""};
+ String[] value() default
+ { "" };
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java Fri Jan 22 16:31:47 2010
@@ -20,7 +20,6 @@
import org.apache.tapestry5.internal.structure.PageResetListener;
import org.apache.tapestry5.internal.transform.ParameterConduit;
import org.apache.tapestry5.runtime.RenderQueue;
-import org.apache.tapestry5.services.FieldValueConduit;
/**
* An extension of {@link org.apache.tapestry5.ComponentResources} that represents additional
@@ -72,22 +71,6 @@
ParameterAccess getParameterAccess(String parameterName);
/**
- * Gets access object suitable for handling mixin fields which are bound to a parameter of the
- * core component
- *
- * @param boundParameterName
- * the name of the mixin field that should be linked to the core component's field.
- * @param parentParameterNames
- * the list of parameter names to try in the parent. The first name that matches a
- * declared parameter name in the core component will be used. This allows
- * BindParameter to be used with
- * mixins that have a similar parameter type with different parameter names (eg:
- * @since 5.2.0
- */
- ParameterAccess getContainerBoundParameterAccess(String boundParameterName,
- String... parentParameterNames);
-
- /**
* Delegates to
* {@link Page#addResetListener(org.apache.tapestry5.internal.structure.PageResetListener)}.
*
@@ -97,7 +80,7 @@
void addPageResetListener(PageResetListener listener);
/** @since 5.2.0 */
- FieldValueConduit getParameterConduit(String parameterName);
+ ParameterConduit getParameterConduit(String parameterName);
/** @since 5.2.0 */
void setParameterConduit(String parameterName, ParameterConduit conduit);
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java Fri Jan 22 16:31:47 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,
@@ -28,7 +28,7 @@
{
/**
* Returns true if the component has finished loading. Initially, this value will be false.
- *
+ *
* @see org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidLoad()
*/
boolean isLoaded();
@@ -41,8 +41,9 @@
/**
* Returns the binding for the given parameter name, or null.
- *
- * @param parameterName name of component parameter
+ *
+ * @param parameterName
+ * name of component parameter
* @return binding if bound, or null
* @since 5.1.0.0
*/
@@ -50,31 +51,18 @@
/**
* Returns the mixin instance for the fully qualfied mixin class name.
- *
- * @param mixinClassName fully qualified class name
+ *
+ * @param mixinClassName
+ * fully qualified class name
* @return IllegalArgumentException if no such mixin is associated with the core component
*/
Component getMixinByClassName(String mixinClassName);
/**
- * Returns true if the mixin named by mixinClassName is associated with the core component
- * @param mixinClassName fully qualified class name
- * @since 5.2.0.0
- */
- boolean isMixingIn(String mixinClassName);
-
- /**
- * Registers an action that should be executed immediately after page load.
- * @since 5.2.0.0
- */
- void deferLoadAction(Runnable action);
-
- /**
* Constructs a map linking informal parameters to the corresponding bindings.
- *
+ *
* @return map, possible empty
*/
Map<String, Binding> getInformalParameterBindings();
-
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java Fri Jan 22 16:31:47 2010
@@ -1,10 +1,10 @@
-// Copyright 2008, 2009 The Apache Software Foundation
+// Copyright 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,
@@ -19,49 +19,40 @@
/**
* A wrapper around a named parameter provided by {@link org.apache.tapestry5.internal.InternalComponentResources}. The
* parameter may be bound or unbound.
- *
+ *
* @since 5.0.19
*/
public interface ParameterAccess extends AnnotationProvider
{
/**
* Is the parameter bound?
- *
+ *
* @return true if bound
*/
boolean isBound();
/**
- * Reads the current value of the parameter via the parameter's {@link org.apache.tapestry5.Binding}. This method is
- * intended for use from generated code (where it is easier to specify the type as a name than a Class instance).
- *
- * @param desiredTypeName the fully qualified name of the Java type to coerce the result to
- * @return the value of the parameter, or null if not bound
- * @throws NullPointerException if the parameter's value is null and null is not allowed
- */
- Object read(String desiredTypeName);
-
-
- /**
* Reads the value of a parameter, via the parameter's {@link org.apache.tapestry5.Binding}.
- *
+ *
* @param <T>
- * @param expectedType the expected type of parameter
+ * @param expectedType
+ * the expected type of parameter
* @return the value for the parameter, or null if the parameter is not bound.
*/
<T> T read(Class<T> expectedType);
/**
- * Updates the parameter to a new value. If the parameter is not bound, this method does nothing.
- *
- * @param parameterValue new value for parameter
+ * Updates the parameter to a new value. If the parameter is not bound, this method does nothing.
+ *
+ * @param parameterValue
+ * new value for parameter
* @param <T>
*/
<T> void write(T parameterValue);
/**
* Returns true if the binding is bound, and the binding is invariant.
- *
+ *
* @return true if invariant
* @see org.apache.tapestry5.Binding#isInvariant()
*/
@@ -72,30 +63,9 @@
* with property bindings, and is used to determine the actual type of the property, rather than the type of
* parameter (remember that type coercion automatically occurs, which can mask significant differences between the
* parameter type and the bound property type).
- *
+ *
* @return the type of the bound parameter, or null if the parameter is not bound
* @see org.apache.tapestry5.Binding#getBindingType()
*/
Class getBoundType();
-
- /**
- * Registers the given ParameterChangeListener. Registering a listener which is already
- * registered is silently ignored.
- * @param listener
- * @since 5.2.0.0
- */
- void registerParameterChangeListener(ParameterChangeListener listener);
-
- /**
- * Unregisters the given listener. Unregistering a listener that isn't registered is not an error.
- * @since 5.2.0.0
- */
- void unregisterParameterChangeListener(ParameterChangeListener listener);
-
- /**
- * @return true if the parameter value should be cached within a component during rendering
- * @since 5.2.0.0
- */
- boolean shouldCache();
-
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java Fri Jan 22 16:31:47 2010
@@ -4,7 +4,7 @@
// 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,
@@ -66,8 +66,8 @@
private Map<String, Boolean> handledEvents;
- public MutableComponentModelImpl(String componentClassName, Logger logger, Resource baseResource,
- ComponentModel parentModel)
+ public MutableComponentModelImpl(String componentClassName, Logger logger,
+ Resource baseResource, ComponentModel parentModel)
{
this.componentClassName = componentClassName;
this.logger = logger;
@@ -106,7 +106,8 @@
return componentClassName;
}
- public void addParameter(String name, boolean required, boolean allowNull, String defaultBindingPrefix, boolean cached)
+ public void addParameter(String name, boolean required, boolean allowNull,
+ String defaultBindingPrefix, boolean cached)
{
Defense.notBlank(name, "name");
Defense.notBlank(defaultBindingPrefix, "defaultBindingPrefix");
@@ -117,36 +118,49 @@
parameters = CollectionFactory.newCaseInsensitiveMap();
if (parameters.containsKey(name))
- throw new IllegalArgumentException(ModelMessages.duplicateParameter(name, componentClassName));
+ throw new IllegalArgumentException(ModelMessages.duplicateParameter(name,
+ componentClassName));
- parameters.put(name, new ParameterModelImpl(name, required, allowNull, defaultBindingPrefix,cached));
+ parameters.put(name, new ParameterModelImpl(name, required, allowNull,
+ defaultBindingPrefix, cached));
}
- public void addParameter(String name, boolean required, boolean allowNull, String defaultBindingPrefix)
- {
- //assume /false/ for the default because:
- //if the parameter is actually cached, the only effect will be to reduce that optimization in certain
- //scenarios (mixin BindParameter). But if the value is NOT cached but we say it is,
- //we'll get incorrect behavior.
- addParameter(name,required,allowNull,defaultBindingPrefix,false);
+ public void addParameter(String name, boolean required, boolean allowNull,
+ String defaultBindingPrefix)
+ {
+ // assume /false/ for the default because:
+ // if the parameter is actually cached, the only effect will be to reduce that optimization
+ // in certain
+ // scenarios (mixin BindParameter). But if the value is NOT cached but we say it is,
+ // we'll get incorrect behavior.
+ addParameter(name, required, allowNull, defaultBindingPrefix, false);
}
public ParameterModel getParameterModel(String parameterName)
{
ParameterModel result = InternalUtils.get(parameters, parameterName.toLowerCase());
- if (result == null && parentModel != null) result = parentModel.getParameterModel(parameterName);
+ if (result == null && parentModel != null)
+ result = parentModel.getParameterModel(parameterName);
return result;
}
+ @Override
+ public boolean isFormalParameter(String parameterName)
+ {
+ return getParameterModel(parameterName) != null;
+ }
+
public List<String> getParameterNames()
{
List<String> names = CollectionFactory.newList();
- if (parameters != null) names.addAll(parameters.keySet());
+ if (parameters != null)
+ names.addAll(parameters.keySet());
- if (parentModel != null) names.addAll(parentModel.getParameterNames());
+ if (parentModel != null)
+ names.addAll(parentModel.getParameterNames());
Collections.sort(names);
@@ -158,19 +172,19 @@
return InternalUtils.sortedKeys(parameters);
}
- public MutableEmbeddedComponentModel addEmbeddedComponent(String id, String type, String componentClassName,
- boolean inheritInformalParameters, Location location)
+ public MutableEmbeddedComponentModel addEmbeddedComponent(String id, String type,
+ String componentClassName, boolean inheritInformalParameters, Location location)
{
// TODO: Parent compent model? Or would we simply override the parent?
- if (embeddedComponents == null) embeddedComponents = CollectionFactory.newCaseInsensitiveMap();
+ if (embeddedComponents == null)
+ embeddedComponents = CollectionFactory.newCaseInsensitiveMap();
else if (embeddedComponents.containsKey(id))
- throw new IllegalArgumentException(ModelMessages.duplicateComponentId(id, this.componentClassName));
+ throw new IllegalArgumentException(ModelMessages.duplicateComponentId(id,
+ this.componentClassName));
- MutableEmbeddedComponentModel embedded = new MutableEmbeddedComponentModelImpl(id, type, componentClassName,
- this.componentClassName,
- inheritInformalParameters,
- location);
+ MutableEmbeddedComponentModel embedded = new MutableEmbeddedComponentModelImpl(id, type,
+ componentClassName, this.componentClassName, inheritInformalParameters, location);
embeddedComponents.put(id, embedded);
@@ -181,9 +195,11 @@
{
List<String> result = CollectionFactory.newList();
- if (embeddedComponents != null) result.addAll(embeddedComponents.keySet());
+ if (embeddedComponents != null)
+ result.addAll(embeddedComponents.keySet());
- if (parentModel != null) result.addAll(parentModel.getEmbeddedComponentIds());
+ if (parentModel != null)
+ result.addAll(parentModel.getEmbeddedComponentIds());
Collections.sort(result);
@@ -194,7 +210,8 @@
{
EmbeddedComponentModel result = InternalUtils.get(embeddedComponents, componentId);
- if (result == null && parentModel != null) result = parentModel.getEmbeddedComponentModel(componentId);
+ if (result == null && parentModel != null)
+ result = parentModel.getEmbeddedComponentModel(componentId);
return result;
}
@@ -203,9 +220,11 @@
{
String result = InternalUtils.get(persistentFields, fieldName);
- if (result == null && parentModel != null) result = parentModel.getFieldPersistenceStrategy(fieldName);
+ if (result == null && parentModel != null)
+ result = parentModel.getFieldPersistenceStrategy(fieldName);
- if (result == null) throw new IllegalArgumentException(ModelMessages.missingPersistentField(fieldName));
+ if (result == null)
+ throw new IllegalArgumentException(ModelMessages.missingPersistentField(fieldName));
return result;
}
@@ -219,7 +238,8 @@
{
String logicalFieldName = persistentFieldNameAllocator.allocateId(fieldName);
- if (persistentFields == null) persistentFields = CollectionFactory.newMap();
+ if (persistentFields == null)
+ persistentFields = CollectionFactory.newMap();
persistentFields.put(logicalFieldName, strategy);
@@ -233,13 +253,15 @@
public void addMixinClassName(String mixinClassName, String... order)
{
- if (mixinClassNames == null) mixinClassNames = CollectionFactory.newList();
+ if (mixinClassNames == null)
+ mixinClassNames = CollectionFactory.newList();
mixinClassNames.add(mixinClassName);
if (order != null && order.length > 0)
{
- if (mixinOrders == null) mixinOrders = CollectionFactory.newCaseInsensitiveMap();
- mixinOrders.put(mixinClassName,order);
+ if (mixinOrders == null)
+ mixinOrders = CollectionFactory.newCaseInsensitiveMap();
+ mixinOrders.put(mixinClassName, order);
}
}
@@ -247,9 +269,11 @@
{
List<String> result = CollectionFactory.newList();
- if (mixinClassNames != null) result.addAll(mixinClassNames);
+ if (mixinClassNames != null)
+ result.addAll(mixinClassNames);
- if (parentModel != null) result.addAll(parentModel.getMixinClassNames());
+ if (parentModel != null)
+ result.addAll(parentModel.getMixinClassNames());
Collections.sort(result);
@@ -286,7 +310,8 @@
Defense.notBlank(key, "key");
Defense.notBlank(value, "value");
- if (metaData == null) metaData = CollectionFactory.newCaseInsensitiveMap();
+ if (metaData == null)
+ metaData = CollectionFactory.newCaseInsensitiveMap();
// TODO: Error if duplicate?
@@ -297,7 +322,8 @@
{
Defense.notNull(renderPhase, "renderPhase");
- if (handledRenderPhases == null) handledRenderPhases = CollectionFactory.newSet();
+ if (handledRenderPhases == null)
+ handledRenderPhases = CollectionFactory.newSet();
handledRenderPhases.add(renderPhase);
}
@@ -314,7 +340,8 @@
{
String result = InternalUtils.get(metaData, key);
- if (result == null && parentModel != null) result = parentModel.getMeta(key);
+ if (result == null && parentModel != null)
+ result = parentModel.getMeta(key);
return result;
}
@@ -334,15 +361,14 @@
public boolean handlesEvent(String eventType)
{
- if (InternalUtils.get(handledEvents, eventType) != null) return true;
+ if (InternalUtils.get(handledEvents, eventType) != null)
+ return true;
- return parentModel == null
- ? false
- : parentModel.handlesEvent(eventType);
+ return parentModel == null ? false : parentModel.handlesEvent(eventType);
}
public String[] getOrderForMixin(String mixinClassName)
{
- return InternalUtils.get(mixinOrders,mixinClassName);
+ return InternalUtils.get(mixinOrders, mixinClassName);
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java Fri Jan 22 16:31:47 2010
@@ -16,6 +16,7 @@
import javassist.CtClass;
import org.apache.tapestry5.internal.structure.ComponentPageElement;
+import org.apache.tapestry5.internal.structure.InternalComponentResourcesImpl;
import org.apache.tapestry5.internal.structure.Page;
import org.apache.tapestry5.ioc.Location;
import org.apache.tapestry5.ioc.Messages;
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java Fri Jan 22 16:31:47 2010
@@ -68,13 +68,12 @@
/**
* Implements {@link RenderCommand} and represents a component within an overall page. Much of a
* component page
- * element's behavior is delegated to user code, via a
- * {@link org.apache.tapestry5.runtime.Component} instance.
+ * element's behavior is delegated to user code, via a {@link org.apache.tapestry5.runtime.Component} instance.
* <p/>
* Once instantiated, a ComponentPageElement should be registered as a
* {@linkplain org.apache.tapestry5.internal.structure.Page#addLifecycleListener(org.apache.tapestry5.runtime.PageLifecycleListener)
- * lifecycle listener}. This could be done inside the constructors, but that tends to complicate
- * unit tests, so its done by {@link org.apache.tapestry5.internal.services.PageElementFactoryImpl}.
+ * lifecycle listener}. This could be done inside the constructors, but that tends to complicate unit tests, so its done
+ * by {@link org.apache.tapestry5.internal.services.PageElementFactoryImpl}.
*/
public class ComponentPageElementImpl extends BaseLocatable implements ComponentPageElement,
PageLifecycleListener
@@ -249,9 +248,7 @@
}
/**
- * Replaces
- * {@link org.apache.tapestry5.internal.structure.ComponentPageElementImpl.BeginRenderPhase}
- * when there is
+ * Replaces {@link org.apache.tapestry5.internal.structure.ComponentPageElementImpl.BeginRenderPhase} when there is
* a handler for AfterRender but not BeginRender.
*/
private class OptimizedBeginRenderPhase implements RenderCommand
@@ -515,8 +512,6 @@
private Orderer<Component> mixinAfterOrderer;
- private List<Runnable> deferredLoadActions;
-
private boolean loaded;
/**
@@ -588,7 +583,7 @@
.getComponentResources();
coreResources = new InternalComponentResourcesImpl(this.page, this, containerResources,
- this.elementResources, completeId, nestedId, instantiator);
+ this.elementResources, completeId, nestedId, instantiator, false);
coreComponent = coreResources.getComponent();
@@ -713,7 +708,7 @@
InternalComponentResourcesImpl resources = new InternalComponentResourcesImpl(page, this,
coreResources, elementResources, completeId + mixinExtension, nestedId
- + mixinExtension, instantiator);
+ + mixinExtension, instantiator, true);
mixinIdToComponentResources.put(mixinId, resources);
// note that since we're using explicit ordering now,
@@ -833,21 +828,9 @@
// that is invoked first, before we check for unbound parameters.
invoke(false, CONTAINING_PAGE_DID_LOAD);
- executeDeferredLoadActions();
verifyRequiredParametersAreBound();
}
- private void executeDeferredLoadActions()
- {
- if (deferredLoadActions == null)
- return;
- for (Runnable action : deferredLoadActions)
- {
- action.run();
- }
- deferredLoadActions = null;// having executed them, we have no need now to store them.
- }
-
public void enqueueBeforeRenderBody(RenderQueue queue)
{
// If no body, then no beforeRenderBody or afterRenderBody
@@ -930,18 +913,6 @@
return null;
}
- public boolean isMixingIn(String mixinClassName)
- {
- return mixinForClassName(mixinClassName) != null;
- }
-
- public void deferLoadAction(Runnable action)
- {
- if (deferredLoadActions == null)
- deferredLoadActions = CollectionFactory.newList();
- deferredLoadActions.add(action);
- }
-
public ComponentResources getMixinResources(String mixinId)
{
ComponentResources result = null;
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java Fri Jan 22 16:31:47 2010
@@ -18,7 +18,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Set;
import org.apache.tapestry5.Binding;
import org.apache.tapestry5.Block;
@@ -29,8 +28,6 @@
import org.apache.tapestry5.MarkupWriter;
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.services.Instantiator;
import org.apache.tapestry5.internal.transform.ParameterConduit;
import org.apache.tapestry5.ioc.AnnotationProvider;
@@ -46,7 +43,6 @@
import org.apache.tapestry5.runtime.Component;
import org.apache.tapestry5.runtime.PageLifecycleListener;
import org.apache.tapestry5.runtime.RenderQueue;
-import org.apache.tapestry5.services.FieldValueConduit;
import org.slf4j.Logger;
/**
@@ -81,7 +77,7 @@
// Case-insensitive map from container-parameter name to ParameterAccess, for BindParameter.
// Should only ever be used for mixins.
private Map<String, ParameterAccess> containerParameterAccess;
-
+
// Case insentive map from parameter name to ParameterConduit, used to support mixins
// which need access to the containing component's PC's
private Map<String, ParameterConduit> conduits;
@@ -95,6 +91,8 @@
private boolean informalsComputed;
+ private final boolean mixin;
+
/**
* We keep a linked list of informal parameters, which saves us the expense of determining which
* bindings are formal
@@ -139,7 +137,7 @@
public InternalComponentResourcesImpl(Page page, ComponentPageElement element,
ComponentResources containerResources, ComponentPageElementResources elementResources,
- String completeId, String nestedId, Instantiator componentInstantiator)
+ String completeId, String nestedId, Instantiator componentInstantiator, boolean mixin)
{
this.page = page;
this.element = element;
@@ -147,11 +145,18 @@
this.elementResources = elementResources;
this.completeId = completeId;
this.nestedId = nestedId;
+ this.mixin = mixin;
componentModel = componentInstantiator.getModel();
component = componentInstantiator.newInstance(this);
}
+ @Override
+ public boolean isMixin()
+ {
+ return mixin;
+ }
+
public Location getLocation()
{
return element.getLocation();
@@ -346,16 +351,6 @@
return element.getMixinByClassName(mixinClassName);
}
- public boolean isMixingIn(String mixinClassName)
- {
- return element.isMixingIn(mixinClassName);
- }
-
- public void deferLoadAction(Runnable action)
- {
- element.deferLoadAction(action);
- }
-
public void renderInformalParameters(MarkupWriter writer)
{
if (bindings == null)
@@ -514,54 +509,6 @@
return result;
}
- public ParameterAccess getContainerBoundParameterAccess(final String boundParameterName,
- String... parentParameterNames)
- {
- if (containerParameterAccess == null)
- containerParameterAccess = CollectionFactory.newCaseInsensitiveMap();
-
- ParameterAccess result = containerParameterAccess.get(boundParameterName);
- if (result == null)
- {
- final InternalComponentResources res = (InternalComponentResources) getContainerResources();
- // Ideally, this check would occur at class fabrication time. But there's not currently
- // a way
- // to tell if a component class is a mixin class, short of checking for "mixins" in the
- // FQCN.
- // So we check to make sure that this component class name is in the set of mixins
- // defined for the container
- // resources.
- if (!res.isMixingIn(this.getComponentModel().getComponentClassName()))
- {
- // then we're not a mixin, we're a component in the tree.
- throw new TapestryException(StructureMessages.bindParameterOnlyOnMixin(
- boundParameterName, this), this, null);
- }
- // Have to be careful here. Problem is that if the mixin is not @MixinAfter, its
- // PAGE_DID_LOAD will be called
- // before the core component's. That can potentially result in missing default bindings
- // if we
- // call getParameterAcces at the wrong time (the unbound parameter access will be
- // cached...).
- String parentParameterName = findParentParameterName(parentParameterNames);
- if (parentParameterName == null) { throw new TapestryException(StructureMessages
- .noSuchCoreComponentParameter(this, boundParameterName, parentParameterNames),
- this, null); }
- result = createContainerParameterAccess(parentParameterName);
- containerParameterAccess.put(boundParameterName, result);
- }
- return result;
- }
-
- private String findParentParameterName(String... queries)
- {
- for (String query : queries)
- {
- if (getContainerResources().getComponentModel().getParameterModel(query) != null) { return query; }
- }
- return null;
- }
-
private ParameterAccess createParameterAccess(final String parameterName)
{
final Binding binding = getBinding(parameterName);
@@ -580,13 +527,6 @@
return binding != null;
}
- public Object read(String desiredTypeName)
- {
- Class desiredType = elementResources.toClass(desiredTypeName);
-
- return read(desiredType);
- }
-
public <T> T read(Class<T> desiredType)
{
if (binding == null)
@@ -621,26 +561,16 @@
public <T> void write(T parameterValue)
{
-
if (binding == null)
- {
- // have to fire in case there's a mixin watching value;
- // even if it's not bound to any other value,
- // the mixin needs to know that the value internal to the component
- // was changed.
- fireParameterChanged(parameterName, parameterValue);
return;
- }
Class bindingType = binding.getBindingType();
-
+
try
{
Object coerced = elementResources.coerce(parameterValue, bindingType);
binding.set(coerced);
-
- fireParameterChanged(parameterName, coerced);
}
catch (Exception ex)
{
@@ -663,122 +593,7 @@
{
return binding == null ? null : binding.getAnnotation(annotationClass);
}
-
- private Set<ParameterChangeListener> listeners;
-
- public void registerParameterChangeListener(ParameterChangeListener listener)
- {
- Defense.notNull(listener, "listener");
- if (listeners == null)
- listeners = CollectionFactory.newSet();
- listeners.add(listener);
- }
-
- public void unregisterParameterChangeListener(ParameterChangeListener listener)
- {
- if (listeners == null)
- return;
- listeners.remove(listener);
- }
-
- public boolean shouldCache()
- {
- return cache;
- }
-
- protected void fireParameterChanged(String parameterName, Object newValue)
- {
- ParameterChangedEvent event = new ParameterChangedEvent(parameterName, newValue);
-
- if (listeners != null)
- {
- for (ParameterChangeListener l : listeners)
- {
- l.parameterChanged(event);
- }
- }
- }
-
- };
- }
-
- private ParameterAccess createContainerParameterAccess(final String parentParameterName)
- {
-
- return new ParameterAccess()
- {
-
- private ParameterAccess access()
- {
- return element.getComponentResources().getParameterAccess(parentParameterName);
- }
-
- public boolean isBound()
- {
- return element.getBinding(parentParameterName) != null;
- }
-
- public Object read(String desiredTypeName)
- {
- return access().read(desiredTypeName);
- }
-
- public <T> T read(Class<T> expectedType)
- {
- return access().read(expectedType);
- }
-
- public <T> void write(T parameterValue)
- {
- access().write(parameterValue);
- }
-
- public boolean isInvariant()
- {
- return access().isInvariant();
- }
-
- public Class getBoundType()
- {
- return access().getBoundType();
- }
-
- public void registerParameterChangeListener(final ParameterChangeListener listener)
- {
- // if it's not bound, try defering.
- if (isBound())
- {
- access().registerParameterChangeListener(listener);
- }
- else
- {
- // try waiting for it. If it's not bound after load, then it's not bound at all.
- element.deferLoadAction(new Runnable()
- {
- public void run()
- {
- access().registerParameterChangeListener(listener);
- }
- });
- }
- }
-
- public void unregisterParameterChangeListener(ParameterChangeListener listener)
- {
- access().unregisterParameterChangeListener(listener);
- }
-
- public boolean shouldCache()
- {
- return access().shouldCache();
- }
-
- public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
- {
- return access().getAnnotation(annotationClass);
- }
};
-
}
public void addPageResetListener(PageResetListener listener)
@@ -787,7 +602,7 @@
}
@Override
- public FieldValueConduit getParameterConduit(String parameterName)
+ public ParameterConduit getParameterConduit(String parameterName)
{
return InternalUtils.get(conduits, parameterName);
}
@@ -795,10 +610,10 @@
@Override
public void setParameterConduit(String parameterName, ParameterConduit conduit)
{
- if (conduits == null) conduits = CollectionFactory.newCaseInsensitiveMap();
+ if (conduits == null)
+ conduits = CollectionFactory.newCaseInsensitiveMap();
conduits.put(parameterName, conduit);
}
-
-
+
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java Fri Jan 22 16:31:47 2010
@@ -122,20 +122,4 @@
return MESSAGES.get("persist-change-before-load-complete");
}
- static String noSuchCoreComponentParameter(ComponentResources mixinResources,
- String boundId, String...parameters)
- {
- return MESSAGES.format("no-such-core-component-parameter",
- boundId,
- mixinResources.getComponentModel().getComponentClassName(),
- mixinResources.getContainerResources().getComponentModel().getComponentClassName(),
- InternalUtils.joinSorted(Arrays.asList(parameters)),
- InternalUtils.joinSorted(Arrays.asList(mixinResources.getContainerResources().getComponentModel().getDeclaredParameterNames())));
- }
-
- public static String bindParameterOnlyOnMixin(String boundParameterName, InternalComponentResourcesImpl internalComponentResources)
- {
- return MESSAGES.format("bind-parameter-only-on-mixin",boundParameterName,
- internalComponentResources.getComponentModel().getComponentClassName());
- }
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BindParameterWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BindParameterWorker.java?rev=902157&r1=902156&r2=902157&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BindParameterWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BindParameterWorker.java Fri Jan 22 16:31:47 2010
@@ -1,10 +1,10 @@
-// Copyright 2009 The Apache Software Foundation
+// Copyright 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,335 +14,168 @@
package org.apache.tapestry5.internal.transform;
-import org.apache.tapestry5.services.*;
-import org.apache.tapestry5.model.MutableComponentModel;
+import java.util.List;
+
+import javax.xml.ws.ServiceMode;
+
+import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.annotations.BindParameter;
-import org.apache.tapestry5.internal.*;
-import org.apache.tapestry5.internal.bindings.LiteralBinding;
-import org.apache.tapestry5.ioc.util.BodyBuilder;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.corelib.pages.ServiceStatus;
+import org.apache.tapestry5.internal.InternalComponentResources;
+import org.apache.tapestry5.internal.services.ComponentClassCache;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry5.Binding;
-
-import java.util.List;
-import java.util.Iterator;
-import java.util.Arrays;
-import java.lang.reflect.Modifier;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.internal.util.TapestryException;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.model.ComponentModel;
+import org.apache.tapestry5.model.MutableComponentModel;
+import org.apache.tapestry5.services.ClassTransformation;
+import org.apache.tapestry5.services.ComponentClassTransformWorker;
+import org.apache.tapestry5.services.ComponentValueProvider;
+import org.apache.tapestry5.services.FieldValueConduit;
/**
- * Responsible for identifying, via the {@link org.apache.tapestry5.annotations.BindParameter} annotation,
- * mixin fields that should be bound to a core-component parameter value.
- *
- * @since 5.2.0.0
+ * Responsible for identifying, via the {@link org.apache.tapestry5.annotations.BindParameter} annotation, mixin fields
+ * that should be bound to a core-component parameter value.
+ *
+ * @since 5.2.0
*/
public class BindParameterWorker implements ComponentClassTransformWorker
{
+ private final TypeCoercer typeCoercer;
- private static final String EQUAL_METHOD_NAME = BindParameterWorker.class.getName() + ".equal";
+ private final ComponentClassCache componentClassCache;
- public void transform(final ClassTransformation transformation, MutableComponentModel model)
+ public BindParameterWorker(TypeCoercer typeCoercer, ComponentClassCache componentClassCache)
{
- List<String> fieldNames = transformation.findFieldsWithAnnotation(BindParameter.class);
-
- for(String fieldName : fieldNames)
- {
- BindParameter annotation = transformation.getFieldAnnotation(fieldName, BindParameter.class);
- convertFieldIntoContainerBoundParameter(fieldName, annotation, transformation, model);
- }
-
+ this.typeCoercer = typeCoercer;
+ this.componentClassCache = componentClassCache;
}
- private void convertFieldIntoContainerBoundParameter(String name, BindParameter annotation, ClassTransformation transformation,
- MutableComponentModel model)
+ public void transform(final ClassTransformation transformation, MutableComponentModel model)
{
- transformation.claimField(name, annotation);
-
- String boundParameterName = getBoundParameterName(name, annotation.name());
- String[] parentParameterNames = getParentParameterNames(name, annotation.value());
-
-
- String type = transformation.getFieldType(name);
-
- //we can't do this exactly the same as parameter. We can't know at transformation time which parameter
- //this thing will be linked to, because it could be wired to any number of different components.
- //So we have to wait until runtime to examine caching and whether we should cache, rather than
- //constructing the class differently based on caching or not.
- String cachedFieldName = transformation.addField(Modifier.PRIVATE, "boolean", name + "_cached");
-
- String resourcesFieldName = transformation.getResourcesFieldName();
+ List<String> fieldNames = transformation.findFieldsWithAnnotation(BindParameter.class);
- String accessFieldName = addBoundParameterSetup(name,
- boundParameterName, parentParameterNames,
- cachedFieldName, type, resourcesFieldName,
- transformation);
+ for (String fieldName : fieldNames)
+ {
+ BindParameter annotation = transformation.getFieldAnnotation(fieldName,
+ BindParameter.class);
- addReaderMethod(name, cachedFieldName, accessFieldName, boundParameterName, type, resourcesFieldName,
- transformation);
+ convertFieldIntoContainerBoundParameter(fieldName, annotation, transformation);
+ }
- addWriterMethod(name, cachedFieldName, accessFieldName, boundParameterName, type, resourcesFieldName,
- transformation);
}
-
- /**
- * Returns the name of a field that stores whether the parameter binding is invariant.
- */
- private String addBoundParameterSetup(String fieldName, String boundParameterName, String[] parentParameterNames,
- String cachedFieldName, String fieldType,
- String resourcesFieldName, ClassTransformation transformation)
+ private void convertFieldIntoContainerBoundParameter(final String fieldName,
+ final BindParameter annotation, ClassTransformation transformation)
{
- String accessFieldName = transformation.addField(Modifier.PRIVATE, ParameterAccess.class.getName(),
- fieldName + "_access");
-
- String parentNamesField = transformation.addField(Modifier.PRIVATE, String[].class.getName(),
- fieldName + "_parentparameternames");
-
- String defaultFieldName = transformation.addField(Modifier.PRIVATE, fieldType, fieldName + "_default");
-
- BodyBuilder builder = new BodyBuilder().begin();
+ final String fieldTypeName = transformation.getFieldType(fieldName);
- builder.addln("%s = new String[%d];",parentNamesField,parentParameterNames.length);
+ transformation.claimField(fieldName, annotation);
- for(int i=0;i<parentParameterNames.length;i++)
+ ComponentValueProvider<FieldValueConduit> provider = new ComponentValueProvider<FieldValueConduit>()
{
- builder.addln("%s[%d]=\"%s\";",parentNamesField,i,parentParameterNames[i]);
- }
-
- builder.addln("%s = %s.getContainerBoundParameterAccess(\"%s\",%s);",
- accessFieldName,
- resourcesFieldName,
- boundParameterName,
- parentNamesField);
-
- // Store the current value of the field into the default field. This value will
- // be used to reset the field after rendering.
-
- builder.addln("%s = %s;", defaultFieldName, fieldName);
-
- addListenerSetup(fieldName, fieldType, boundParameterName, parentParameterNames, accessFieldName, builder,
- transformation);
-
- builder.end();
-
- transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, builder
- .toString());
-
- // Now, when the component completes rendering, ensure that any variant parameters are
- // are returned to default value. This isn't necessary when the parameter is not cached,
- // because (unless the binding is invariant), there's no value to get rid of (and if it is
- // invariant, there's no need to get rid of it).
- // as with reader/writer methods, we have to do the caching check at runtime.
- builder.clear();
-
- builder.addln("if (%s.shouldCache() && ! %1$s.isInvariant())", accessFieldName);
- builder.begin();
- builder.addln("%s = %s;", fieldName, defaultFieldName);
- builder.addln("%s = false;", cachedFieldName);
- builder.end();
-
- // Clean up after the component renders.
-
- String body = builder.toString();
-
- transformation.extendMethod(TransformConstants.POST_RENDER_CLEANUP_SIGNATURE, body);
-
- // And again, when the page is detached (TAPESTRY-2460)
-
- transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE, body);
-
- return accessFieldName;
- }
-
- private void addListenerSetup(
- String fieldName,
- String fieldType,
- String boundParameterName,
- String[] parentParameterNames,
- String accessFieldName,
- BodyBuilder builder,
- ClassTransformation transformation)
+ @Override
+ public FieldValueConduit get(final ComponentResources resources)
+ {
+ if (!resources.isMixin())
+ throw new TapestryException(TransformMessages.bindParameterOnlyOnMixin(
+ fieldName, resources), null);
+
+ final InternalComponentResources containerResources = (InternalComponentResources) resources
+ .getContainerResources();
+
+ // Evaluate this early so that we get a fast fail.
+
+ final String containerParameterName = identifyParameterName(resources,
+ InternalUtils.stripMemberName(fieldName), annotation.value());
+
+ final Class fieldType = componentClassCache.forName(fieldTypeName);
+
+ return new FieldValueConduit()
+ {
+ private ParameterConduit conduit;
+
+ /**
+ * Defer obtaining the conduit object until needed, to deal with the complex
+ * lifecycle of
+ * parameters. Perhaps this can be addressed by converting constructors into
+ * methods invoked
+ * from the page loaded lifecycle method?
+ */
+ private ParameterConduit getParameterConduit()
+ {
+ if (conduit == null)
+ {
+ conduit = containerResources
+ .getParameterConduit(containerParameterName);
+
+ }
+
+ return conduit;
+ }
+
+ @Override
+ public void set(Object newValue)
+ {
+ getParameterConduit().set(newValue);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object get()
+ {
+ // For the moment, this results in two passes through the TypeCoercer; we'll look
+ // to optimize that in the future. The first pass is deep inside ParameterConduit (coercing
+ // to the component parameter field type), the second is here (usually the same type so no
+ // real coercion necessary).
+
+ Object result = getParameterConduit().get();
+
+ return typeCoercer.coerce(result, fieldType);
+ }
+ };
+ }
+ };
+
+ transformation.replaceFieldAccess(fieldName, provider);
+ }
+
+ private String identifyParameterName(ComponentResources resources, String firstGuess,
+ String... otherGuesses)
{
- transformation.addImplementedInterface(ParameterChangeListener.class);
- builder.addln("%s.registerParameterChangeListener($0);",accessFieldName);
+ ComponentModel model = resources.getContainerResources().getComponentModel();
- TransformMethodSignature signature = new TransformMethodSignature(Modifier.PUBLIC, "void", "parameterChanged",
- new String[] {ParameterChangedEvent.class.getName()}, null);
+ List<String> guesses = CollectionFactory.newList();
+ guesses.add(firstGuess);
- BodyBuilder changedBody = new BodyBuilder().begin();
- //by this point, we know that there is at least one entry in parent Parameter Names.
- changedBody.add("if (%s($1, \"%s\")", EQUAL_METHOD_NAME, parentParameterNames[0]);
- for(int i=1; i<parentParameterNames.length; i++)
+ for (String name : otherGuesses)
{
- changedBody.add(" || %s($1, \"%s\")", EQUAL_METHOD_NAME, parentParameterNames[i]);
+ guesses.add(name);
}
- changedBody.add(")").begin();
-
- String cast = TransformUtils.getWrapperTypeName(fieldType);
-
- if (TransformUtils.isPrimitive(fieldType))
- changedBody.addln("%s = ((%s) $1.getNewValue()).%s();",
- fieldName, cast, TransformUtils.getUnwrapperMethodName(fieldType));
- else
- changedBody.addln("%s = (%s) $1.getNewValue();",fieldName, cast);
-
- changedBody.addln("return;").end();
-
- changedBody.end();
-
- transformation.extendMethod(signature,changedBody.toString());
-
- }
-
- private void addWriterMethod(String fieldName, String cachedFieldName, String accessFieldName,
- String boundParameterName, String fieldType,
- String resourcesFieldName, ClassTransformation transformation)
- {
- BodyBuilder builder = new BodyBuilder();
- builder.begin();
-
- // Before the component is loaded, updating the property sets the default value
- // for the parameter. The value is stored in the field, but will be
- // rolled into default field inside containingPageDidLoad().
-
- builder.addln("if (! %s.isLoaded())", resourcesFieldName);
- builder.begin();
- builder.addln("%s = $1;", fieldName);
- builder.addln("return;");
- builder.end();
-
- //unregistering the listener from the parameter change listener list avoids double-setting the field,
- builder.addln("%s.unregisterParameterChangeListener($0);",accessFieldName);
-
- // Always start by updating the parameter; this will implicitly check for
- // read-only or unbound parameters. $1 is the single parameter
- // to the method.
- builder.addln("%s.write(($w)$1);", accessFieldName);
- builder.addln("%s = $1;",fieldName);
- builder.addln("%s.registerParameterChangeListener($0);",accessFieldName);
-
- //note that there's no way of knowing at class transformation time which component a mixin will
- //be associated with and, further more, no way of knowing which @Parameter a mixin field will be
- //@BindParameter'ed to. So we have to generate caching code that works at runtime, rather than
- //including or not including caching logic at transformation time.
- builder.addln("if (%s.shouldCache())",accessFieldName).begin();
- builder.addln("%s = %s.isRendering();",cachedFieldName, resourcesFieldName).end();
- builder.end();
-
- String methodName = transformation.newMemberName("update_boundparameter", boundParameterName);
-
- TransformMethodSignature signature = new TransformMethodSignature(Modifier.PRIVATE, "void", methodName,
- new String[] {fieldType}, null);
-
- transformation.addMethod(signature, builder.toString());
-
- builder.clear();
-
- //add the catch because if we don't re-register the class as a parameter change listener, it's value
- //could wind up stale, and write can throw an exception.
- builder.begin();
- builder.addln("%s.registerParameterChangeListener($0);", accessFieldName);
- builder.addln("throw $e;");
- builder.end();
-
- transformation.addCatch(signature,Exception.class.getName(),builder.toString());
-
- transformation.replaceWriteAccess(fieldName, methodName);
- }
-
- /**
- * Adds a private method that will be the replacement for read-access to the field.
- */
- private void addReaderMethod(String fieldName, String cachedFieldName, String accessFieldName,
- String boundParameterName, String fieldType, String resourcesFieldName,
- ClassTransformation transformation)
- {
- BodyBuilder builder = new BodyBuilder();
- builder.begin();
-
- // While the component is still loading, or when the value for the component is cached,
- // or if the value is not bound, then return the current value of the field.
-
- builder.addln("if ((%s.shouldCache() && %s) || ! %s.isLoaded() || ! %s.isBound()) return %s;",
- accessFieldName, cachedFieldName, resourcesFieldName, accessFieldName, fieldName);
-
- String cast = TransformUtils.getWrapperTypeName(fieldType);
-
- // 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.
-
- builder.addln("%s result = ($r) ((%s) %s.read(\"%2$s\"));", fieldType, cast, accessFieldName);
-
- // If the binding is invariant, then it's ok to cache. Othewise, its only
- // ok to cache if a) the @Parameter says to cache and b) the component
- // is rendering at the point when field is accessed.
- builder.add("if (%s.isInvariant() || (%1$s.shouldCache() && %s.isRendering()))",
- accessFieldName, resourcesFieldName);
-
- builder.begin();
- builder.addln("%s = result;", fieldName);
- builder.addln("%s = true;", cachedFieldName);
- builder.end();
-
- builder.addln("return result;");
- builder.end();
-
- String methodName = transformation.newMemberName("read_boundparameter", boundParameterName);
-
- TransformMethodSignature signature = new TransformMethodSignature(Modifier.PRIVATE, fieldType, methodName, null,
- null);
-
- transformation.addMethod(signature, builder.toString());
-
- transformation.replaceReadAccess(fieldName, methodName);
- }
-
- private String getBoundParameterName(String fieldName, String annotatedName)
- {
- if (InternalUtils.isNonBlank(annotatedName)) return annotatedName;
-
- return InternalUtils.stripMemberName(fieldName);
- }
-
- private String[] getParentParameterNames(String fieldName, String... names)
- {
- List<String> temp = CollectionFactory.newList(names);
- for(Iterator<String> it = temp.iterator();it.hasNext(); )
+ for (String name : guesses)
{
- String name =it.next();
- if (InternalUtils.isBlank(name)) it.remove();
+ if (model.isFormalParameter(name))
+ return name;
}
- if (temp.isEmpty())
- return new String[] {InternalUtils.stripMemberName(fieldName)};
- return temp.toArray(new String[temp.size()]);
- }
+ String message = String
+ .format(
+ "Failed to bind parameter of mixin %s (type %s). Containing component %s does not contain a formal parameter %s %s. Formal parameters: %s.",
+ resources.getCompleteId(),
- /**
- * Invoked from generated code as part of the handling of parameter default methods.
- */
- public static void bind(String parameterName, InternalComponentResources resources, Object value)
- {
- if (value == null) return;
+ resources.getComponentModel().getComponentClassName(),
- if (value instanceof Binding)
- {
- Binding binding = (Binding) value;
+ model.getComponentClassName(),
- resources.bindParameter(parameterName, binding);
- return;
- }
+ guesses.size() == 1 ? "matching" : "matching any of",
- resources.bindParameter(parameterName, new LiteralBinding(null, "default " + parameterName, value));
- }
+ InternalUtils.joinSorted(guesses),
- public static <T> boolean equal(T left, T right)
- {
- return TapestryInternalUtils.isEqual(left,right);
- }
+ InternalUtils.joinSorted(model.getDeclaredParameterNames()));
+ throw new TapestryException(message, resources.getLocation(), null);
+ }
}