Author: hlship
Date: Thu Mar 4 19:14:27 2010
New Revision: 919137
URL: http://svn.apache.org/viewvc?rev=919137&view=rev
Log:
TAP5-1035: Use UnknownValueException when unable to bind a mixin field to a
containing component parameter
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BindParameterWorker.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
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=919137&r1=919136&r2=919137&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
Thu Mar 4 19:14:27 2010
@@ -20,9 +20,11 @@
import org.apache.tapestry5.annotations.BindParameter;
import org.apache.tapestry5.internal.InternalComponentResources;
import org.apache.tapestry5.internal.services.ComponentClassCache;
+import org.apache.tapestry5.ioc.internal.util.AvailableValues;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.internal.util.TapestryException;
+import org.apache.tapestry5.ioc.internal.util.UnknownValueException;
import org.apache.tapestry5.ioc.services.FieldValueConduit;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.apache.tapestry5.model.ComponentModel;
@@ -30,6 +32,7 @@
import org.apache.tapestry5.services.ClassTransformation;
import org.apache.tapestry5.services.ComponentClassTransformWorker;
import org.apache.tapestry5.services.ComponentValueProvider;
+import org.apache.tapestry5.services.TransformField;
/**
* Responsible for identifying, via the {...@link
org.apache.tapestry5.annotations.BindParameter} annotation, mixin fields
@@ -39,6 +42,61 @@
*/
public class BindParameterWorker implements ComponentClassTransformWorker
{
+ private final class BoundParameterFieldValueConduit implements
FieldValueConduit
+ {
+ private final String containerParameterName;
+
+ private final InternalComponentResources containerResources;
+
+ private final Class fieldType;
+
+ private ParameterConduit conduit;
+
+ private BoundParameterFieldValueConduit(String containerParameterName,
+ InternalComponentResources containerResources, Class fieldType)
+ {
+ this.containerParameterName = containerParameterName;
+ this.containerResources = containerResources;
+ this.fieldType = fieldType;
+ }
+
+ /**
+ * 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;
+ }
+
+ public void set(Object newValue)
+ {
+ getParameterConduit().set(newValue);
+ }
+
+ @SuppressWarnings("unchecked")
+ 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);
+ }
+ }
+
private final TypeCoercer typeCoercer;
private final ComponentClassCache componentClassCache;
@@ -51,93 +109,62 @@
public void transform(final ClassTransformation transformation,
MutableComponentModel model)
{
- List<String> fieldNames =
transformation.findFieldsWithAnnotation(BindParameter.class);
+ for (TransformField field :
transformation.matchFieldsWithAnnotation(BindParameter.class))
+ convertFieldIntoContainerBoundParameter(field);
+ }
- for (String fieldName : fieldNames)
- {
- BindParameter annotation =
transformation.getFieldAnnotation(fieldName,
- BindParameter.class);
+ private void convertFieldIntoContainerBoundParameter(TransformField field)
+ {
+ BindParameter annotation = field.getAnnotation(BindParameter.class);
- convertFieldIntoContainerBoundParameter(fieldName, annotation,
transformation);
- }
+ field.claim(annotation);
- }
+ final String[] possibleNames = annotation.value();
- private void convertFieldIntoContainerBoundParameter(final String
fieldName,
- final BindParameter annotation, ClassTransformation transformation)
- {
- final String fieldTypeName = transformation.getFieldType(fieldName);
+ final String fieldTypeName = field.getType();
- transformation.claimField(fieldName, annotation);
+ final String fieldName = field.getName();
ComponentValueProvider<FieldValueConduit> provider = new
ComponentValueProvider<FieldValueConduit>()
{
-
public FieldValueConduit get(final ComponentResources resources)
{
- if (!resources.isMixin())
- throw new
TapestryException(TransformMessages.bindParameterOnlyOnMixin(
- fieldName, resources), null);
+ try
+ {
+ return createFieldValueConduit(resources, fieldTypeName,
fieldName, possibleNames);
+ }
+ catch (Exception ex)
+ {
+ throw new TapestryException(String.format("Failure binding
parameter field '%s' of mixin %s (type %s): %s",
+ fieldName, resources.getCompleteId(),
+
resources.getComponentModel().getComponentClassName(),
InternalUtils.toMessage(ex)), ex);
+ }
+ }
- final InternalComponentResources containerResources =
(InternalComponentResources) resources
- .getContainerResources();
+ };
- // Evaluate this early so that we get a fast fail.
+ field.replaceAccess(provider);
+ }
- final String containerParameterName =
identifyParameterName(resources,
- InternalUtils.stripMemberName(fieldName),
annotation.value());
+ private FieldValueConduit createFieldValueConduit(final ComponentResources
resources, final String fieldTypeName,
+ final String fieldName, final String[] possibleNames)
+ {
+ if (!resources.isMixin())
+ throw new
TapestryException(TransformMessages.bindParameterOnlyOnMixin(fieldName,
resources), null);
- final Class fieldType =
componentClassCache.forName(fieldTypeName);
+ InternalComponentResources containerResources =
(InternalComponentResources) resources.getContainerResources();
- return new FieldValueConduit()
- {
- private ParameterConduit conduit;
+ // Evaluate this early so that we get a fast fail.
- /**
- * 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;
- }
-
- public void set(Object newValue)
- {
- getParameterConduit().set(newValue);
- }
-
- @SuppressWarnings("unchecked")
- 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);
- }
- };
- }
- };
+ String containerParameterName = identifyParameterName(resources,
InternalUtils.stripMemberName(fieldName),
+ possibleNames);
+
+ Class fieldType = componentClassCache.forName(fieldTypeName);
- transformation.getField(fieldName).replaceAccess(provider);
+ return new BoundParameterFieldValueConduit(containerParameterName,
containerResources, fieldType);
}
- private String identifyParameterName(ComponentResources resources, String
firstGuess,
- String... otherGuesses)
+ private String identifyParameterName(ComponentResources resources, String
firstGuess, String... otherGuesses)
{
ComponentModel model =
resources.getContainerResources().getComponentModel();
@@ -155,21 +182,15 @@
return name;
}
- 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(),
-
- resources.getComponentModel().getComponentClassName(),
-
- model.getComponentClassName(),
+ String message = String.format("Containing component %s does not
contain a formal parameter %s %s.",
- guesses.size() == 1 ? "matching" : "matching any of",
+ model.getComponentClassName(),
- InternalUtils.joinSorted(guesses),
+ guesses.size() == 1 ? "matching" : "matching any of",
-
InternalUtils.joinSorted(model.getDeclaredParameterNames()));
+ InternalUtils.joinSorted(guesses));
- throw new TapestryException(message, resources.getLocation(), null);
+ throw new UnknownValueException(message, new AvailableValues("formal
parameters", model
+ .getDeclaredParameterNames()));
}
}
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java?rev=919137&r1=919136&r2=919137&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
Thu Mar 4 19:14:27 2010
@@ -1282,7 +1282,9 @@
assertTextPresent(
"An unexpected application exception has occurred.",
- "Failed to bind parameter of mixin
BindParameterNoSuchParameter:throwexception$echovalue2 (type
org.apache.tapestry5.integration.app1.mixins.EchoValue2). Containing component
org.apache.tapestry5.corelib.components.Any does not contain a formal parameter
matching any of boundParameter, value. Formal parameters: clientId, element.");
+ "Failure binding parameter field 'boundParameter' of mixin
BindParameterNoSuchParameter:throwexception$echovalue2 (type
org.apache.tapestry5.integration.app1.mixins.EchoValue2)",
+
+ "Containing component
org.apache.tapestry5.corelib.components.Any does not contain a formal parameter
matching any of boundParameter, value.");
}
@Test