1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.acegisecurity.domain.validation;
17
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.Map;
22 import java.util.Set;
23
24 import org.springframework.beans.BeansException;
25 import org.springframework.beans.factory.BeanFactory;
26 import org.springframework.beans.factory.BeanFactoryAware;
27 import org.springframework.beans.factory.BeanFactoryUtils;
28 import org.springframework.beans.factory.ListableBeanFactory;
29 import org.springframework.util.Assert;
30 import org.springframework.validation.Validator;
31
32
33 /***
34 * A basic implementation of {@link ValidationRegistryManager}.
35 *
36 * <p>
37 * Locates <code>Validator</code>s registered in bean factory.
38 * </p>
39 *
40 * <p>If more than one <code>Validator</code> can support a given object, the
41 * supporting <code>Validator</code>s will be iterated and their bean factory
42 * defined bean names will be used to attempt to select the "best matching"
43 * <code>Validator</code>. The lowercase version of a given object's simplified
44 * class name will be searched within the bean names. If more than one
45 * <code>Validator</code> contains this search criteria, an exception will be
46 * thrown as the actual intended <code>Validator</code> is unidentifiable.
47 *
48 * <p>For example, say you had a PartyValidator which could validate
49 * com.foo.Party, and also its subclass, com.foo.Person. There is also a
50 * PersonValidator which can only validate Person. PartyValidator and
51 * PersonValidator are registered in the bean container as "partyValidator"
52 * and "personValidator". When <code>ValidationRegistryManagerImpl</code>
53 * is asked to return the <code>Validator</code> for Person, it will locate
54 * the two matching <code>Validator</code>s in the bean container. As there
55 * are two matching, it will look at the lowercase representation of the
56 * bean names and see if either contain the lower simplified class name of
57 * the object being search for (com.foo.Person thus becomes simply "person").
58 * <code>ValidationRegistryManagerImpl</code> will then correctly return the
59 * PersonValidator for Person. If the PartyValidator had been registered with
60 * an ambiguous bean name of say "personAndPartyValidator", both bean names
61 * would have matched and an exception would have been thrown.
62 *
63 * @author Matthew E. Porter
64 * @author Ben Alex
65 * @version $Id: ValidationRegistryManagerImpl.java,v 1.7 2005/11/17 00:55:50 benalex Exp $
66 */
67 public class ValidationRegistryManagerImpl implements ValidationRegistryManager,
68 BeanFactoryAware {
69
70
71
72
73 private ListableBeanFactory bf;
74 private Map<Class,String> validatorMap = new HashMap<Class,String>();
75
76
77
78 public void setBeanFactory(BeanFactory beanFactory)
79 throws BeansException {
80 Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
81 "BeanFactory must be ListableBeanFactory");
82 this.bf = (ListableBeanFactory) beanFactory;
83 }
84
85 public Validator findValidator(Class domainClass) {
86 Assert.notNull(domainClass, "domainClass cannot be null");
87
88 if (validatorMap.containsKey(domainClass)) {
89 if (validatorMap.get(domainClass) == null) {
90 return null;
91 }
92 return (Validator) this.bf.getBean((String)validatorMap.get(domainClass), Validator.class);
93 }
94
95
96 Map<String,Validator> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, Validator.class, true, true);
97
98
99 Set<String> candidateValidatorNames = new HashSet<String>();
100 Iterator<String> iter = beans.keySet().iterator();
101 while (iter.hasNext()) {
102 String beanName = iter.next();
103 Validator validator = beans.get(beanName);
104 if (validator.supports(domainClass)) {
105 candidateValidatorNames.add(beanName);
106 }
107 }
108
109 if (candidateValidatorNames.size() == 0) {
110
111 this.validatorMap.put(domainClass, null);
112 return null;
113 } else if (candidateValidatorNames.size() == 1) {
114
115 String validator = candidateValidatorNames.iterator().next();
116 this.validatorMap.put(domainClass, validator);
117 return beans.get(validator);
118 } else {
119
120 StringBuffer sb = new StringBuffer();
121 Iterator<String> iterCandidates = candidateValidatorNames.iterator();
122 String lastFound = null;
123 int numberFound = 0;
124 while (iterCandidates.hasNext()) {
125 String candidate = iterCandidates.next();
126 sb.append(candidate);
127 if (iterCandidates.hasNext()) {
128 sb.append(",");
129 }
130 if (candidate.toLowerCase().contains(domainClass.getSimpleName().toLowerCase())) {
131 numberFound++;
132 lastFound = candidate;
133 }
134 }
135 if (numberFound != 1) {
136 throw new IllegalArgumentException("More than one Validator found (" + sb.toString() + ") that supports '" + domainClass + "', but cannot determine the most specific Validator to use; give a hint by making bean name include the simple name of the target class ('" + domainClass.getSimpleName().toString() + "')");
137 }
138 this.validatorMap.put(domainClass, lastFound);
139 return beans.get(lastFound);
140 }
141 }
142
143 public void registerValidator(Class domainClass, String beanName) {
144 Assert.notNull(domainClass, "domainClass cannot be null");
145 Assert.notNull(beanName, "beanName cannot be null");
146 Assert.isTrue(this.bf.containsBean(beanName), "beanName not found in context");
147 Assert.isInstanceOf(Validator.class, this.bf.getBean(beanName), "beanName '" + beanName + "' must be a Validator");
148 Assert.isTrue(((Validator)this.bf.getBean(beanName)).supports(domainClass), "Validator does not support " + domainClass);
149 this.validatorMap.put(domainClass, beanName);
150 }
151 }