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 2006/12/22 00:09:47 UTC
svn commit: r489493 [1/2] - in /tapestry/tapestry5/tapestry-core/trunk/src:
main/java/org/apache/tapestry/corelib/base/
main/java/org/apache/tapestry/internal/
main/java/org/apache/tapestry/internal/bindings/
main/java/org/apache/tapestry/internal/serv...
Author: hlship
Date: Thu Dec 21 15:09:45 2006
New Revision: 489493
URL: http://svn.apache.org/viewvc?view=rev&rev=489493
Log:
Add an additional parameter to BindingSource and BindingFactory to identify the component whose parameter is being bound.
Add validator: binding prefix.
Move toUserPresentable() to TapestryUtils, move InternalUtilsTest to tapestry-ioc.
Added:
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/ValidatorBindingFactory.java
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/ValidatorBindingFactoryTest.java
Removed:
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/util/InternalUtilsTest.java
Modified:
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/base/AbstractField.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BindingsMessages.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/ComponentBindingFactory.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBindingFactory.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/MessageBindingFactory.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BindingSourceImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ValidatorSpecification.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingFactory.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/bindings/BindingsStrings.properties
tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/TapestryUtilsTest.java
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/BindingFactoryTest.java
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/PropBindingFactoryTest.java
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/BindingSourceImplTest.java
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/base/AbstractField.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/base/AbstractField.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/base/AbstractField.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/base/AbstractField.java Thu Dec 21 15:09:45 2006
@@ -27,8 +27,8 @@
import org.apache.tapestry.annotations.SetupRender;
import org.apache.tapestry.corelib.mixins.RenderDisabled;
import org.apache.tapestry.corelib.mixins.RenderInformals;
+import org.apache.tapestry.internal.TapestryUtils;
import org.apache.tapestry.internal.services.FormParameterLookup;
-import org.apache.tapestry.ioc.internal.util.InternalUtils;
import org.apache.tapestry.services.FormSupport;
import org.apache.tapestry.services.PageRenderSupport;
@@ -164,7 +164,7 @@
if (_label != null)
return _label;
- return InternalUtils.toUserPresentable(_resources.getId());
+ return TapestryUtils.toUserPresentable(_resources.getId());
}
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java Thu Dec 21 15:09:45 2006
@@ -48,4 +48,38 @@
return Character.toLowerCase(first) + input.substring(1);
}
+
+ /**
+ * Capitalizes the string, and inserts a space before each upper case character (or sequence of
+ * upper case characters). Thus "userId" becomes "User Id", etc.
+ */
+ public static String toUserPresentable(String id)
+ {
+ StringBuilder builder = new StringBuilder(id.length() * 2);
+
+ char[] chars = id.toCharArray();
+ boolean postSpace = true;
+
+ for (int i = 0; i < chars.length; i++)
+ {
+ char ch = chars[i];
+
+ if (i == 0)
+ {
+ builder.append(Character.toUpperCase(ch));
+ continue;
+ }
+
+ boolean upperCase = Character.isUpperCase(ch);
+
+ if (upperCase && !postSpace)
+ builder.append(' ');
+
+ builder.append(ch);
+
+ postSpace = upperCase;
+ }
+
+ return builder.toString();
+ }
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BindingsMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BindingsMessages.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BindingsMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/BindingsMessages.java Thu Dec 21 15:09:45 2006
@@ -12,41 +12,47 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package org.apache.tapestry.internal.bindings;
-
+package org.apache.tapestry.internal.bindings;
+
+import org.apache.tapestry.ComponentResources;
import org.apache.tapestry.ioc.Messages;
import org.apache.tapestry.ioc.internal.util.MessagesImpl;
import org.apache.tapestry.services.Binding;
-
-final class BindingsMessages
-{
- private static final Messages MESSAGES = MessagesImpl.forClass(BindingsMessages.class);
-
- private BindingsMessages()
- {
- }
-
- static String bindingIsReadOnly(Binding binding)
- {
- return MESSAGES.format("binding-is-read-only", binding);
- }
-
- static String bindingIsWriteOnly(Binding binding)
- {
- return MESSAGES.format("binding-is-write-only", binding);
- }
-
- static String noSuchProperty(Class targetClass, String propertyName, String propertyPath)
- {
- return MESSAGES.format(
- "no-such-property",
- targetClass.getName(),
- propertyName,
- propertyPath);
- }
-
- static String writeOnlyProperty(String propertyName, Class clazz, String propertyPath)
- {
- return MESSAGES.format("write-only-property", propertyName, clazz.getName(), propertyPath);
- }
-}
+
+final class BindingsMessages
+{
+ private static final Messages MESSAGES = MessagesImpl.forClass(BindingsMessages.class);
+
+ private BindingsMessages()
+ {
+ }
+
+ static String bindingIsReadOnly(Binding binding)
+ {
+ return MESSAGES.format("binding-is-read-only", binding);
+ }
+
+ static String bindingIsWriteOnly(Binding binding)
+ {
+ return MESSAGES.format("binding-is-write-only", binding);
+ }
+
+ static String noSuchProperty(Class targetClass, String propertyName, String propertyPath)
+ {
+ return MESSAGES.format(
+ "no-such-property",
+ targetClass.getName(),
+ propertyName,
+ propertyPath);
+ }
+
+ static String writeOnlyProperty(String propertyName, Class clazz, String propertyPath)
+ {
+ return MESSAGES.format("write-only-property", propertyName, clazz.getName(), propertyPath);
+ }
+
+ static String validatorBindingForFieldsOnly(ComponentResources component)
+ {
+ return MESSAGES.format("validator-binding-for-fields-only", component.getCompleteId());
+ }
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/ComponentBindingFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/ComponentBindingFactory.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/ComponentBindingFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/ComponentBindingFactory.java Thu Dec 21 15:09:45 2006
@@ -22,10 +22,10 @@
/** The "component:" binding prefix, which allows access to a child component via its id. */
public class ComponentBindingFactory implements BindingFactory
{
- public Binding newBinding(String description, ComponentResources component, String expression,
- Location location)
+ public Binding newBinding(String description, ComponentResources container, ComponentResources component,
+ String expression, Location location)
{
- return new ComponentBinding(description, component, expression, location);
+ return new ComponentBinding(description, container, expression, location);
}
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBinding.java Thu Dec 21 15:09:45 2006
@@ -12,41 +12,42 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package org.apache.tapestry.internal.bindings;
-
+package org.apache.tapestry.internal.bindings;
+
import org.apache.tapestry.ioc.Location;
-
-/**
- * Binding type for literal string values, usually supplied in-line in the component template.
- */
-public class LiteralBinding extends AbstractBinding
-{
- private final String _description;
-
- private final Object _value;
-
- public LiteralBinding(String description, Object value, Location location)
- {
- super(location);
- _description = description;
- _value = value;
- }
-
- public Object get()
- {
- return _value;
- }
-
- @Override
- public String toString()
- {
- return String.format("LiteralBinding[%s: %s]", _description, _value);
- }
-
- public Class getBindingType()
- {
- // Could be a problem for a LiteralBinding of null but that will certainly be read-only.
- return _value.getClass();
- }
-
-}
+
+/**
+ * Binding type for literal, immutable values. This is often used for literal string values supplied
+ * in-line in the component template, but is used for many other things as well.
+ */
+public class LiteralBinding extends AbstractBinding
+{
+ private final String _description;
+
+ private final Object _value;
+
+ public LiteralBinding(String description, Object value, Location location)
+ {
+ super(location);
+ _description = description;
+ _value = value;
+ }
+
+ public Object get()
+ {
+ return _value;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("LiteralBinding[%s: %s]", _description, _value);
+ }
+
+ public Class getBindingType()
+ {
+ // Could be a problem for a LiteralBinding of null but that will certainly be read-only.
+ return _value.getClass();
+ }
+
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBindingFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBindingFactory.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBindingFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/LiteralBindingFactory.java Thu Dec 21 15:09:45 2006
@@ -26,8 +26,8 @@
*/
public class LiteralBindingFactory implements BindingFactory
{
- public Binding newBinding(String description, ComponentResources component, String expression,
- Location location)
+ public Binding newBinding(String description, ComponentResources container, ComponentResources component,
+ String expression, Location location)
{
return new LiteralBinding(description, expression, location);
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/MessageBindingFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/MessageBindingFactory.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/MessageBindingFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/MessageBindingFactory.java Thu Dec 21 15:09:45 2006
@@ -26,10 +26,10 @@
public class MessageBindingFactory implements BindingFactory
{
- public Binding newBinding(String description, ComponentResources component, String expression,
- Location location)
+ public Binding newBinding(String description, ComponentResources container, ComponentResources component,
+ String expression, Location location)
{
- String messageValue = component.getMessages().get(expression);
+ String messageValue = container.getMessages().get(expression);
return new LiteralBinding(description, messageValue, location);
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java Thu Dec 21 15:09:45 2006
@@ -12,265 +12,265 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package org.apache.tapestry.internal.bindings;
-
+package org.apache.tapestry.internal.bindings;
+
import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newThreadSafeMap;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Map;
-
-import org.apache.tapestry.ComponentResources;
-import org.apache.tapestry.events.InvalidationListener;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Map;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.events.InvalidationListener;
import org.apache.tapestry.ioc.Location;
import org.apache.tapestry.ioc.internal.util.TapestryException;
-import org.apache.tapestry.ioc.services.ClassFab;
-import org.apache.tapestry.ioc.services.ClassFabUtils;
-import org.apache.tapestry.ioc.services.ClassFactory;
-import org.apache.tapestry.ioc.services.MethodSignature;
-import org.apache.tapestry.ioc.services.PropertyAccess;
-import org.apache.tapestry.ioc.services.PropertyAdapter;
+import org.apache.tapestry.ioc.services.ClassFab;
+import org.apache.tapestry.ioc.services.ClassFabUtils;
+import org.apache.tapestry.ioc.services.ClassFactory;
+import org.apache.tapestry.ioc.services.MethodSignature;
+import org.apache.tapestry.ioc.services.PropertyAccess;
+import org.apache.tapestry.ioc.services.PropertyAdapter;
import org.apache.tapestry.ioc.util.BodyBuilder;
import org.apache.tapestry.services.Binding;
-import org.apache.tapestry.services.BindingFactory;
-
-/**
- * Binding factory for reading and updating JavaBean properties. Uses
- * {@link org.apache.tapestry.ioc.services.PropertyAccess} to analyze the properties, and generates
- * a binding class using the component {@link org.apache.tapestry.ioc.services.ClassFactory}.
- */
-public class PropBindingFactory implements BindingFactory, InvalidationListener
-{
- private final PropertyAccess _access;
-
- private final ClassFactory _classFactory;
-
- // The key is a combination of class name and property path.
- private final Map<String, BindingConstructor> _cache = newThreadSafeMap();
-
- private static class BindingConstructor
- {
- private final Class _propertyType;
-
- private final Constructor _constructor;
-
- BindingConstructor(Class propertyType, Constructor constructor)
- {
- _propertyType = propertyType;
- _constructor = constructor;
- }
-
- Binding newBindingInstance(Object target, String toString, Location location)
- throws Exception
- {
- return (Binding) _constructor.newInstance(target, _propertyType, toString, location);
- }
- }
-
- private static final MethodSignature GET_SIGNATURE = new MethodSignature(Object.class, "get",
- null, null);
-
- private static final MethodSignature SET_SIGNATURE = new MethodSignature(void.class, "set",
- new Class[]
- { Object.class }, null);
-
- public PropBindingFactory(PropertyAccess propertyAccess, ClassFactory classFactory)
- {
- _access = propertyAccess;
- _classFactory = classFactory;
- }
-
- public Binding newBinding(String description, ComponentResources component, String expression,
- Location location)
- {
- Object target = component.getComponent();
- Class targetClass = target.getClass();
-
- try
- {
- BindingConstructor cons = findCachedConstructor(targetClass, expression);
-
- String toString = String.format("PropBinding[%s %s(%s)]", description, component
- .getCompleteId(), expression);
-
- return cons.newBindingInstance(target, toString, location);
- }
- catch (Throwable ex)
- {
- throw new TapestryException(ex.getMessage(), location, ex);
- }
- }
-
- /**
- * Searches for a cached binding constructor for the given target class and property name. As
- * necessary, a new cached constructor is created.
- *
- * @param targetClass
- * the class of the component which contains the property
- * @param propertyName
- * the name of the property to expose as a binding
- * @return
- */
- private BindingConstructor findCachedConstructor(Class targetClass, String propertyName)
- {
- // The only problem with this key is if we can get in a situation where two different
- // versions of the class (from the default class loader, and the component class loader)
- // are accessed in this way at the same time. I'm pretty sure that can't happen.
-
- String key = targetClass.getName() + ":" + propertyName;
-
- BindingConstructor result = _cache.get(key);
-
- if (result == null)
- {
- result = createConstructor(key, targetClass, propertyName);
- _cache.put(key, result);
- }
-
- return result;
- }
-
- private BindingConstructor createConstructor(String key, Class targetClass, String propertyPath)
- {
- // Race condition: simulataneous calls to fillCache() for the same targetClass/propertyName
- // combination may result in duplicate binding classes being created, which causes no great
- // harm.
-
- StringBuilder builder = new StringBuilder("_target");
-
- Class step = targetClass;
- String[] terms = propertyPath.split("\\.");
-
- for (int i = 0; i < terms.length - 1; i++)
- {
- String name = terms[i];
-
- PropertyAdapter pa = _access.getAdapter(step).getPropertyAdapter(name);
-
- if (pa == null)
- throw new RuntimeException(BindingsMessages
- .noSuchProperty(step, name, propertyPath));
-
- Method m = pa.getReadMethod();
-
- if (m == null)
- throw new RuntimeException(BindingsMessages.writeOnlyProperty(
- name,
- step,
- propertyPath));
-
- // Extend the expression by invoking the read access method.
-
- builder.append(".");
- builder.append(m.getName());
- builder.append("()");
-
- // The next property (in the path, or the terminal) that is evaluated is in terms of the
- // return type of this method.
-
- step = pa.getType();
- }
-
- String terminalName = terms[terms.length - 1];
-
- PropertyAdapter adapter = _access.getAdapter(step).getPropertyAdapter(terminalName);
-
- if (adapter == null)
- throw new RuntimeException(BindingsMessages.noSuchProperty(
- targetClass,
- terminalName,
- propertyPath));
-
- Class bindingClass = createBindingClass(
- targetClass,
- builder.toString(),
- terminalName,
- adapter);
-
- // The fabricated class is only going to have the one constructor. This is the easiest
- // way to access it.
-
- BindingConstructor result = new BindingConstructor(adapter.getType(), bindingClass
- .getConstructors()[0]);
-
- return result;
- }
-
- /**
- * @param targetClass
- * root class of expression
- * @param pathExpression
- * Javassist expression to navigate to the terminal property, starting with the
- * _target instance variable
- * @param propertyName
- * the name of the terminal property
- * @param adapter
- * property adapter for the terminal property
- * @return Class that implements Binding
- */
- private Class createBindingClass(Class targetClass, String pathExpression, String propertyName,
- PropertyAdapter adapter)
- {
- String name = ClassFabUtils.generateClassName("PropBinding");
-
- ClassFab classFab = _classFactory.newClass(name, BasePropBinding.class);
-
- classFab.addField("_target", targetClass);
-
- classFab.addConstructor(
- new Class[]
- { targetClass, Class.class, String.class, Location.class },
- null,
- "{ super($2, $3, $4); _target = $1; }");
-
- if (adapter.isRead())
- {
- String body = String.format("return ($w) %s.%s();", pathExpression, adapter
- .getReadMethod().getName());
-
- classFab.addMethod(Modifier.PUBLIC, GET_SIGNATURE, body);
- }
-
- if (adapter.isUpdate())
- {
- Class propertyType = adapter.getType();
-
- BodyBuilder builder = new BodyBuilder();
- builder.begin();
-
- String propertyTypeName = propertyType.getName();
- builder.add("%s value = ", propertyTypeName);
-
- if (propertyType.isPrimitive())
- {
- String wrapperType = ClassFabUtils.getWrapperTypeName(propertyTypeName);
- String unwrapMethod = ClassFabUtils.getUnwrapMethodName(propertyTypeName);
-
- // Cast the value to the wrapper type, and then extract the primitive
- // value from that.
-
- builder.addln("((%s) $1).%s();", wrapperType, unwrapMethod);
- }
- else
- builder.addln("(%s) $1;", propertyTypeName);
-
- builder.addln("%s.%s(value);", pathExpression, adapter.getWriteMethod().getName());
- builder.end();
-
- classFab.addMethod(Modifier.PUBLIC, SET_SIGNATURE, builder.toString());
- }
-
- return classFab.createClass();
- }
-
- /**
- * The cache contains references to classes loaded by the Tapestry class loader. When that
- * loader is invalidated (due to class file changes), the cache is cleared and rebuilt.
- */
- public void objectWasInvalidated()
- {
- _cache.clear();
- }
-
-}
+import org.apache.tapestry.services.BindingFactory;
+
+/**
+ * Binding factory for reading and updating JavaBean properties. Uses
+ * {@link org.apache.tapestry.ioc.services.PropertyAccess} to analyze the properties, and generates
+ * a binding class using the component {@link org.apache.tapestry.ioc.services.ClassFactory}.
+ */
+public class PropBindingFactory implements BindingFactory, InvalidationListener
+{
+ private final PropertyAccess _access;
+
+ private final ClassFactory _classFactory;
+
+ // The key is a combination of class name and property path.
+ private final Map<String, BindingConstructor> _cache = newThreadSafeMap();
+
+ private static class BindingConstructor
+ {
+ private final Class _propertyType;
+
+ private final Constructor _constructor;
+
+ BindingConstructor(Class propertyType, Constructor constructor)
+ {
+ _propertyType = propertyType;
+ _constructor = constructor;
+ }
+
+ Binding newBindingInstance(Object target, String toString, Location location)
+ throws Exception
+ {
+ return (Binding) _constructor.newInstance(target, _propertyType, toString, location);
+ }
+ }
+
+ private static final MethodSignature GET_SIGNATURE = new MethodSignature(Object.class, "get",
+ null, null);
+
+ private static final MethodSignature SET_SIGNATURE = new MethodSignature(void.class, "set",
+ new Class[]
+ { Object.class }, null);
+
+ public PropBindingFactory(PropertyAccess propertyAccess, ClassFactory classFactory)
+ {
+ _access = propertyAccess;
+ _classFactory = classFactory;
+ }
+
+ public Binding newBinding(String description, ComponentResources container,
+ ComponentResources component, String expression, Location location)
+ {
+ Object target = container.getComponent();
+ Class targetClass = target.getClass();
+
+ try
+ {
+ BindingConstructor cons = findCachedConstructor(targetClass, expression);
+
+ String toString = String.format("PropBinding[%s %s(%s)]", description, container
+ .getCompleteId(), expression);
+
+ return cons.newBindingInstance(target, toString, location);
+ }
+ catch (Throwable ex)
+ {
+ throw new TapestryException(ex.getMessage(), location, ex);
+ }
+ }
+
+ /**
+ * Searches for a cached binding constructor for the given target class and property name. As
+ * necessary, a new cached constructor is created.
+ *
+ * @param targetClass
+ * the class of the component which contains the property
+ * @param propertyName
+ * the name of the property to expose as a binding
+ * @return
+ */
+ private BindingConstructor findCachedConstructor(Class targetClass, String propertyName)
+ {
+ // The only problem with this key is if we can get in a situation where two different
+ // versions of the class (from the default class loader, and the component class loader)
+ // are accessed in this way at the same time. I'm pretty sure that can't happen.
+
+ String key = targetClass.getName() + ":" + propertyName;
+
+ BindingConstructor result = _cache.get(key);
+
+ if (result == null)
+ {
+ result = createConstructor(key, targetClass, propertyName);
+ _cache.put(key, result);
+ }
+
+ return result;
+ }
+
+ private BindingConstructor createConstructor(String key, Class targetClass, String propertyPath)
+ {
+ // Race condition: simulataneous calls to fillCache() for the same targetClass/propertyName
+ // combination may result in duplicate binding classes being created, which causes no great
+ // harm.
+
+ StringBuilder builder = new StringBuilder("_target");
+
+ Class step = targetClass;
+ String[] terms = propertyPath.split("\\.");
+
+ for (int i = 0; i < terms.length - 1; i++)
+ {
+ String name = terms[i];
+
+ PropertyAdapter pa = _access.getAdapter(step).getPropertyAdapter(name);
+
+ if (pa == null)
+ throw new RuntimeException(BindingsMessages
+ .noSuchProperty(step, name, propertyPath));
+
+ Method m = pa.getReadMethod();
+
+ if (m == null)
+ throw new RuntimeException(BindingsMessages.writeOnlyProperty(
+ name,
+ step,
+ propertyPath));
+
+ // Extend the expression by invoking the read access method.
+
+ builder.append(".");
+ builder.append(m.getName());
+ builder.append("()");
+
+ // The next property (in the path, or the terminal) that is evaluated is in terms of the
+ // return type of this method.
+
+ step = pa.getType();
+ }
+
+ String terminalName = terms[terms.length - 1];
+
+ PropertyAdapter adapter = _access.getAdapter(step).getPropertyAdapter(terminalName);
+
+ if (adapter == null)
+ throw new RuntimeException(BindingsMessages.noSuchProperty(
+ targetClass,
+ terminalName,
+ propertyPath));
+
+ Class bindingClass = createBindingClass(
+ targetClass,
+ builder.toString(),
+ terminalName,
+ adapter);
+
+ // The fabricated class is only going to have the one constructor. This is the easiest
+ // way to access it.
+
+ BindingConstructor result = new BindingConstructor(adapter.getType(), bindingClass
+ .getConstructors()[0]);
+
+ return result;
+ }
+
+ /**
+ * @param targetClass
+ * root class of expression
+ * @param pathExpression
+ * Javassist expression to navigate to the terminal property, starting with the
+ * _target instance variable
+ * @param propertyName
+ * the name of the terminal property
+ * @param adapter
+ * property adapter for the terminal property
+ * @return Class that implements Binding
+ */
+ private Class createBindingClass(Class targetClass, String pathExpression, String propertyName,
+ PropertyAdapter adapter)
+ {
+ String name = ClassFabUtils.generateClassName("PropBinding");
+
+ ClassFab classFab = _classFactory.newClass(name, BasePropBinding.class);
+
+ classFab.addField("_target", targetClass);
+
+ classFab.addConstructor(
+ new Class[]
+ { targetClass, Class.class, String.class, Location.class },
+ null,
+ "{ super($2, $3, $4); _target = $1; }");
+
+ if (adapter.isRead())
+ {
+ String body = String.format("return ($w) %s.%s();", pathExpression, adapter
+ .getReadMethod().getName());
+
+ classFab.addMethod(Modifier.PUBLIC, GET_SIGNATURE, body);
+ }
+
+ if (adapter.isUpdate())
+ {
+ Class propertyType = adapter.getType();
+
+ BodyBuilder builder = new BodyBuilder();
+ builder.begin();
+
+ String propertyTypeName = propertyType.getName();
+ builder.add("%s value = ", propertyTypeName);
+
+ if (propertyType.isPrimitive())
+ {
+ String wrapperType = ClassFabUtils.getWrapperTypeName(propertyTypeName);
+ String unwrapMethod = ClassFabUtils.getUnwrapMethodName(propertyTypeName);
+
+ // Cast the value to the wrapper type, and then extract the primitive
+ // value from that.
+
+ builder.addln("((%s) $1).%s();", wrapperType, unwrapMethod);
+ }
+ else
+ builder.addln("(%s) $1;", propertyTypeName);
+
+ builder.addln("%s.%s(value);", pathExpression, adapter.getWriteMethod().getName());
+ builder.end();
+
+ classFab.addMethod(Modifier.PUBLIC, SET_SIGNATURE, builder.toString());
+ }
+
+ return classFab.createClass();
+ }
+
+ /**
+ * The cache contains references to classes loaded by the Tapestry class loader. When that
+ * loader is invalidated (due to class file changes), the cache is cleared and rebuilt.
+ */
+ public void objectWasInvalidated()
+ {
+ _cache.clear();
+ }
+
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/ValidatorBindingFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/ValidatorBindingFactory.java?view=auto&rev=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/ValidatorBindingFactory.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/ValidatorBindingFactory.java Thu Dec 21 15:09:45 2006
@@ -0,0 +1,60 @@
+// Copyright 2006 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.tapestry.internal.bindings;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.Field;
+import org.apache.tapestry.FieldValidator;
+import org.apache.tapestry.ioc.Location;
+import org.apache.tapestry.ioc.internal.util.TapestryException;
+import org.apache.tapestry.ioc.services.FieldValidatorSource;
+import org.apache.tapestry.services.Binding;
+import org.apache.tapestry.services.BindingFactory;
+
+/**
+ * Factory for bindings that provide a {@link FieldValidator} based on a validator specification.
+ * This binding factory is only useable with components that implement the {@link Field} interface.
+ */
+public class ValidatorBindingFactory implements BindingFactory
+{
+ private final FieldValidatorSource _fieldValidatorSource;
+
+ public ValidatorBindingFactory(FieldValidatorSource fieldValidatorSource)
+ {
+ _fieldValidatorSource = fieldValidatorSource;
+ }
+
+ public Binding newBinding(String description, ComponentResources container,
+ ComponentResources component, String expression, Location location)
+ {
+ Object fieldAsObject = component.getComponent();
+
+ if (!Field.class.isInstance(fieldAsObject))
+ throw new TapestryException(BindingsMessages.validatorBindingForFieldsOnly(component),
+ location, null);
+
+ Field field = (Field) fieldAsObject;
+
+ // The expression is a validator specification, such as "required,minLength=5".
+ // ValidatorBindingFactory is the odd man out becasuse it needs the binding component (the
+ // component whose parameter is to be bound) rather than the containing component, the way
+ // most factories work.
+
+ FieldValidator validator = _fieldValidatorSource.createValidators(field, expression);
+
+ return new LiteralBinding(description, validator, location);
+ }
+
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BindingSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BindingSourceImpl.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BindingSourceImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/BindingSourceImpl.java Thu Dec 21 15:09:45 2006
@@ -12,64 +12,67 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package org.apache.tapestry.internal.services;
-
-import java.util.Map;
-
-import org.apache.tapestry.ComponentResources;
+package org.apache.tapestry.internal.services;
+
+import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;
+import static org.apache.tapestry.ioc.internal.util.Defense.notNull;
+
+import java.util.Map;
+
+import org.apache.tapestry.ComponentResources;
import org.apache.tapestry.ioc.Location;
-import org.apache.tapestry.ioc.internal.util.Defense;
import org.apache.tapestry.ioc.internal.util.TapestryException;
import org.apache.tapestry.services.Binding;
-import org.apache.tapestry.services.BindingFactory;
-import org.apache.tapestry.services.BindingSource;
-
-public class BindingSourceImpl implements BindingSource
-{
- private final Map<String, BindingFactory> _factories;
-
- public BindingSourceImpl(Map<String, BindingFactory> factories)
- {
- _factories = factories;
- }
-
- public Binding newBinding(String description, ComponentResources component,
- String defaultPrefix, String expression, Location location)
- {
- Defense.notBlank(description, "description");
- Defense.notNull(component, "component");
- Defense.notBlank(defaultPrefix, "defaultPrefix");
- Defense.notBlank(expression, "expression");
- // Location might be null
-
- String subexpression = expression;
- int colonx = expression.indexOf(':');
-
- BindingFactory factory = null;
-
- if (colonx > 0)
- {
- String prefix = expression.substring(0, colonx);
-
- factory = _factories.get(prefix);
- if (factory != null)
- subexpression = expression.substring(colonx + 1);
- }
-
- if (factory == null)
- factory = _factories.get(defaultPrefix);
-
- // And if that's null, what then? We assume that the default prefix is a valid prefix,
- // or we'll get an NPE below and report it like any other error.
-
- try
- {
- return factory.newBinding(description, component, subexpression, location);
- }
- catch (Exception ex)
- {
- throw new TapestryException(ServicesMessages.bindingSourceFailure(expression, ex),
- location, ex);
- }
- }
-}
+import org.apache.tapestry.services.BindingFactory;
+import org.apache.tapestry.services.BindingSource;
+
+public class BindingSourceImpl implements BindingSource
+{
+ private final Map<String, BindingFactory> _factories;
+
+ public BindingSourceImpl(Map<String, BindingFactory> factories)
+ {
+ _factories = factories;
+ }
+
+ public Binding newBinding(String description, ComponentResources container,
+ ComponentResources component, String defaultPrefix, String expression, Location location)
+ {
+ notBlank(description, "description");
+ notNull(container, "container");
+ notNull(component, "component");
+ notBlank(defaultPrefix, "defaultPrefix");
+ notBlank(expression, "expression");
+ // Location might be null
+
+ String subexpression = expression;
+ int colonx = expression.indexOf(':');
+
+ BindingFactory factory = null;
+
+ if (colonx > 0)
+ {
+ String prefix = expression.substring(0, colonx);
+
+ factory = _factories.get(prefix);
+ if (factory != null)
+ subexpression = expression.substring(colonx + 1);
+ }
+
+ if (factory == null)
+ factory = _factories.get(defaultPrefix);
+
+ // And if that's null, what then? We assume that the default prefix is a valid prefix,
+ // or we'll get an NPE below and report it like any other error.
+
+ try
+ {
+ return factory.newBinding(description, container, component, subexpression, location);
+ }
+ catch (Exception ex)
+ {
+ throw new TapestryException(ServicesMessages.bindingSourceFailure(expression, ex),
+ location, ex);
+ }
+ }
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java Thu Dec 21 15:09:45 2006
@@ -376,8 +376,8 @@
_keywords.put("null", null);
}
- public Binding newBinding(String description, ComponentResources component,
- String expression, Location location)
+ public Binding newBinding(String description, ComponentResources container,
+ ComponentResources component, String expression, Location location)
{
String key = expression.trim().toLowerCase();
@@ -391,11 +391,11 @@
BindingFactory thisFactory = new BindingFactory()
{
- public Binding newBinding(String description, ComponentResources component,
- String expression, Location location)
+ public Binding newBinding(String description, ComponentResources container,
+ ComponentResources component, String expression, Location location)
{
if ("this".equalsIgnoreCase(expression.trim()))
- return new LiteralBinding(description, component.getComponent(), location);
+ return new LiteralBinding(description, container.getComponent(), location);
return null;
}
@@ -405,8 +405,8 @@
{
private final Pattern _pattern = Pattern.compile("^\\s*(-?\\d+)\\s*$");
- public Binding newBinding(String description, ComponentResources component,
- String expression, Location location)
+ public Binding newBinding(String description, ComponentResources container,
+ ComponentResources component, String expression, Location location)
{
Matcher matcher = _pattern.matcher(expression);
@@ -426,8 +426,8 @@
private final Pattern _pattern = Pattern
.compile("^\\s*(-?\\d+)\\s*\\.\\.\\s*(-?\\d+)\\s*$");
- public Binding newBinding(String description, ComponentResources component,
- String expression, Location location)
+ public Binding newBinding(String description, ComponentResources container,
+ ComponentResources component, String expression, Location location)
{
Matcher matcher = _pattern.matcher(expression);
@@ -451,8 +451,8 @@
private final Pattern _pattern = Pattern
.compile("^\\s*(\\-?((\\d+\\.)|(\\d*\\.\\d+)))\\s*$");
- public Binding newBinding(String description, ComponentResources component,
- String expression, Location location)
+ public Binding newBinding(String description, ComponentResources container,
+ ComponentResources component, String expression, Location location)
{
Matcher matcher = _pattern.matcher(expression);
@@ -473,8 +473,8 @@
private final Pattern _pattern = Pattern.compile("^\\s*'(.*)'\\s*$");
- public Binding newBinding(String description, ComponentResources component,
- String expression, Location location)
+ public Binding newBinding(String description, ComponentResources container,
+ ComponentResources component, String expression, Location location)
{
Matcher matcher = _pattern.matcher(expression);
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java Thu Dec 21 15:09:45 2006
@@ -109,9 +109,9 @@
Binding binding = _bindingSource.newBinding(
"expansion",
componentResources,
+ componentResources,
InternalConstants.PROP_BINDING_PREFIX,
- token.getExpression(),
- token.getLocation());
+ token.getExpression(), token.getLocation());
return new ExpansionPageElement(binding, _typeCoercer);
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java Thu Dec 21 15:09:45 2006
@@ -385,8 +385,13 @@
// At some point we may add meta data to control what the default prefix is within a
// component.
- Binding binding = _bindingSource.newBinding("parameter " + name, loadingComponent
- .getComponentResources(), InternalConstants.PROP_BINDING_PREFIX, value, null);
+ Binding binding = _bindingSource.newBinding(
+ "parameter " + name,
+ loadingComponent.getComponentResources(),
+ component.getComponentResources(),
+ InternalConstants.PROP_BINDING_PREFIX,
+ value,
+ null);
component.addParameter(name, binding);
}
@@ -427,6 +432,7 @@
Binding binding = _bindingSource.newBinding(
"parameter " + name,
loadingElement.getComponentResources(),
+ component.getComponentResources(),
InternalConstants.PROP_BINDING_PREFIX,
token.getValue(),
token.getLocation());
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ValidatorSpecification.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ValidatorSpecification.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ValidatorSpecification.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ValidatorSpecification.java Thu Dec 21 15:09:45 2006
@@ -1,3 +1,17 @@
+// Copyright 2006 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.tapestry.internal.services;
/** Validator type and constraint values parsed from a validator specification. */
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java Thu Dec 21 15:09:45 2006
@@ -53,8 +53,8 @@
import org.apache.tapestry.services.BindingSource;
import org.apache.tapestry.services.ComponentClassResolver;
import org.apache.tapestry.services.Infrastructure;
-import org.apache.tapestry.services.TapestryModule;
import org.apache.tapestry.services.Request;
+import org.apache.tapestry.services.TapestryModule;
import org.apache.tapestry.test.TapestryTestCase;
import org.easymock.EasyMock;
import org.testng.annotations.AfterMethod;
@@ -134,11 +134,6 @@
protected final ComponentInstantiatorSource newComponentInstantiatorSource()
{
return newMock(ComponentInstantiatorSource.class);
- }
-
- protected Component newComponent()
- {
- return newMock(Component.class);
}
protected final Page newPage()
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingFactory.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingFactory.java Thu Dec 21 15:09:45 2006
@@ -12,31 +12,37 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package org.apache.tapestry.services;
-
-import org.apache.tapestry.ComponentResources;
+package org.apache.tapestry.services;
+
+import org.apache.tapestry.ComponentResources;
import org.apache.tapestry.ioc.Location;
-
-/**
- * Creates a binding of a particular type.
- *
- *
- */
-public interface BindingFactory
-{
- /**
- * Creates a new binding instance.
- *
- * @param description
- * of the binding, such as, "parameter foo"
- * @param component
- * the component, as represented by its resources, for which a binding is to be
- * created.
- * @param expression
- * @param location
- * from which the binding was generate, or null if not known
- * @return the new binding instance
- */
- Binding newBinding(String description, ComponentResources component, String expression,
- Location location);
-}
+
+/**
+ * Creates a binding of a particular type.
+ */
+public interface BindingFactory
+{
+ /**
+ * Creates a new binding instance.
+ * <p>
+ * The binding represents a connection between the container and the component (the component is
+ * usually the child of the component, though in a few cases, it is the component itself). In
+ * most cases, the expression is evaluated in terms of the resources of the <em>container</em>
+ * and the component is ignored.
+ *
+ * @param description
+ * of the binding, such as, "parameter foo"
+ * @param container
+ * the component, as represented by its resources, for which a binding is to be
+ * created.
+ * @param component
+ * the component whose parameter is to be bound by the resulting binding (rarely
+ * used)
+ * @param expression
+ * @param location
+ * from which the binding was generate, or null if not known
+ * @return the new binding instance
+ */
+ Binding newBinding(String description, ComponentResources container,
+ ComponentResources component, String expression, Location location);
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/BindingSource.java Thu Dec 21 15:09:45 2006
@@ -12,35 +12,41 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package org.apache.tapestry.services;
-
-import org.apache.tapestry.ComponentResources;
+package org.apache.tapestry.services;
+
+import org.apache.tapestry.ComponentResources;
import org.apache.tapestry.ioc.Location;
-
-/**
- * Used to acquire bindings for component parameters.
- *
- *
- */
-public interface BindingSource
-{
- /**
- * Examines the expression and strips off the leading prefix. The prefix is used to choose the
- * appropriate {@link BindingFactory}, which recieves the description, the expression (after
- * the prefix), and the location. If the prefix doesn't exist, or if there's no prefix, then the
- * factory for the default prefix (often "literal") is used (and passed the full prefix).
- *
- * @param description
- * description of the binding, such as "parameter foo"
- * @param component
- * @param defaultPrefix
- * the default prefix used when the expression itself does not have a prefix
- * @param expression
- * the binding
- * @param location
- * location assigned to the binding (or null if not known)
- * @return a binding
- */
- Binding newBinding(String description, ComponentResources component, String defaultPrefix,
- String expression, Location location);
-}
+
+/**
+ * Used to acquire bindings for component parameters.
+ */
+public interface BindingSource
+{
+ /**
+ * Examines the expression and strips off the leading prefix. The prefix is used to choose the
+ * appropriate {@link BindingFactory}, which recieves the description, the expression (after
+ * the prefix), and the location. If the prefix doesn't exist, or if there's no prefix, then the
+ * factory for the default prefix (often "literal") is used (and passed the full prefix).
+ * <p>
+ * The binding represents a connection between the container and the component (the component is
+ * usually the child of the component, though in a few cases, it is the component itself). In
+ * most cases, the expression is evaluated in terms of the resources of the <em>container</em>
+ * and the component is ignored.
+ *
+ * @param description
+ * description of the binding, such as "parameter foo"
+ * @param container
+ * typically, the parent of the component
+ * @param component
+ * the component whose paramter is to be bound
+ * @param defaultPrefix
+ * the default prefix used when the expression itself does not have a prefix
+ * @param expression
+ * the binding
+ * @param location
+ * location assigned to the binding (or null if not known)
+ * @return a binding
+ */
+ Binding newBinding(String description, ComponentResources container,
+ ComponentResources component, String defaultPrefix, String expression, Location location);
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java Thu Dec 21 15:09:45 2006
@@ -42,6 +42,7 @@
import org.apache.tapestry.internal.bindings.ComponentBindingFactory;
import org.apache.tapestry.internal.bindings.LiteralBindingFactory;
import org.apache.tapestry.internal.bindings.MessageBindingFactory;
+import org.apache.tapestry.internal.bindings.ValidatorBindingFactory;
import org.apache.tapestry.internal.services.ActionLinkHandler;
import org.apache.tapestry.internal.services.ActionLinkHandlerImpl;
import org.apache.tapestry.internal.services.ApplicationGlobalsImpl;
@@ -131,8 +132,7 @@
* The root module for Tapestry.
*/
@Id("tapestry")
-@SubModule(
-{ InternalModule.class })
+@SubModule(InternalModule.class)
public final class TapestryModule
{
private final ChainBuilder _chainBuilder;
@@ -516,17 +516,19 @@
/**
* Contributes the factory for serveral built-in binding prefixes ("literal", prop", "component"
- * and "message").
+ * "message" and "validator").
*/
public static void contributeBindingSource(
MappedConfiguration<String, BindingFactory> configuration,
@InjectService("tapestry.internal.PropBindingFactory")
- BindingFactory propBindingFactory)
+ BindingFactory propBindingFactory, @Inject("infrastructure:FieldValidatorSource")
+ FieldValidatorSource fieldValidatorSource)
{
configuration.add(InternalConstants.LITERAL_BINDING_PREFIX, new LiteralBindingFactory());
configuration.add(InternalConstants.PROP_BINDING_PREFIX, propBindingFactory);
configuration.add("component", new ComponentBindingFactory());
configuration.add("message", new MessageBindingFactory());
+ configuration.add("validator", new ValidatorBindingFactory(fieldValidatorSource));
}
/**
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java Thu Dec 21 15:09:45 2006
@@ -38,6 +38,7 @@
import org.apache.tapestry.Asset;
import org.apache.tapestry.ComponentResources;
import org.apache.tapestry.ComponentResourcesCommon;
+import org.apache.tapestry.FieldValidator;
import org.apache.tapestry.MarkupWriter;
import org.apache.tapestry.Validator;
import org.apache.tapestry.annotations.Inject;
@@ -47,6 +48,7 @@
import org.apache.tapestry.ioc.Resource;
import org.apache.tapestry.ioc.ServiceLocator;
import org.apache.tapestry.ioc.internal.util.ClasspathResource;
+import org.apache.tapestry.ioc.services.FieldValidatorSource;
import org.apache.tapestry.ioc.services.ThreadLocale;
import org.apache.tapestry.ioc.test.IOCTestCase;
import org.apache.tapestry.model.ComponentModel;
@@ -181,9 +183,11 @@
}
protected final void train_newBinding(BindingFactory factory, String description,
- ComponentResources component, String expression, Location l, Binding b)
+ ComponentResources container, ComponentResources component, String expression,
+ Location l, Binding binding)
{
- expect(factory.newBinding(description, component, expression, l)).andReturn(b);
+ expect(factory.newBinding(description, container, component, expression, l)).andReturn(
+ binding);
}
protected final ComponentResources newComponentResources()
@@ -401,8 +405,8 @@
return newMock(RequestHandler.class);
}
- protected final void train_service(RequestHandler handler, Request request,
- Response response, boolean result) throws IOException
+ protected final void train_service(RequestHandler handler, Request request, Response response,
+ boolean result) throws IOException
{
expect(handler.service(request, response)).andReturn(result);
}
@@ -465,7 +469,8 @@
protected final <T extends Annotation> void train_getMethodAnnotation(ClassTransformation ct,
MethodSignature signature, Class<T> annotationClass, T annotation)
{
- expect(ct.getMethodAnnotation(signature, annotationClass)).andReturn(annotation).atLeastOnce();
+ expect(ct.getMethodAnnotation(signature, annotationClass)).andReturn(annotation)
+ .atLeastOnce();
}
protected final ClasspathAssetAliasManager newClasspathAssetAliasManager()
@@ -511,7 +516,8 @@
expect(request.getPath()).andReturn(path).atLeastOnce();
}
- protected final void train_toResourcePath(ClasspathAssetAliasManager manager, String clientURL, String resourcePath)
+ protected final void train_toResourcePath(ClasspathAssetAliasManager manager, String clientURL,
+ String resourcePath)
{
expect(manager.toResourcePath(clientURL)).andReturn(resourcePath).atLeastOnce();
}
@@ -521,7 +527,8 @@
expect(request.getDateHeader(name)).andReturn(value).atLeastOnce();
}
- protected final void train_findMethods(ClassTransformation transformation, final MethodSignature... signatures)
+ protected final void train_findMethods(ClassTransformation transformation,
+ final MethodSignature... signatures)
{
IAnswer<List<MethodSignature>> answer = new IAnswer<List<MethodSignature>>()
{
@@ -529,24 +536,24 @@
{
// Can't think of a way to do this without duplicating some code out of
// InternalClassTransformationImpl
-
+
List<MethodSignature> result = newList();
MethodFilter filter = (MethodFilter) EasyMock.getCurrentArguments()[0];
-
+
for (MethodSignature sig : signatures)
{
if (filter.accept(sig))
result.add(sig);
}
-
+
// We don't have to sort them for testing purposes. Usually there's just going to be
// one in there.
-
+
return result;
}
-
+
};
-
+
expect(transformation.findMethods(EasyMock.isA(MethodFilter.class))).andAnswer(answer);
}
@@ -555,7 +562,8 @@
expect(model.isRootClass()).andReturn(isRootClass);
}
- protected final void train_getValidationMessages(ValidationMessagesSource messagesSource, Locale locale, Messages messages)
+ protected final void train_getValidationMessages(ValidationMessagesSource messagesSource,
+ Locale locale, Messages messages)
{
expect(messagesSource.getValidationMessages(locale)).andReturn(messages).atLeastOnce();
}
@@ -578,5 +586,25 @@
protected final ValidationMessagesSource newValidationMessagesSource()
{
return newMock(ValidationMessagesSource.class);
+ }
+
+ protected final FieldValidator newFieldValidator()
+ {
+ return newMock(FieldValidator.class);
+ }
+
+ protected FieldValidatorSource newFieldValidatorSource()
+ {
+ return newMock(FieldValidatorSource.class);
+ }
+
+ protected final Component newComponent()
+ {
+ return newMock(Component.class);
+ }
+
+ protected final void train_getComponent(ComponentResources resources, Component component)
+ {
+ expect(resources.getComponent()).andReturn(component).atLeastOnce();
}
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/bindings/BindingsStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/bindings/BindingsStrings.properties?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/bindings/BindingsStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/bindings/BindingsStrings.properties Thu Dec 21 15:09:45 2006
@@ -16,3 +16,4 @@
binding-is-write-only=Binding %s is write-only.
no-such-property=Class %s does not contain a property named '%s' (within property path '%s').
write-only-property=Property '%s' of class %s (within property path '%s') is not readable (it has no read accessor method).
+validator-binding-for-fields-only=Component '%s' is not a field (it does not implement the Field interface) and may not be used with the validator: binding prefix.
Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt Thu Dec 21 15:09:45 2006
@@ -125,7 +125,8 @@
*------------+----------------------------------------------------------------------------------+
| prop: | The name of a property of the containing component to read or update. |
*------------+----------------------------------------------------------------------------------+
-
+| validator: | A <validator specification> used to create some number of field validators. |
+*------------+----------------------------------------------------------------------------------+
The default binding prefix is "prop:".
@@ -162,6 +163,24 @@
Such values are read only and invariant.
+Validator Bindings
+
+ The "validator:" binding prefix is highly specialized. It allows a short string to be
+ used to create and configure the objects that perform input validation for
+ form control components, such as TextField and Checkbox.
+
+ The string is a comma-separated list of <validator types>. These are short aliases
+ that for objects that perform the validation. In many cases, the validation is configurable
+ in some way: for example, a validator that enforces a minimum string length
+ needs to know what that minimum string length is. Such values are specified after an equals sign.
+
+ For example: <<<validator:required,minLength=5>>> would presumably enforce that a field
+ requires a value, with at least five characters.
+
+ TODO: More ability to escape or quote constraint values. Ability to reference methods
+ or properties of the container to perform some of the validation. Links to
+ proper discussion of validation, once the code and documentation is ready.
+
Parameters Are Bi-Directional
Parameters are not simply variables; each parameter represents a connection, or <binding>, between
@@ -190,8 +209,8 @@
(Though the whitespace will be quite different.)
- The relevant part is that components can read fixed values, or live properties of their
- container, and can <change> properties of their container as well.
+ The relevant part is that components can read fixed values, or <live>
+ properties of their container, and can <change> properties of their container as well.
Required Parameters
Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/TapestryUtilsTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/TapestryUtilsTest.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/TapestryUtilsTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/TapestryUtilsTest.java Thu Dec 21 15:09:45 2006
@@ -75,4 +75,20 @@
{ "z", "z" },
{ "0abc", "0abc" } };
}
+
+ @Test(dataProvider = "to_user_presentable")
+ public void to_user_presentable(String input, String expected)
+ {
+ assertEquals(TapestryUtils.toUserPresentable(input), expected);
+ }
+
+ @DataProvider(name = "to_user_presentable")
+ public Object[][] to_user_presentable_data()
+ {
+ return new Object[][]
+ {
+ { "hello", "Hello" },
+ { "userId", "User Id" },
+ { "useHTML", "Use HTML" }, };
+ }
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/BindingFactoryTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/BindingFactoryTest.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/BindingFactoryTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/BindingFactoryTest.java Thu Dec 21 15:09:45 2006
@@ -41,7 +41,7 @@
BindingFactory factory = new LiteralBindingFactory();
- Binding b = factory.newBinding("test binding", res, "Tapestry5", l);
+ Binding b = factory.newBinding("test binding", res, null, "Tapestry5", l);
assertSame(InternalUtils.locationOf(b), l);
Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/PropBindingFactoryTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/PropBindingFactoryTest.java?view=diff&rev=489493&r1=489492&r2=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/PropBindingFactoryTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/PropBindingFactoryTest.java Thu Dec 21 15:09:45 2006
@@ -56,11 +56,6 @@
return resources;
}
- private void train_getComponent(ComponentResources resources, Component component)
- {
- expect(resources.getComponent()).andReturn(component);
- }
-
@Test
public void object_property()
{
@@ -70,7 +65,7 @@
replay();
- Binding binding = _factory.newBinding("test binding", resources, "objectValue", l);
+ Binding binding = _factory.newBinding("test binding", resources, null, "objectValue", l);
assertSame(binding.getBindingType(), String.class);
@@ -97,7 +92,12 @@
replay();
- Binding binding = _factory.newBinding("test binding", resources, "stringHolder.value", l);
+ Binding binding = _factory.newBinding(
+ "test binding",
+ resources,
+ null,
+ "stringHolder.value",
+ l);
assertSame(binding.getBindingType(), String.class);
@@ -132,7 +132,7 @@
try
{
- _factory.newBinding("test binding", resources, propertyPath, l);
+ _factory.newBinding("test binding", resources, null, propertyPath, l);
unreachable();
}
catch (TapestryException ex)
@@ -162,7 +162,7 @@
try
{
- _factory.newBinding("test binding", resources, propertyPath, l);
+ _factory.newBinding("test binding", resources, null, propertyPath, l);
unreachable();
}
catch (TapestryException ex)
@@ -187,7 +187,7 @@
replay();
- Binding binding = _factory.newBinding("test binding", resources, "intValue", l);
+ Binding binding = _factory.newBinding("test binding", resources, null, "intValue", l);
assertSame(binding.getBindingType(), int.class);
@@ -211,7 +211,7 @@
replay();
- Binding binding = _factory.newBinding("test binding", resources, "readOnly", l);
+ Binding binding = _factory.newBinding("test binding", resources, null, "readOnly", l);
assertEquals(binding.get(), "ReadOnly");
@@ -240,7 +240,7 @@
replay();
- Binding binding = _factory.newBinding("test binding", resources, "writeOnly", l);
+ Binding binding = _factory.newBinding("test binding", resources, null, "writeOnly", l);
binding.set("updated");
@@ -281,8 +281,8 @@
replay();
- Binding binding1 = factory.newBinding("test binding 1", resources1, "objectValue", l);
- Binding binding2 = factory.newBinding("test binding 2", resources2, "objectValue", l);
+ Binding binding1 = factory.newBinding("test binding 1", resources1, null, "objectValue", l);
+ Binding binding2 = factory.newBinding("test binding 2", resources2, null, "objectValue", l);
assertSame(binding2.getClass(), binding1.getClass());
@@ -296,7 +296,7 @@
factory.objectWasInvalidated();
- Binding binding3 = factory.newBinding("test binding 3", resources3, "objectValue", l);
+ Binding binding3 = factory.newBinding("test binding 3", resources3, null, "objectValue", l);
// We'll assume the behavior is the same, but expect the class to be different.
@@ -318,7 +318,7 @@
try
{
- _factory.newBinding("test binding", resources, "missingProperty", l);
+ _factory.newBinding("test binding", resources, null, "missingProperty", l);
unreachable();
}
catch (TapestryException ex)
@@ -345,7 +345,7 @@
replay();
- Binding binding = _factory.newBinding(description, resources, "this", l);
+ Binding binding = _factory.newBinding(description, resources, null, "this", l);
assertSame(binding.get(), component);
@@ -361,7 +361,7 @@
replay();
- Binding binding = _factory.newBinding(description, resources, expression, l);
+ Binding binding = _factory.newBinding(description, resources, null, expression, l);
assertEquals(binding.get(), expected);
Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/ValidatorBindingFactoryTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/ValidatorBindingFactoryTest.java?view=auto&rev=489493
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/ValidatorBindingFactoryTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/ValidatorBindingFactoryTest.java Thu Dec 21 15:09:45 2006
@@ -0,0 +1,93 @@
+// Copyright 2006 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.tapestry.internal.bindings;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.Field;
+import org.apache.tapestry.FieldValidator;
+import org.apache.tapestry.ioc.Location;
+import org.apache.tapestry.ioc.internal.util.TapestryException;
+import org.apache.tapestry.ioc.services.FieldValidatorSource;
+import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.services.Binding;
+import org.apache.tapestry.services.BindingFactory;
+import org.apache.tapestry.test.TapestryTestCase;
+import org.testng.annotations.Test;
+
+public class ValidatorBindingFactoryTest extends TapestryTestCase
+{
+ private interface FieldComponent extends Field, Component
+ {
+ };
+
+ @Test
+ public void not_a_field()
+ {
+ FieldValidatorSource source = newFieldValidatorSource();
+ ComponentResources container = newComponentResources();
+ ComponentResources component = newComponentResources();
+ Component instance = newComponent();
+ Location l = newLocation();
+
+ train_getComponent(component, instance);
+ train_getCompleteId(component, "foo.Bar:baz");
+
+ replay();
+
+ BindingFactory factory = new ValidatorBindingFactory(source);
+
+ try
+ {
+ factory.newBinding("descrip", container, component, "zip,zoom", l);
+ }
+ catch (TapestryException ex)
+ {
+ assertEquals(
+ ex.getMessage(),
+ "Component 'foo.Bar:baz' is not a field (it does not implement the Field interface) and may not be used with the validator: binding prefix.");
+ assertSame(ex.getLocation(), l);
+ }
+
+ verify();
+ }
+
+ @Test
+ public void success()
+ {
+
+ FieldValidatorSource source = newFieldValidatorSource();
+ ComponentResources container = newComponentResources();
+ ComponentResources component = newComponentResources();
+ FieldComponent instance = newMock(FieldComponent.class);
+ Location l = newLocation();
+ FieldValidator validator = newFieldValidator();
+
+ String expression = "required,minLength=5";
+
+ train_getComponent(component, instance);
+
+ expect(source.createValidators(instance, expression)).andReturn(validator);
+
+ replay();
+
+ BindingFactory factory = new ValidatorBindingFactory(source);
+
+ Binding binding = factory.newBinding("descrip", container, component, expression, l);
+
+ assertSame(binding.get(), validator);
+
+ verify();
+ }
+}