1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.struts.faces.renderer;
23
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.Comparator;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.function.Function;
33
34 import org.apache.commons.validator.Field;
35 import org.apache.commons.validator.Form;
36 import org.apache.commons.validator.ValidatorAction;
37 import org.apache.commons.validator.ValidatorResources;
38 import org.apache.commons.validator.Var;
39 import org.apache.commons.validator.util.ValidatorUtils;
40 import org.apache.struts.Globals;
41 import org.apache.struts.config.ModuleConfig;
42 import org.apache.struts.faces.component.FormComponent;
43 import org.apache.struts.faces.component.JavascriptValidatorComponent;
44 import org.apache.struts.faces.util.StrutsContext;
45 import org.apache.struts.faces.util.Utils;
46 import org.apache.struts.util.MessageResources;
47 import org.apache.struts.validator.Resources;
48 import org.apache.struts.validator.ValidatorPlugIn;
49
50 import jakarta.faces.component.UIComponent;
51 import jakarta.faces.context.FacesContext;
52 import jakarta.faces.context.ResponseWriter;
53
54
55
56
57
58
59
60
61 public class JavascriptValidatorRenderer extends AbstractRenderer {
62
63 private final static Function<ValidatorAction, Integer> SIZE_VA = (va) ->
64 va == null || va.getDepends() == null ? 0 : va.getDepends().length();
65
66 private final static Comparator<ValidatorAction> COMP_VA = (va1, va2) -> {
67 final int size1 = SIZE_VA.apply(va1);
68 final int size2 = SIZE_VA.apply(va2);
69
70 if (size1 == 0) {
71 return size2 == 0 ? 0 : -1;
72 } else if (size2 == 0) {
73 return 1;
74 }
75 return va1.getDependencyList().size()
76 - va2.getDependencyList().size();
77 };
78
79 private final static String HTML_BEGIN_COMMENT = "\n<!-- Begin \n";
80
81 private final static String HTML_END_COMMENT = "//End --> \n";
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 protected void renderEnd(FacesContext context, UIComponent component,
98 ResponseWriter writer) throws IOException {
99
100 if (context == null || component == null) {
101 throw new NullPointerException();
102 }
103
104 final StringBuilder results = new StringBuilder();
105
106 final StrutsContext strutsContext = new StrutsContext(context);
107 JavascriptValidatorComponent jsv = (JavascriptValidatorComponent) component;
108
109 ModuleConfig config = strutsContext.getModuleConfig();
110
111 ValidatorResources resources = Utils.getMapValue(ValidatorResources.class,
112 context.getExternalContext().getApplicationMap(),
113 ValidatorPlugIn.VALIDATOR_KEY + config.getPrefix());
114
115 Locale locale = strutsContext.getLocale();
116
117
118 String bundle = jsv.getBundle();
119 if (bundle == null) {
120 bundle = Globals.MESSAGES_KEY;
121 }
122
123 Form form = resources.getForm(locale, jsv.getFormName());
124 if (form != null) {
125 if (jsv.isDynamicJavascript()) {
126 MessageResources messages = Utils.getMapValue(MessageResources.class,
127 context.getExternalContext().getApplicationMap(), bundle);
128
129 List<ValidatorAction> lActions = new ArrayList<>();
130 List<String> lActionMethods = new ArrayList<>();
131
132
133 for (Field field : form.getFields()) {
134
135 for (String depends : field.getDependencyList()) {
136
137 if (depends != null && !lActionMethods.contains(depends)) {
138 lActionMethods.add(depends);
139 }
140 }
141
142 }
143
144
145 for (Iterator<String> i = lActionMethods.iterator(); i.hasNext();) {
146 String depends = i.next();
147 ValidatorAction va = resources.getValidatorAction(depends);
148
149
150 if (va == null) {
151 throw new NullPointerException(
152 "Depends string \""
153 + depends
154 + "\" was not found in validator-rules.xml.");
155 }
156
157 String javascript = va.getJavascript();
158 if (javascript != null && javascript.length() > 0) {
159 lActions.add(va);
160 } else {
161 i.remove();
162 }
163 }
164
165 Collections.sort(lActions, COMP_VA);
166
167 String methods = null;
168 for (ValidatorAction va : lActions) {
169
170 if (methods == null) {
171 methods = va.getMethod() + "(form)";
172 } else {
173 methods += " && " + va.getMethod() + "(form)";
174 }
175 }
176
177 results.append(getJavascriptBegin(methods, jsv));
178
179 final String formClientId = getFormClientId(jsv);
180 final String formClientIdFunc = formClientId.replace(':', '_');
181
182 for (ValidatorAction va : lActions) {
183 String jscriptVar = null;
184 String functionName = null;
185
186 if (va.getJsFunctionName() != null
187 && va.getJsFunctionName().length() > 0) {
188 functionName = va.getJsFunctionName();
189 } else {
190 functionName = va.getName();
191 }
192
193 results.append(" function " +
194 formClientIdFunc + "_" + functionName +
195 " () { \n");
196
197 for (Field field : form.getFields()) {
198
199
200
201
202 if (field.isIndexed()
203 || field.getPage() != jsv.getPage()
204 || !field.isDependency(va.getName())) {
205
206 continue;
207 }
208
209 String message =
210 Resources.getMessage(messages, locale, va, field);
211
212 message = (message != null) ? message : "";
213
214 jscriptVar = this.getNextVar(jscriptVar);
215
216 results.append(
217 " this."
218 + jscriptVar
219 + " = new Array(\""
220 + formClientId
221 + ":"
222 + field.getKey()
223 + "\", \""
224 + message
225 + "\", ");
226
227 results.append("new Function (\"varName\", \"");
228
229 Map<String, Var> vars = field.getVars();
230
231 for (Map.Entry<String, Var> entry : vars.entrySet()) {
232 String varName = entry.getKey();
233 Var var = entry.getValue();
234 String varValue = var.getValue();
235 String jsType = var.getJsType();
236
237
238
239 if (varName.startsWith("field")) {
240 continue;
241 }
242
243 if (Var.JSTYPE_INT.equalsIgnoreCase(jsType)) {
244 results.append(
245 "this."
246 + varName
247 + "="
248 + ValidatorUtils.replace(
249 varValue,
250 "\\",
251 "\\\\")
252 + "; ");
253 } else if (Var.JSTYPE_REGEXP.equalsIgnoreCase(
254 jsType)) {
255 results.append(
256 "this."
257 + varName
258 + "=/"
259 + ValidatorUtils.replace(
260 varValue,
261 "\\",
262 "\\\\")
263 + "/; ");
264 } else if (Var.JSTYPE_STRING.equalsIgnoreCase(
265 jsType)) {
266 results.append(
267 "this."
268 + varName
269 + "='"
270 + ValidatorUtils.replace(
271 varValue,
272 "\\",
273 "\\\\")
274 + "'; ");
275
276
277 } else if ("mask".equalsIgnoreCase(varName)) {
278 results.append(
279 "this."
280 + varName
281 + "=/"
282 + ValidatorUtils.replace(
283 varValue,
284 "\\",
285 "\\\\")
286 + "/; ");
287 } else {
288 results.append(
289 "this."
290 + varName
291 + "='"
292 + ValidatorUtils.replace(
293 varValue,
294 "\\",
295 "\\\\")
296 + "'; ");
297 }
298 }
299
300 results.append(" return this[varName];\"));\n");
301 }
302 results.append(" } \n\n");
303 }
304 } else if (jsv.isStaticJavascript()) {
305 results.append(getStartElement(jsv));
306 if (jsv.isHtmlComment()) {
307 results.append(HTML_BEGIN_COMMENT);
308 }
309 }
310 }
311
312 if (jsv.isStaticJavascript()) {
313 results.append(getJavascriptStaticMethods(resources));
314 }
315
316 if (form != null
317 && (jsv.isDynamicJavascript() || jsv.isStaticJavascript())) {
318
319 results.append(getJavascriptEnd(jsv));
320 }
321
322 writer.write(results.toString());
323 }
324
325
326
327
328
329
330 protected String getJavascriptBegin(String methods, JavascriptValidatorComponent component) {
331 StringBuilder sb = new StringBuilder();
332 String name =
333 component.getFormName().substring(0, 1).toUpperCase()
334 + component.getFormName().substring(1);
335
336 sb.append(getStartElement(component));
337
338 if (isXhtml(component) && component.isCdata()) {
339 sb.append("<![CDATA[\r\n");
340 }
341
342 if (!isXhtml(component) && component.isHtmlComment()) {
343 sb.append(HTML_BEGIN_COMMENT);
344 }
345 sb.append("\n var bCancel = false; \n\n");
346
347 if (component.getMethod() == null || component.getMethod().isEmpty()) {
348 sb.append(
349 " function validate"
350 + name
351 + "(form) { "
352 + " \n");
353 } else {
354 sb.append(
355 " function "
356 + component.getMethod()
357 + "(form) { "
358 + " \n");
359 }
360 sb.append(" if (bCancel) \n");
361 sb.append(" return true; \n");
362 sb.append(" else \n");
363
364
365 if (methods == null || methods.isEmpty()) {
366 sb.append(" return true; \n");
367 } else {
368 sb.append(" return " + methods + "; \n");
369 }
370
371 sb.append(" } \n\n");
372
373 return sb.toString();
374 }
375
376 protected String getJavascriptStaticMethods(ValidatorResources resources) {
377 StringBuilder sb = new StringBuilder();
378
379 sb.append("\n\n");
380
381 for (ValidatorAction va : resources.getValidatorActions().values()) {
382 if (va != null) {
383 String javascript = va.getJavascript();
384 if (javascript != null && javascript.length() > 0) {
385 sb.append(javascript + "\n");
386 }
387 }
388 }
389
390 return sb.toString();
391 }
392
393
394
395
396 protected String getJavascriptEnd(JavascriptValidatorComponent component) {
397 StringBuilder sb = new StringBuilder();
398
399 sb.append("\n");
400 if (!isXhtml(component) && component.isHtmlComment()){
401 sb.append(HTML_END_COMMENT);
402 }
403
404 if (isXhtml(component) && component.isCdata()) {
405 sb.append("]]>\r\n");
406 }
407
408 sb.append("</script>\n\n");
409
410 return sb.toString();
411 }
412
413
414
415
416
417 private String getNextVar(String input) {
418 if (input == null) {
419 return "aa";
420 }
421
422 input = input.toLowerCase();
423
424 for (int i = input.length(); i > 0; i--) {
425 int pos = i - 1;
426
427 char c = input.charAt(pos);
428 c++;
429
430 if (c <= 'z') {
431 if (i == 0) {
432 return c + input.substring(pos, input.length());
433 } else if (i == input.length()) {
434 return input.substring(0, pos) + c;
435 } else {
436 return input.substring(0, pos) + c + input.substring(pos,
437 input.length() - 1);
438 }
439 } else {
440 input = replaceChar(input, pos, 'a');
441 }
442
443 }
444
445 return null;
446
447 }
448
449
450
451
452 private String replaceChar(String input, int pos, char c) {
453 if (pos == 0) {
454 return c + input.substring(pos, input.length());
455 } else if (pos == input.length()) {
456 return input.substring(0, pos) + c;
457 } else {
458 return input.substring(0, pos) + c + input.substring(pos,
459 input.length() - 1);
460 }
461 }
462
463
464
465
466
467 private String getStartElement(JavascriptValidatorComponent component) {
468 StringBuilder start =
469 new StringBuilder("<script type=\"text/javascript\"");
470
471
472 if (!isXhtml(component)) {
473 start.append(" language=\"Javascript\"");
474 }
475
476 if (component.getSrc() != null) {
477 start.append(" src=\"" + component.getSrc() + "\"");
478 }
479
480 start.append("> \n");
481 return start.toString();
482 }
483
484
485
486
487
488
489
490
491
492 private String getFormClientId(final JavascriptValidatorComponent component){
493 final String formName = component.getFormName();
494
495 for (UIComponent current = component; current != null; current = current.getParent()) {
496 List<UIComponent> kids = current.getChildren();
497 for (UIComponent kid : kids) {
498 if (kid instanceof FormComponent
499 && formName.equals(kid.getAttributes().get("beanName"))) {
500
501 return kid.getClientId(FacesContext.getCurrentInstance());
502 }
503 }
504 }
505
506 throw new IllegalArgumentException
507 ("Cannot find child FormComponent for form '" + formName + "'");
508 }
509 }