Revision: 662
http://stripes.svn.sourceforge.net/stripes/?rev=662&view=rev
Author: bengunter
Date: 2007-12-11 09:22:44 -0800 (Tue, 11 Dec 2007)
Log Message:
-----------
Fixed STS-260: Expose validation metadata via a public interface. Validation
metadata is now globally accessible through the ValidationMetadataProvider
interface, an instance of which can be obtained from Configuration.
Modified Paths:
--------------
trunk/stripes/src/net/sourceforge/stripes/config/Configuration.java
trunk/stripes/src/net/sourceforge/stripes/config/DefaultConfiguration.java
trunk/stripes/src/net/sourceforge/stripes/config/RuntimeConfiguration.java
trunk/stripes/src/net/sourceforge/stripes/controller/BindingPolicyManager.java
trunk/stripes/src/net/sourceforge/stripes/controller/DefaultActionBeanPropertyBinder.java
Added Paths:
-----------
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultValidationMetadataProvider.java
trunk/stripes/src/net/sourceforge/stripes/validation/ValidationMetadataProvider.java
Modified: trunk/stripes/src/net/sourceforge/stripes/config/Configuration.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/config/Configuration.java
2007-12-11 06:23:42 UTC (rev 661)
+++ trunk/stripes/src/net/sourceforge/stripes/config/Configuration.java
2007-12-11 17:22:44 UTC (rev 662)
@@ -20,6 +20,7 @@
import net.sourceforge.stripes.localization.LocalizationBundleFactory;
import net.sourceforge.stripes.localization.LocalePicker;
import net.sourceforge.stripes.validation.TypeConverterFactory;
+import net.sourceforge.stripes.validation.ValidationMetadataProvider;
import net.sourceforge.stripes.tag.TagErrorRendererFactory;
import net.sourceforge.stripes.tag.PopulationStrategy;
import net.sourceforge.stripes.format.FormatterFactory;
@@ -191,4 +192,13 @@
* @return MultipartWrapperFactory an instance of the wrapper factory
*/
MultipartWrapperFactory getMultipartWrapperFactory();
+
+ /**
+ * Returns an instance of [EMAIL PROTECTED] ValidationMetadataProvider}
that can be used by Stripes to
+ * determine what validations need to be applied during
+ * [EMAIL PROTECTED] LifecycleStage#BindingAndValidation}.
+ *
+ * @return an instance of [EMAIL PROTECTED] ValidationMetadataProvider}
+ */
+ ValidationMetadataProvider getValidationMetadataProvider();
}
Modified:
trunk/stripes/src/net/sourceforge/stripes/config/DefaultConfiguration.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/config/DefaultConfiguration.java
2007-12-11 06:23:42 UTC (rev 661)
+++ trunk/stripes/src/net/sourceforge/stripes/config/DefaultConfiguration.java
2007-12-11 17:22:44 UTC (rev 662)
@@ -50,7 +50,9 @@
import net.sourceforge.stripes.tag.TagErrorRendererFactory;
import net.sourceforge.stripes.util.Log;
import net.sourceforge.stripes.validation.DefaultTypeConverterFactory;
+import net.sourceforge.stripes.validation.DefaultValidationMetadataProvider;
import net.sourceforge.stripes.validation.TypeConverterFactory;
+import net.sourceforge.stripes.validation.ValidationMetadataProvider;
/**
* <p>Centralized location for defaults for all Configuration properties.
This implementation does
@@ -90,6 +92,7 @@
private Map<LifecycleStage,Collection<Interceptor>> interceptors;
private ExceptionHandler exceptionHandler;
private MultipartWrapperFactory multipartWrapperFactory;
+ private ValidationMetadataProvider validationMetadataProvider;
/** Gratefully accepts the BootstrapPropertyResolver handed to the
Configuration. */
public void setBootstrapPropertyResolver(BootstrapPropertyResolver
resolver) {
@@ -176,6 +179,12 @@
this.multipartWrapperFactory.init(this);
}
+ this.validationMetadataProvider = initValidationMetadataProvider();
+ if (this.validationMetadataProvider == null) {
+ this.validationMetadataProvider = new
DefaultValidationMetadataProvider();
+ this.validationMetadataProvider.init(this);
+ }
+
this.interceptors = new HashMap<LifecycleStage,
Collection<Interceptor>>();
Map<LifecycleStage, Collection<Interceptor>> map =
initCoreInterceptors();
if (map != null) {
@@ -347,6 +356,20 @@
protected MultipartWrapperFactory initMultipartWrapperFactory() { return
null; }
/**
+ * Returns an instance of [EMAIL PROTECTED] ValidationMetadataProvider}
that can be used by Stripes to
+ * determine what validations need to be applied during
+ * [EMAIL PROTECTED] LifecycleStage#BindingAndValidation}.
+ *
+ * @return an instance of [EMAIL PROTECTED] ValidationMetadataProvider}
+ */
+ public ValidationMetadataProvider getValidationMetadataProvider() {
+ return this.validationMetadataProvider;
+ }
+
+ /** Allows subclasses to initialize a non-default [EMAIL PROTECTED]
ValidationMetadataProvider}. */
+ protected ValidationMetadataProvider initValidationMetadataProvider() {
return null; }
+
+ /**
* Returns a list of interceptors that should be executed around the
lifecycle stage
* indicated. By default returns a single element list containing the
* [EMAIL PROTECTED] BeforeAfterMethodInterceptor}.
Modified:
trunk/stripes/src/net/sourceforge/stripes/config/RuntimeConfiguration.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/config/RuntimeConfiguration.java
2007-12-11 06:23:42 UTC (rev 661)
+++ trunk/stripes/src/net/sourceforge/stripes/config/RuntimeConfiguration.java
2007-12-11 17:22:44 UTC (rev 662)
@@ -35,6 +35,7 @@
import net.sourceforge.stripes.util.ReflectUtil;
import net.sourceforge.stripes.util.StringUtil;
import net.sourceforge.stripes.validation.TypeConverterFactory;
+import net.sourceforge.stripes.validation.ValidationMetadataProvider;
/**
* <p>Configuration class that uses the BootstrapPropertyResolver to look for
configuration values,
@@ -90,7 +91,10 @@
/** The Configuration Key for looking up the name of the
MultipartWrapperFactory class */
public static final String MULTIPART_WRAPPER_FACTORY =
"MultipartWrapperFactory.Class";
-
+
+ /** The Configuration Key for looking up the name of the
ValidationMetadataProvider class */
+ public static final String VALIDATION_METADATA_PROVIDER =
"ValidationMetadataProvider.Class";
+
/** The Configuration Key for looking up the comma separated list of core
interceptor classes. */
public static final String CORE_INTERCEPTOR_LIST =
"CoreInterceptor.Classes";
@@ -163,6 +167,11 @@
return initializeComponent(MultipartWrapperFactory.class,
MULTIPART_WRAPPER_FACTORY);
}
+ /** Looks for a class name in config and uses that to create the
component. */
+ @Override protected ValidationMetadataProvider
initValidationMetadataProvider() {
+ return initializeComponent(ValidationMetadataProvider.class,
VALIDATION_METADATA_PROVIDER);
+ }
+
/**
* Looks for a list of class names separated by commas under the
configuration key
* [EMAIL PROTECTED] #CORE_INTERCEPTOR_LIST}. White space surrounding the
class names is trimmed,
Modified:
trunk/stripes/src/net/sourceforge/stripes/controller/BindingPolicyManager.java
===================================================================
---
trunk/stripes/src/net/sourceforge/stripes/controller/BindingPolicyManager.java
2007-12-11 06:23:42 UTC (rev 661)
+++
trunk/stripes/src/net/sourceforge/stripes/controller/BindingPolicyManager.java
2007-12-11 17:22:44 UTC (rev 662)
@@ -14,27 +14,25 @@
*/
package net.sourceforge.stripes.controller;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.StrictBinding;
import net.sourceforge.stripes.action.StrictBinding.Policy;
import net.sourceforge.stripes.exception.StripesRuntimeException;
import net.sourceforge.stripes.util.Log;
import net.sourceforge.stripes.util.bean.PropertyExpressionEvaluation;
-import net.sourceforge.stripes.validation.Validate;
-import net.sourceforge.stripes.validation.ValidateNestedProperties;
+import net.sourceforge.stripes.validation.ValidationMetadata;
+import net.sourceforge.stripes.validation.ValidationMetadataProvider;
/**
* Manages the policies observed by [EMAIL PROTECTED]
DefaultActionBeanPropertyBinder} when binding properties
@@ -181,96 +179,20 @@
}
/**
- * Get all the properties and nested properties of the given class to
which a [EMAIL PROTECTED] Validate}
- * annotation is applied. The annotation may be applied to the property's
read method, write
- * method, or field declaration. If a property has a [EMAIL PROTECTED]
ValidateNestedProperties}
- * annotation, then the nested properties named in its [EMAIL PROTECTED]
Validate} annotations will be
- * included as well. However, the [EMAIL PROTECTED]
ValidateNestedProperties} annotation alone is not
- * sufficient to include a property; it must have its own [EMAIL
PROTECTED] Validate} annotation.
+ * Get all the properties and nested properties of the given class for
which there is a
+ * corresponding [EMAIL PROTECTED] ValidationMetadata}, as returned by
+ * [EMAIL PROTECTED]
ValidationMetadataProvider#getValidationMetadata(Class, String)}. The idea here
is
+ * that if the bean property must be validated, then it is expected that
the property may be
+ * bound to the bean.
*
* @param beanClass a class
* @return The validated properties. If no properties are annotated then
null.
+ * @see ValidationMetadataProvider#getValidationMetadata(Class)
*/
protected String[] getValidatedProperties(Class<?> beanClass) {
- // for the sake of pretty code, i declare these first
- Method method;
- Validate simple;
- ValidateNestedProperties nested;
- List<Validate> simples = new ArrayList<Validate>();
- List<ValidateNestedProperties> nesteds = new
ArrayList<ValidateNestedProperties>();
- List<String> properties = new ArrayList<String>();
- try {
- PropertyDescriptor[] pds =
Introspector.getBeanInfo(beanClass).getPropertyDescriptors();
- for (PropertyDescriptor pd : pds) {
- String propertyName = pd.getName();
- simples.clear();
- nesteds.clear();
-
- // check getter method
- method = pd.getReadMethod();
- if (method != null) {
- simple = method.getAnnotation(Validate.class);
- nested =
method.getAnnotation(ValidateNestedProperties.class);
- if (simple != null)
- simples.add(simple);
- if (nested != null)
- nesteds.add(nested);
- }
-
- // check setter method
- method = pd.getWriteMethod();
- if (method != null) {
- simple = method.getAnnotation(Validate.class);
- nested =
method.getAnnotation(ValidateNestedProperties.class);
- if (simple != null)
- simples.add(simple);
- if (nested != null)
- nesteds.add(nested);
- }
-
- // check the field (possibly declared in a superclass) with
the same
- // name as the property for annotations
- Field field = null;
- for (Class<?> c = beanClass; c != null && field == null; c =
c.getSuperclass()) {
- try {
- field = c.getDeclaredField(propertyName);
- }
- catch (NoSuchFieldException e) {
- }
- }
- if (field != null) {
- simple = field.getAnnotation(Validate.class);
- nested =
field.getAnnotation(ValidateNestedProperties.class);
- if (simple != null)
- simples.add(simple);
- if (nested != null)
- nesteds.add(nested);
- }
-
- // add to allow list if @Validate present
- if (!simples.isEmpty())
- properties.add(propertyName);
-
- // add all sub-properties referenced in
@ValidateNestedProperties
- for (ValidateNestedProperties vnp : nesteds) {
- Validate[] validates = vnp.value();
- if (validates != null) {
- for (Validate validate : validates) {
- if (validate.field() != null) {
- properties.add(propertyName + '.' +
validate.field());
- }
- }
- }
- }
- }
- }
- catch (Exception e) {
- log.error(e, "%%% Failure checking @Validate annotations ",
getClass().getName());
- StripesRuntimeException sre = new
StripesRuntimeException(e.getMessage(), e);
- sre.setStackTrace(e.getStackTrace());
- throw sre;
- }
- return properties.size() == 0 ? null : properties.toArray(new
String[properties.size()]);
+ Set<String> properties =
StripesFilter.getConfiguration().getValidationMetadataProvider()
+ .getValidationMetadata(beanClass).keySet();
+ return new ArrayList<String>(properties).toArray(new
String[properties.size()]);
}
/**
Modified:
trunk/stripes/src/net/sourceforge/stripes/controller/DefaultActionBeanPropertyBinder.java
===================================================================
---
trunk/stripes/src/net/sourceforge/stripes/controller/DefaultActionBeanPropertyBinder.java
2007-12-11 06:23:42 UTC (rev 661)
+++
trunk/stripes/src/net/sourceforge/stripes/controller/DefaultActionBeanPropertyBinder.java
2007-12-11 17:22:44 UTC (rev 662)
@@ -53,9 +53,6 @@
public class DefaultActionBeanPropertyBinder implements
ActionBeanPropertyBinder {
private static final Log log =
Log.getInstance(DefaultActionBeanPropertyBinder.class);
- /** Map of validation annotations that is built at startup. */
- private Map<Class<? extends ActionBean>, Map<String, ValidationMetadata>>
validations;
-
/** Configuration instance passed in at initialization time. */
private Configuration configuration;
@@ -65,96 +62,9 @@
*/
public void init(Configuration configuration) throws Exception {
this.configuration = configuration;
- Set<Class<? extends ActionBean>> beanClasses =
ActionClassCache.getInstance()
- .getActionBeanClasses();
- this.validations = new HashMap<Class<? extends ActionBean>,
Map<String, ValidationMetadata>>();
-
- for (Class<? extends ActionBean> beanClass : beanClasses) {
- Map<String, ValidationMetadata> fieldValidations = new
HashMap<String, ValidationMetadata>();
- processClassAnnotations(beanClass, fieldValidations);
- this.validations.put(beanClass, fieldValidations);
-
- // Print out a pretty debug message showing what validations got
configured
- StringBuilder builder = new StringBuilder(128);
- for (Map.Entry<String, ValidationMetadata> entry :
fieldValidations.entrySet()) {
- if (builder.length() > 0) {
- builder.append(", ");
- }
- builder.append(entry.getKey());
- builder.append("->");
- builder.append(entry.getValue());
- }
-
- log.debug("Loaded validations for ActionBean ",
beanClass.getSimpleName(), ": ",
- builder.length() > 0 ? builder : "<none>");
- }
}
/**
- * Helper method that processes a class looking for validation
annotations. Will recurse and
- * process the superclasses first in order to ensure that annotations
lower down the inheritance
- * hierarchy take precedence over those higher up.
- *
- * @param clazz the ActionBean subclasses (or parent thereof) in question
- * @param fieldValidations a map of fieldname->ValidationMetadata in which
to store validations
- */
- protected void processClassAnnotations(Class<?> clazz,
- Map<String, ValidationMetadata> fieldValidations) {
- Class<?> superclass = clazz.getSuperclass();
- if (superclass != null) {
- processClassAnnotations(superclass, fieldValidations);
- }
-
- // Process the methods on the class
- Method[] methods = clazz.getDeclaredMethods();
- for (Method method : methods) {
- if (!Modifier.isPublic(method.getModifiers())) {
- continue; // only public methods!
- }
-
- processPropertyAnnotations(getPropertyName(method.getName()),
method
- .getAnnotation(Validate.class), method
- .getAnnotation(ValidateNestedProperties.class),
fieldValidations);
- }
-
- // Process the fields for validation annotations
- Field[] fields = clazz.getDeclaredFields();
- for (Field field : fields) {
- processPropertyAnnotations(field.getName(),
field.getAnnotation(Validate.class), field
- .getAnnotation(ValidateNestedProperties.class),
fieldValidations);
- }
- }
-
- /**
- * Helper method that takes the name of a property on the ActionBean along
with the property's
- * (potentially null) Validate and ValidateNestedProperties annotations
and stores the
- * information in to the internval cache of validation information.
- */
- protected void processPropertyAnnotations(String name, Validate validate,
- ValidateNestedProperties nested, Map<String, ValidationMetadata>
fieldValidations) {
- if (validate != null) {
- fieldValidations.put(name, new ValidationMetadata(name, validate));
- }
-
- if (nested != null) {
- Validate[] validations = nested.value();
- for (Validate nestedValidate : validations) {
- if ("".equals(nestedValidate.field())) {
- log.warn("Nested validation on field ", name,
- " used without nested field name: ", ReflectUtil
- .toString(nestedValidate));
- }
- else {
- String nestedName = name + "." + nestedValidate.field();
- fieldValidations.put(nestedName, new
ValidationMetadata(nestedName,
- nestedValidate));
- }
- }
- }
-
- }
-
- /**
* <p>
* Loops through the parameters contained in the request and attempts to
bind each one to the
* supplied ActionBean. Invokes validation for each of the properties on
the bean before binding
@@ -174,6 +84,8 @@
*/
public ValidationErrors bind(ActionBean bean, ActionBeanContext context,
boolean validate) {
ValidationErrors fieldErrors = context.getValidationErrors();
+ Map<String, ValidationMetadata> validationInfos = this.configuration
+
.getValidationMetadataProvider().getValidationMetadata(bean.getClass());
// Take the ParameterMap and turn the keys into ParameterNames
Map<ParameterName, String[]> parameters = getParameters(context);
@@ -210,8 +122,7 @@
log.trace("Running binding for property with name: ",
name);
// Determine the target type
- ValidationMetadata validationInfo =
this.validations.get(bean.getClass()).get(
- name.getStrippedName());
+ ValidationMetadata validationInfo =
validationInfos.get(name.getStrippedName());
PropertyExpressionEvaluation eval = new
PropertyExpressionEvaluation(
PropertyExpression.getExpression(pname), bean);
Class<?> type = eval.getType();
@@ -520,24 +431,6 @@
}
/**
- * Helper method that returns the name of the property when supplied with
the corresponding get
- * or set method. Does not do anything particularly intelligent, just
drops the first three
- * characters and makes the next character lower case.
- */
- protected String getPropertyName(String methodName) {
- if (methodName.length() > 3
- && (methodName.startsWith("get") ||
methodName.startsWith("set"))) {
- return methodName.substring(3, 4).toLowerCase() +
methodName.substring(4);
- }
- else if (methodName.length() > 2 && methodName.startsWith("is")) {
- return methodName.substring(2, 3).toLowerCase() +
methodName.substring(3);
- }
- else {
- return "";
- }
- }
-
- /**
* Validates that all required fields have been submitted. This is done by
looping through the
* set of validation annotations and checking that each field marked as
required was submitted
* in the request and submitted with a non-empty value.
@@ -556,7 +449,8 @@
}
}
- Map<String, ValidationMetadata> validationInfos =
this.validations.get(bean.getClass());
+ Map<String, ValidationMetadata> validationInfos = this.configuration
+
.getValidationMetadataProvider().getValidationMetadata(bean.getClass());
StripesRequestWrapper req =
StripesRequestWrapper.findStripesWrapper(bean.getContext()
.getRequest());
@@ -738,12 +632,13 @@
protected void doPostConversionValidations(ActionBean bean,
Map<ParameterName, List<Object>> convertedValues, ValidationErrors
errors) {
+ Map<String, ValidationMetadata> validationInfos = this.configuration
+
.getValidationMetadataProvider().getValidationMetadata(bean.getClass());
for (Map.Entry<ParameterName, List<Object>> entry :
convertedValues.entrySet()) {
// Sort out what we need to validate this field
ParameterName name = entry.getKey();
List<Object> values = entry.getValue();
- ValidationMetadata validationInfo =
this.validations.get(bean.getClass()).get(
- name.getStrippedName());
+ ValidationMetadata validationInfo =
validationInfos.get(name.getStrippedName());
if (values.size() == 0 || validationInfo == null) {
continue;
@@ -875,7 +770,7 @@
* not guaranteed to be the same length as the values array passed
in.
*/
@SuppressWarnings("unchecked")
- private List<Object> convert(ActionBean bean, ParameterName propertyName,
String[] values,
+ protected List<Object> convert(ActionBean bean, ParameterName
propertyName, String[] values,
Class propertyType, ValidationMetadata validationInfo,
List<ValidationError> errors)
throws Exception {
Added:
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultValidationMetadataProvider.java
===================================================================
---
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultValidationMetadataProvider.java
(rev 0)
+++
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultValidationMetadataProvider.java
2007-12-11 17:22:44 UTC (rev 662)
@@ -0,0 +1,177 @@
+/* Copyright 2007 Ben Gunter
+ *
+ * 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 net.sourceforge.stripes.validation;
+
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sourceforge.stripes.action.ActionBean;
+import net.sourceforge.stripes.config.Configuration;
+import net.sourceforge.stripes.exception.StripesRuntimeException;
+import net.sourceforge.stripes.util.Log;
+
+/**
+ * Provides a globally accessible cache of validation metadata for properties
of [EMAIL PROTECTED] ActionBean}
+ * classes.
+ *
+ * @author Ben Gunter
+ * @since Stripes 1.5
+ */
+public class DefaultValidationMetadataProvider implements
ValidationMetadataProvider {
+ private static final Log log =
Log.getInstance(DefaultValidationMetadataProvider.class);
+ private Configuration configuration;
+
+ /** Map class -> field -> validation meta data */
+ private final Map<Class<?>, Map<String, ValidationMetadata>> cache = new
HashMap<Class<?>, Map<String, ValidationMetadata>>();
+
+ /** Currently does nothing except store a reference to [EMAIL PROTECTED]
configuration}. */
+ public void init(Configuration configuration) throws Exception {
+ this.configuration = configuration;
+ }
+
+ /** Get the [EMAIL PROTECTED] Configuration} object that was passed into
[EMAIL PROTECTED] #init(Configuration)}. */
+ public Configuration getConfiguration() {
+ return configuration;
+ }
+
+ public Map<String, ValidationMetadata> getValidationMetadata(Class<?>
beanType) {
+ Map<String, ValidationMetadata> meta = cache.get(beanType);
+ if (meta == null) {
+ meta = loadForClass(beanType);
+ cache.put(beanType, meta);
+ }
+
+ return meta;
+ }
+
+ public ValidationMetadata getValidationMetadata(Class<?> beanType, String
field) {
+ return getValidationMetadata(beanType).get(field);
+ }
+
+ /**
+ * Get validation information for all the properties and nested properties
of the given class.
+ * The [EMAIL PROTECTED] Validate} and/or [EMAIL PROTECTED]
ValidateNestedProperties} annotations may be applied to
+ * the property's read method, write method, or field declaration. If a
property has a
+ * [EMAIL PROTECTED] ValidateNestedProperties} annotation, then the nested
properties named in its
+ * [EMAIL PROTECTED] Validate} annotations will be included as well.
+ *
+ * @param clazz a class
+ * @return A map of (possibly nested) property names to [EMAIL PROTECTED]
ValidationMetadata} for the
+ * property.
+ */
+ protected Map<String, ValidationMetadata> loadForClass(Class<?> clazz) {
+ Map<String, ValidationMetadata> meta = new HashMap<String,
ValidationMetadata>();
+ try {
+ PropertyDescriptor[] pds =
Introspector.getBeanInfo(clazz).getPropertyDescriptors();
+ for (PropertyDescriptor pd : pds) {
+ String propertyName = pd.getName();
+ Validate simple = null;
+ ValidateNestedProperties nested = null;
+
+ // check getter method
+ Method method = pd.getReadMethod();
+ if (method != null &&
Modifier.isPublic(method.getModifiers())) {
+ simple = method.getAnnotation(Validate.class);
+ nested =
method.getAnnotation(ValidateNestedProperties.class);
+ }
+
+ // check setter method
+ method = pd.getWriteMethod();
+ if (method != null &&
Modifier.isPublic(method.getModifiers())) {
+ if (simple == null)
+ simple = method.getAnnotation(Validate.class);
+ if (nested == null)
+ nested =
method.getAnnotation(ValidateNestedProperties.class);
+ }
+
+ // check the field (possibly declared in a superclass) with
the same
+ // name as the property for annotations
+ Field field = null;
+ for (Class<?> c = clazz; c != null && field == null; c =
c.getSuperclass()) {
+ try {
+ field = c.getDeclaredField(propertyName);
+ }
+ catch (NoSuchFieldException e) {
+ }
+ }
+ if (field != null) {
+ if (simple == null)
+ simple = field.getAnnotation(Validate.class);
+ if (nested == null)
+ nested =
field.getAnnotation(ValidateNestedProperties.class);
+ }
+
+ // add to allow list if @Validate present
+ if (simple != null) {
+ if (simple.field() != null) {
+ meta.put(propertyName, new
ValidationMetadata(propertyName, simple));
+ }
+ else {
+ log.warn("Field name present in @Validate but should
be omitted: ", clazz,
+ ", property ", propertyName, ", given field
name ", simple.field());
+ }
+ }
+
+ // add all sub-properties referenced in
@ValidateNestedProperties
+ if (nested != null) {
+ Validate[] validates = nested.value();
+ if (validates != null) {
+ for (Validate validate : validates) {
+ if (validate.field() != null) {
+ String fullName = propertyName + '.' +
validate.field();
+ meta.put(fullName, new
ValidationMetadata(fullName, validate));
+ }
+ else {
+ log.warn("Field name missing from nested
@Validate: ", clazz,
+ ", property ", propertyName);
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (RuntimeException e) {
+ log.error(e, "Failure checking @Validate annotations ",
getClass().getName());
+ throw e;
+ }
+ catch (Exception e) {
+ log.error(e, "Failure checking @Validate annotations ",
getClass().getName());
+ StripesRuntimeException sre = new
StripesRuntimeException(e.getMessage(), e);
+ sre.setStackTrace(e.getStackTrace());
+ throw sre;
+ }
+
+ // Print out a pretty debug message showing what validations got
configured
+ StringBuilder builder = new StringBuilder(128);
+ for (Map.Entry<String, ValidationMetadata> entry : meta.entrySet()) {
+ if (builder.length() > 0) {
+ builder.append(", ");
+ }
+ builder.append(entry.getKey());
+ builder.append("->");
+ builder.append(entry.getValue());
+ }
+ log.debug("Loaded validations for ActionBean ", clazz.getSimpleName(),
": ", builder
+ .length() > 0 ? builder : "<none>");
+
+ return Collections.unmodifiableMap(meta);
+ }
+}
Added:
trunk/stripes/src/net/sourceforge/stripes/validation/ValidationMetadataProvider.java
===================================================================
---
trunk/stripes/src/net/sourceforge/stripes/validation/ValidationMetadataProvider.java
(rev 0)
+++
trunk/stripes/src/net/sourceforge/stripes/validation/ValidationMetadataProvider.java
2007-12-11 17:22:44 UTC (rev 662)
@@ -0,0 +1,49 @@
+/* Copyright 2007 Ben Gunter
+ *
+ * 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 net.sourceforge.stripes.validation;
+
+import java.util.Map;
+
+import net.sourceforge.stripes.action.ActionBean;
+import net.sourceforge.stripes.config.ConfigurableComponent;
+
+/**
+ *
+ *
+ * @author Ben Gunter
+ * @since Stripes 1.5
+ */
+public interface ValidationMetadataProvider extends ConfigurableComponent {
+ /**
+ * Get a map of property names to [EMAIL PROTECTED] ValidationMetadata}
for the given [EMAIL PROTECTED] ActionBean}
+ * class.
+ *
+ * @param beanType any class
+ * @return A map of property names to [EMAIL PROTECTED]
ValidationMetadata}. If no validation information
+ * is present for the given class, then an empty map will be
returned.
+ */
+ Map<String, ValidationMetadata> getValidationMetadata(Class<?> beanType);
+
+ /**
+ * Get the validation metadata associated with the named [EMAIL PROTECTED]
property} of the given
+ * [EMAIL PROTECTED] ActionBean} class.
+ *
+ * @param beanType any class
+ * @param property a (possibly nested) property of [EMAIL PROTECTED]
beanType}
+ * @return A [EMAIL PROTECTED] ValidationMetadata} object, if there is one
associated with the property. If
+ * the property is not to be validated, then null.
+ */
+ ValidationMetadata getValidationMetadata(Class<?> beanType, String
property);
+}
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
SF.Net email is sponsored by:
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development