Browse Source

Handle multiple Validators.support()ing a given class.

Ben Alex 20 years ago
parent
commit
c1d156e363

+ 56 - 5
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;
  * <p>
  * Locates <code>Validator</code>s registered in bean factory.
  * </p>
+ * 
+ * <p>If more than one <code>Validator</code> can support a given object, the
+ * supporting <code>Validator</code>s will be iterated and their bean factory
+ * defined bean names will be used to attempt to select the "best matching"
+ * <code>Validator</code>. The lowercase version of a given object's simplified
+ * class name will be searched within the bean names. If more than one
+ * <code>Validator</code> contains this search criteria, an exception will be
+ * thrown as the actual intended <code>Validator</code> is unidentifiable.
+ * 
+ * <p>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 <code>ValidationRegistryManagerImpl</code>
+ * is asked to return the <code>Validator</code> for Person, it will locate
+ * the two matching <code>Validator</code>s 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").
+ * <code>ValidationRegistryManagerImpl</code> 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<String,Validator> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, Validator.class, true, true);
+		
+		// Search all Validators for those that support the class
+		Set<String> candidateValidatorNames = new HashSet<String>();
 		Iterator<String> 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<String> 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) {