From c1d156e36355ff58d5616798d82c36c899ea0ae7 Mon Sep 17 00:00:00 2001 From: Ben Alex Date: Sat, 14 May 2005 05:04:07 +0000 Subject: [PATCH] Handle multiple Validators.support()ing a given class. --- .../ValidationRegistryManagerImpl.java | 61 +++++++++++++++++-- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/domain/src/main/java/org/acegisecurity/domain/validation/ValidationRegistryManagerImpl.java b/domain/src/main/java/org/acegisecurity/domain/validation/ValidationRegistryManagerImpl.java index 317f6e2e6d..0aca977d8a 100644 --- a/domain/src/main/java/org/acegisecurity/domain/validation/ValidationRegistryManagerImpl.java +++ b/domain/src/main/java/org/acegisecurity/domain/validation/ValidationRegistryManagerImpl.java @@ -16,8 +16,10 @@ package net.sf.acegisecurity.domain.validation; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.Map; +import java.util.Set; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; @@ -34,6 +36,29 @@ import org.springframework.validation.Validator; *

* Locates Validators registered in bean factory. *

+ * + *

If more than one Validator can support a given object, the + * supporting Validators will be iterated and their bean factory + * defined bean names will be used to attempt to select the "best matching" + * Validator. The lowercase version of a given object's simplified + * class name will be searched within the bean names. If more than one + * Validator contains this search criteria, an exception will be + * thrown as the actual intended Validator is unidentifiable. + * + *

For example, say you had a PartyValidator which could validate + * com.foo.Party, and also its subclass, com.foo.Person. There is also a + * PersonValidator which can only validate Person. PartyValidator and + * PersonValidator are registered in the bean container as "partyValidator" + * and "personValidator". When ValidationRegistryManagerImpl + * is asked to return the Validator for Person, it will locate + * the two matching Validators in the bean container. As there + * are two matching, it will look at the lowercase representation of the + * bean names and see if either contain the lower simplified class name of + * the object being search for (com.foo.Person thus becomes simply "person"). + * ValidationRegistryManagerImpl will then correctly return the + * PersonValidator for Person. If the PartyValidator had been registered with + * an ambiguous bean name of say "personAndPartyValidator", both bean names + * would have matched and an exception would have been thrown. * * @author Matthew E. Porter * @author Ben Alex @@ -69,19 +94,45 @@ public class ValidationRegistryManagerImpl implements ValidationRegistryManager, // Attempt to find Validator via introspection Map beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, Validator.class, true, true); + + // Search all Validators for those that support the class + Set candidateValidatorNames = new HashSet(); Iterator iter = beans.keySet().iterator(); while (iter.hasNext()) { String beanName = iter.next(); Validator validator = beans.get(beanName); if (validator.supports(domainClass)) { - this.validatorMap.put(domainClass, beanName); - return validator; + candidateValidatorNames.add(beanName); } } - // No Validator found - this.validatorMap.put(domainClass, null); - return null; + if (candidateValidatorNames.size() == 0) { + // No Validator found + this.validatorMap.put(domainClass, null); + return null; + } else if (candidateValidatorNames.size() == 1) { + // Only one Validator found, so return it + String validator = candidateValidatorNames.iterator().next(); + this.validatorMap.put(domainClass, validator); + return beans.get(validator); + } else { + // Try to locate an entry with simple class name in it + Iterator iterCandidates = candidateValidatorNames.iterator(); + String lastFound = null; + int numberFound = 0; + while (iterCandidates.hasNext()) { + String candidate = iterCandidates.next(); + if (candidate.toLowerCase().contains(domainClass.getSimpleName().toLowerCase())) { + numberFound++; + lastFound = candidate; + } + } + if (numberFound != 1) { + throw new IllegalArgumentException("More than one Validator found that supports '" + domainClass + "' and cannot determine most specific Validator to use; give a hint by making bean name include the simple name of the target class"); + } + this.validatorMap.put(domainClass, lastFound); + return beans.get(lastFound); + } } public void registerValidator(Class domainClass, String beanName) {