1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.nuiton.validator.xwork2;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.nuiton.util.StringUtil;
27 import org.nuiton.validator.AbstractNuitonValidatorProvider;
28 import org.nuiton.validator.NuitonValidator;
29 import org.nuiton.validator.NuitonValidatorModel;
30 import org.nuiton.validator.NuitonValidatorScope;
31
32 import java.io.File;
33 import java.io.FilenameFilter;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Comparator;
37 import java.util.EnumSet;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Set;
41 import java.util.SortedSet;
42 import java.util.TreeSet;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
45
46
47
48
49
50
51
52 public class XWork2NuitonValidatorProvider extends AbstractNuitonValidatorProvider {
53
54 public static final String PROVIDER_NAME = "xwork2";
55
56
57 private static final Log log =
58 LogFactory.getLog(XWork2NuitonValidatorProvider.class);
59
60 public XWork2NuitonValidatorProvider() {
61 super(PROVIDER_NAME);
62 }
63
64 @Override
65 public <O> NuitonValidatorModel<O> newModel(Class<O> type,
66 String context,
67 NuitonValidatorScope... scopes) {
68
69 if (scopes.length == 0) {
70
71 scopes = NuitonValidatorScope.values();
72 }
73
74 Map<NuitonValidatorScope, String[]> fields =
75 XWork2ValidatorUtil.detectFields(type, context, scopes);
76
77 Set<NuitonValidatorScope> scopeSet =
78 EnumSet.noneOf(NuitonValidatorScope.class);
79 scopeSet.addAll(Arrays.asList(scopes));
80
81 return new NuitonValidatorModel<O>(type, context, scopeSet, fields);
82 }
83
84 @Override
85 public <O> XWork2NuitonValidator<O> newValidator(NuitonValidatorModel<O> model) {
86 return new XWork2NuitonValidator<O>(model);
87 }
88
89 @Override
90 public SortedSet<NuitonValidator<?>> detectValidators(File sourceRoot,
91 Pattern contextFilter,
92 NuitonValidatorScope[] scopes,
93 Class<?>... types) {
94
95 if (scopes == null) {
96
97
98 scopes = NuitonValidatorScope.values();
99 }
100
101 SortedSet<NuitonValidator<?>> result =
102 new TreeSet<NuitonValidator<?>>(new ValidatorComparator());
103
104 for (Class<?> c : types) {
105 File dir = getClassDir(sourceRoot, c);
106 if (!dir.exists()) {
107
108
109 if (log.isDebugEnabled()) {
110 log.debug("skip none existing directory " + dir);
111 }
112 continue;
113 }
114 String[] contexts = getContexts(c, dir);
115 if (log.isDebugEnabled()) {
116 log.debug("contexts : " + Arrays.toString(contexts));
117 }
118
119 if (contexts.length > 0) {
120 String[] realContexts = getContextsWithoutScopes(contexts);
121
122 if (log.isDebugEnabled()) {
123 log.debug("realContexts : " +
124 Arrays.toString(realContexts));
125 }
126
127 if (contextFilter != null) {
128
129
130 realContexts = getFilterContexts(contextFilter,
131 realContexts
132 );
133 if (log.isDebugEnabled()) {
134 log.debug("filterContexts : " +
135 Arrays.toString(realContexts));
136 }
137 }
138
139 for (String context : realContexts) {
140
141
142 NuitonValidator<?> validator = getValidator(
143 c,
144 context.isEmpty() ? null : context,
145 scopes
146 );
147 if (validator != null) {
148
149 result.add(validator);
150 }
151 }
152 }
153 }
154 return result;
155 }
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172 protected <O> NuitonValidator<O> getValidator(Class<O> klass,
173 String context,
174 NuitonValidatorScope... scopes) {
175
176 NuitonValidatorModel<O> model = newModel(klass, context, scopes);
177
178 XWork2NuitonValidator<O> valitator = newValidator(model);
179
180 Set<NuitonValidatorScope> realScopes = valitator.getEffectiveScopes();
181 if (realScopes.isEmpty()) {
182 valitator = null;
183 if (log.isDebugEnabled()) {
184 log.debug(klass + " : validator skip (no scopes detected)");
185 }
186 } else {
187 if (log.isDebugEnabled()) {
188 log.debug(klass + " : keep validator " + valitator);
189 }
190 }
191 return valitator;
192 }
193
194 protected File getClassDir(File sourceRoot, Class<?> clazz) {
195 String path = clazz.getPackage().getName();
196 path = path.replaceAll("\\.", StringUtil.getFileSeparatorRegex());
197 File dir = new File(sourceRoot, path);
198 return dir;
199 }
200
201 protected String[] getContexts(Class<?> clazz, File dir) {
202 Set<String> result = new TreeSet<String>();
203 ValidatorFilenameFilter filter = new ValidatorFilenameFilter(clazz);
204 if (log.isDebugEnabled()) {
205 log.debug("dir : " + dir);
206 }
207 String[] files = dir.list(filter);
208 for (String file : files) {
209 if (log.isDebugEnabled()) {
210 log.debug("file " + file);
211 }
212 String context = file.substring(
213 filter.prefix.length(),
214 file.length() - ValidatorFilenameFilter.SUFFIX.length()
215 );
216 if (log.isDebugEnabled()) {
217 log.debug("detect " + clazz.getSimpleName() +
218 " context [" + context + "]");
219 }
220 result.add(context);
221 }
222 return result.toArray(new String[result.size()]);
223 }
224
225 protected String[] getContextsWithoutScopes(String[] contexts) {
226 Set<String> result = new TreeSet<String>();
227 NuitonValidatorScope[] scopes = NuitonValidatorScope.values();
228 for (String context : contexts) {
229 for (NuitonValidatorScope scope : scopes) {
230 String scopeName = scope.name().toLowerCase();
231 if (!context.endsWith(scopeName)) {
232
233 continue;
234 }
235 if (log.isDebugEnabled()) {
236 log.debug("detect context : " + context);
237 }
238 String realContext = context.substring(
239 0,
240 context.length() - scopeName.length()
241 );
242 if (realContext.endsWith("-")) {
243 realContext = realContext.substring(
244 0,
245 realContext.length() - 1
246 );
247 }
248 result.add(realContext);
249 }
250 }
251 return result.toArray(new String[result.size()]);
252 }
253
254 protected String[] getFilterContexts(Pattern contextFilter,
255 String[] realContexts) {
256 List<String> result = new ArrayList<String>();
257 for (String c : realContexts) {
258 Matcher m = contextFilter.matcher(c);
259 if (m.matches()) {
260 result.add(c);
261 }
262 }
263 return result.toArray(new String[result.size()]);
264 }
265
266 protected static class ValidatorFilenameFilter implements FilenameFilter {
267
268 protected static final String SUFFIX = "-validation.xml";
269
270 protected Class<?> clazz;
271
272 protected String prefix;
273
274 public ValidatorFilenameFilter(Class<?> clazz) {
275 this.clazz = clazz;
276 prefix = clazz.getSimpleName() + "-";
277 }
278
279 @Override
280 public boolean accept(File dir, String name) {
281 boolean result = name.endsWith(SUFFIX);
282 if (result) {
283 result = name.startsWith(prefix);
284 }
285 return result;
286 }
287 }
288
289 protected static class ValidatorComparator implements Comparator<NuitonValidator<?>> {
290
291 @Override
292 public int compare(NuitonValidator<?> o1, NuitonValidator<?> o2) {
293
294 NuitonValidatorModel<?> model1 = ((XWork2NuitonValidator<?>) o1).getModel();
295 NuitonValidatorModel<?> model2 = ((XWork2NuitonValidator<?>) o2).getModel();
296
297 String contextName1 =
298 model1.getType().getSimpleName() + "-" +
299 (model1.getContext() == null ? "" : model1.getContext());
300 String contextName2 =
301 model2.getType().getSimpleName() + "-" +
302 (model2.getContext() == null ? "" : model2.getContext());
303 return contextName1.compareTo(contextName2);
304 }
305 }
306
307 }