1 package org.andromda.cartridges.jsf.component;
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Iterator;
7 import java.util.LinkedHashMap;
8 import java.util.List;
9 import java.util.Locale;
10 import java.util.Map;
11 import javax.faces.component.EditableValueHolder;
12 import javax.faces.component.UIComponent;
13 import javax.faces.component.UIComponentBase;
14 import javax.faces.context.FacesContext;
15 import javax.faces.context.ResponseWriter;
16 import javax.faces.validator.Validator;
17 import org.andromda.cartridges.jsf.utils.ComponentUtils;
18 import org.andromda.cartridges.jsf.validator.JSFValidator;
19 import org.andromda.cartridges.jsf.validator.JSFValidatorException;
20 import org.andromda.cartridges.jsf.validator.ValidatorMessages;
21 import org.andromda.utils.StringUtilsHelper;
22 import org.apache.commons.lang.StringUtils;
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25 import org.apache.commons.validator.Arg;
26 import org.apache.commons.validator.Field;
27 import org.apache.commons.validator.Form;
28 import org.apache.commons.validator.ValidatorAction;
29 import org.apache.commons.validator.ValidatorResources;
30
31
32
33
34
35
36 public class JSFValidatorComponent
37 extends UIComponentBase
38 {
39 private static final Log logger = LogFactory.getLog(JSFValidatorComponent.class);
40
41
42
43
44 public JSFValidatorComponent()
45 {
46
47 }
48
49
50
51
52
53
54
55 private Map validators = new LinkedHashMap();
56
57
58
59
60
61 public String getRendererType()
62 {
63 return null;
64 }
65
66
67
68
69
70
71 public String getFamily()
72 {
73 return null;
74 }
75
76
77
78
79
80
81
82
83 private void addValidator(
84 final String type,
85 final String id,
86 final JSFValidator validator)
87 {
88 Map map = (Map)this.validators.get(type);
89 if (map == null)
90 {
91 map = new LinkedHashMap();
92 this.validators.put(
93 type,
94 map);
95 }
96 if (id != null)
97 {
98 map.put(
99 id,
100 validator);
101 }
102 }
103
104 private Object getContextAttribute(final String attributeName)
105 {
106 return ComponentUtils.getAttribute(FacesContext.getCurrentInstance()
107 .getExternalContext().getContext(), attributeName);
108 }
109
110 private void setContextAttribute(final String attributeName, final String attributeValue)
111 {
112 ComponentUtils.setAttribute(FacesContext.getCurrentInstance()
113 .getExternalContext().getContext(), attributeName, attributeValue);
114 }
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 private void findValidators(
130 final UIComponent component,
131 final FacesContext context,
132 final UIComponent form)
133 {
134 if (component instanceof EditableValueHolder && this.canValidate(component))
135 {
136 final EditableValueHolder valueHolder = (EditableValueHolder)component;
137 if (form != null)
138 {
139 final String formId = form.getId();
140 final String componentId = component.getId();
141 final ValidatorResources resources = JSFValidator.getValidatorResources();
142 if (resources != null)
143 {
144 final Form validatorForm = resources.getForm(
145 Locale.getDefault(),
146 formId);
147 if (validatorForm != null)
148 {
149 final List validatorFields = validatorForm.getFields();
150 for (final Iterator iterator = validatorFields.iterator(); iterator.hasNext();)
151 {
152 final Field field = (Field)iterator.next();
153
154
155 final String fieldProperty = StringUtilsHelper.lowerCamelCaseName(field.getProperty());
156 if (componentId.equals(fieldProperty))
157 {
158 for (final Iterator dependencyIterator = field.getDependencyList().iterator();
159 dependencyIterator.hasNext();)
160 {
161 final String dependency = (String)dependencyIterator.next();
162 final ValidatorAction action = JSFValidator.getValidatorAction(dependency);
163 if (action != null)
164 {
165 final JSFValidator validator = new JSFValidator(formId, action);
166 final Arg[] args = field.getArgs(dependency);
167 if (args != null)
168 {
169 for (final Iterator varIterator = field.getVars().keySet().iterator(); varIterator.hasNext();)
170 {
171 final String name = (String)varIterator.next();
172 validator.addParameter(
173 name,
174 field.getVarValue(name));
175 }
176 validator.setArgs(ValidatorMessages.getArgs(
177 dependency,
178 field));
179 this.addValidator(
180 dependency,
181 component.getClientId(context),
182 validator);
183 if (!this.validatorPresent(valueHolder, validator))
184 {
185 valueHolder.addValidator(validator);
186 }
187 }
188 }
189 else
190 {
191 logger.error(
192 "No validator action with name '" + dependency +
193 "' registered in rules files '" + JSFValidator.RULES_LOCATION + '\'');
194 }
195 }
196 }
197 }
198 }
199 }
200 }
201 }
202 for (final Iterator iterator = component.getFacetsAndChildren(); iterator.hasNext();)
203 {
204 final UIComponent childComponent = (UIComponent)iterator.next();
205 this.findValidators(
206 childComponent,
207 context,
208 form);
209 }
210 }
211
212
213
214
215
216
217
218 private boolean canValidate(final UIComponent component)
219 {
220 boolean canValidate = true;
221 if (component != null)
222 {
223 canValidate = component.isRendered();
224 if (canValidate)
225 {
226 final UIComponent parent = component.getParent();
227 if (parent != null)
228 {
229 canValidate = canValidate(parent);
230 }
231 }
232 }
233 return canValidate;
234 }
235
236
237
238
239
240
241
242
243 private boolean validatorPresent(EditableValueHolder valueHolder, final Validator validator)
244 {
245 boolean present = false;
246 if (validator != null)
247 {
248 final Validator[] validators = valueHolder.getValidators();
249 if (validators != null)
250 {
251 for (int ctr = 0; ctr < validators.length; ctr++)
252 {
253 final Validator test = validators[ctr];
254 if (test instanceof JSFValidator)
255 {
256 present = test.toString().equals(validator.toString());
257 if (present)
258 {
259 break;
260 }
261 }
262 }
263 }
264 }
265 return present;
266 }
267
268 private static final String JAVASCRIPT_UTILITIES = "javascriptUtilities";
269
270
271
272
273
274
275 private final void writeScriptStart(final FacesContext context, UIComponent component)
276 throws IOException
277 {
278 final ResponseWriter writer = context.getResponseWriter();
279 String id = component.getClientId(context);
280 writer.startElement(
281 "script",
282 component);
283 writer.writeAttribute(
284 "type",
285 "text/javascript",
286 null);
287 writer.writeAttribute("id", id + ":validation-code", null);
288 }
289
290
291
292
293
294
295 private void writeScriptEnd(ResponseWriter writer)
296 throws IOException
297 {
298 writer.endElement("script");
299 }
300
301
302
303
304
305
306 private String getJavaScriptFunctionName(final ValidatorAction action)
307 {
308 String functionName = null;
309 final String javascript = action.getJavascript();
310 if (StringUtils.isNotBlank(javascript))
311 {
312 final String function = "function ";
313 int functionIndex = javascript.indexOf(function);
314 functionName = javascript.substring(functionIndex + 9);
315 functionName = functionName.substring(
316 0,
317 functionName.indexOf('(')).replaceAll(
318 "[\\s]+",
319 " ");
320 }
321 return functionName;
322 }
323
324
325
326
327
328 public static final String CLIENT = "client";
329
330
331
332
333
334
335 public void setClient(final String functionName)
336 {
337 this.getAttributes().put(CLIENT, functionName);
338 }
339
340
341
342
343
344
345 private boolean isClient()
346 {
347 String client = (String)this.getAttributes().get(CLIENT);
348 return StringUtils.isBlank(client) ? true : Boolean.valueOf(client).booleanValue();
349 }
350
351
352
353
354
355
356
357
358
359 private final void writeValidationFunctions(
360 final UIComponent form,
361 final ResponseWriter writer,
362 final FacesContext context)
363 throws IOException
364 {
365 writer.write("var bCancel = false;\n");
366 writer.write("self.validate" + StringUtils.capitalize(form.getId()) + " = ");
367 writer.write("function(form) { return bCancel || true\n");
368
369
370 final Collection validatorTypes = new ArrayList(this.validators.keySet());
371
372
373 for (final Iterator iterator = validatorTypes.iterator(); iterator.hasNext();)
374 {
375 final String type = (String)iterator.next();
376 final ValidatorAction action = JSFValidator.getValidatorAction(type);
377 final String functionName = this.getJavaScriptFunctionName(action);
378 if (StringUtils.isBlank(functionName))
379 {
380 iterator.remove();
381 }
382 }
383
384 for (final Iterator iterator = validatorTypes.iterator(); iterator.hasNext();)
385 {
386 final String type = (String)iterator.next();
387 final ValidatorAction action = JSFValidator.getValidatorAction(type);
388 if (!JAVASCRIPT_UTILITIES.equals(type))
389 {
390 writer.write("&& ");
391 writer.write(this.getJavaScriptFunctionName(action));
392 writer.write("(form)\n");
393 }
394 }
395 writer.write(";}\n");
396
397
398 for (final Iterator iterator = validatorTypes.iterator(); iterator.hasNext();)
399 {
400 final String type = (String)iterator.next();
401 final ValidatorAction action = JSFValidator.getValidatorAction(type);
402 String callback = action.getJsFunctionName();
403 if (StringUtils.isBlank(callback))
404 {
405 callback = type;
406 }
407 writer.write("function ");
408 writer.write(form.getId() + '_' + callback);
409 writer.write("() { \n");
410
411
412 final Map map = (Map)this.validators.get(type);
413 int ctr = 0;
414 for (final Iterator idIterator = map.keySet().iterator(); idIterator.hasNext(); ctr++)
415 {
416 final String id = (String)idIterator.next();
417 final JSFValidator validator = (JSFValidator)map.get(id);
418 writer.write("this[" + ctr + "] = ");
419 this.writeJavaScriptParams(
420 writer,
421 context,
422 id,
423 validator);
424 writer.write(";\n");
425 }
426 writer.write("}\n");
427 }
428
429
430 for (final Iterator iterator = validatorTypes.iterator(); iterator.hasNext();)
431 {
432 final String type = (String)iterator.next();
433 final ValidatorAction action = JSFValidator.getValidatorAction(type);
434 writer.write(action.getJavascript());
435 writer.write("\n");
436 }
437 }
438
439
440
441
442
443
444
445
446
447 private void writeJavaScriptParams(
448 final ResponseWriter writer,
449 final FacesContext context,
450 final String id,
451 final JSFValidator validator)
452 throws IOException
453 {
454 writer.write("new Array(\"");
455 writer.write(id);
456 writer.write("\", \"");
457 writer.write(validator.getErrorMessage(context));
458 writer.write("\", new Function(\"x\", \"return {");
459 final Map parameters = validator.getParameters();
460 for (final Iterator iterator = parameters.keySet().iterator(); iterator.hasNext();)
461 {
462 final String name = (String)iterator.next();
463 writer.write(name);
464 writer.write(":");
465 boolean mask = "mask".equals(name);
466
467
468 if (mask)
469 {
470 writer.write("/");
471 }
472 else
473 {
474 writer.write("'");
475 }
476 final Object parameter = parameters.get(name);
477 writer.write(parameter.toString());
478 if (mask)
479 {
480 writer.write("/");
481 }
482 else
483 {
484 writer.write("'");
485 }
486 if (iterator.hasNext())
487 {
488 writer.write(",");
489 }
490 }
491 writer.write("}[x];\"))");
492 }
493
494
495
496
497 private final Collection forms = new ArrayList();
498
499 private UIComponent findForm(final String id)
500 {
501 UIComponent form = null;
502 UIComponent validator = null;
503 try
504 {
505 validator = this.findComponent(id);
506 }
507 catch (NullPointerException exception)
508 {
509
510 }
511 if (validator instanceof JSFValidatorComponent)
512 {
513 final UIComponent parent = validator.getParent();
514
515 if (parent instanceof UIComponent)
516 {
517 form = parent;
518 }
519 }
520 return form;
521 }
522
523
524
525
526 private static final String RULES_NOT_PRESENT = "validationRulesNotPresent";
527
528
529
530
531
532
533
534
535
536 public void encodeBegin(final FacesContext context)
537 throws IOException
538 {
539 boolean validationResourcesPresent = this.getContextAttribute(RULES_NOT_PRESENT) == null;
540 if (validationResourcesPresent && JSFValidator.getValidatorResources() == null)
541 {
542 this.setContextAttribute(
543 RULES_NOT_PRESENT,
544 "true");
545 validationResourcesPresent = false;
546 }
547 if (validationResourcesPresent)
548 {
549 try
550 {
551 this.validators.clear();
552 this.forms.clear();
553
554 this.addValidator(
555 JAVASCRIPT_UTILITIES,
556 null,
557 null);
558 final UIComponent form = this.findForm(this.getId());
559 if (form != null)
560 {
561 this.findValidators(
562 form,
563 context,
564 form);
565 if (this.isClient())
566 {
567 final ResponseWriter writer = context.getResponseWriter();
568 this.writeScriptStart(context, form);
569 this.writeValidationFunctions(
570 form,
571 writer,
572 context);
573 this.writeScriptEnd(writer);
574 }
575 }
576 }
577 catch (final JSFValidatorException exception)
578 {
579 logger.error(exception);
580 }
581 }
582 }
583 }