1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.uif.container;
17
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25
26 import org.apache.commons.lang.StringUtils;
27 import org.kuali.rice.krad.datadictionary.parse.BeanTag;
28 import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
29 import org.kuali.rice.krad.datadictionary.parse.BeanTags;
30 import org.kuali.rice.krad.uif.UifConstants;
31 import org.kuali.rice.krad.uif.component.BindingInfo;
32 import org.kuali.rice.krad.uif.component.Component;
33 import org.kuali.rice.krad.uif.component.DataBinding;
34 import org.kuali.rice.krad.uif.control.CheckboxControl;
35 import org.kuali.rice.krad.uif.control.Control;
36 import org.kuali.rice.krad.uif.control.SelectControl;
37 import org.kuali.rice.krad.uif.control.TextControl;
38 import org.kuali.rice.krad.uif.element.Action;
39 import org.kuali.rice.krad.uif.element.Image;
40 import org.kuali.rice.krad.uif.element.Label;
41 import org.kuali.rice.krad.uif.element.Link;
42 import org.kuali.rice.krad.uif.element.Message;
43 import org.kuali.rice.krad.uif.field.DataField;
44 import org.kuali.rice.krad.uif.field.Field;
45 import org.kuali.rice.krad.uif.field.FieldGroup;
46 import org.kuali.rice.krad.uif.field.InputField;
47 import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
48 import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleUtils;
49 import org.kuali.rice.krad.uif.util.ComponentUtils;
50 import org.kuali.rice.krad.uif.util.LifecycleElement;
51 import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
52 import org.kuali.rice.krad.uif.view.ExpressionEvaluator;
53 import org.kuali.rice.krad.uif.view.View;
54 import org.kuali.rice.krad.uif.widget.Inquiry;
55 import org.kuali.rice.krad.uif.widget.RichTable;
56 import org.kuali.rice.krad.uif.widget.Tooltip;
57 import org.kuali.rice.krad.util.KRADConstants;
58 import org.kuali.rice.krad.util.KRADUtils;
59 import org.kuali.rice.krad.web.form.UifFormBase;
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 @BeanTags({@BeanTag(name = "lightTableGroup-bean", parent = "Uif-LightTableGroup"),
85 @BeanTag(name = "lightTableSection-bean", parent = "Uif-LightTableSection"),
86 @BeanTag(name = "lightTableSubSection-bean", parent = "Uif-LightTableSubSection")})
87 public class LightTable extends GroupBase implements DataBinding {
88 private static final long serialVersionUID = -8930885219866835711L;
89
90 private static final String VALUE_TOKEN = "@v@";
91 private static final String EXPRESSION_TOKEN = "@e@";
92 private static final String RENDER = "render";
93 private static final String ID_TOKEN = "@id@";
94 private static final String A_TOKEN = "@";
95 private static final String ROW_CLASS = "@rowClass@";
96 private static final String SORT_VALUE = "@sortVal";
97 private static final String SEPARATOR = "@@@";
98
99 private String propertyName;
100 private BindingInfo bindingInfo;
101 private List<Label> headerLabels;
102 private RichTable richTable;
103 private Map<String, String> conditionalRowCssClasses;
104
105 private Map<String, String> expressionConversionMap;
106 private List<String> initialComponentIds;
107 private Map<String, String> renderIdExpressionMap;
108 private boolean emptyTable;
109 private String currentColumnValue;
110
111
112
113
114 public LightTable() {
115 expressionConversionMap = new HashMap<String, String>();
116 initialComponentIds = new ArrayList<String>();
117 renderIdExpressionMap = new HashMap<String, String>();
118 }
119
120
121
122
123
124 @Override
125 public void performInitialization(Object model) {
126 super.performInitialization(model);
127 richTable.setForceLocalJsonData(true);
128
129
130 if (bindingInfo != null) {
131 bindingInfo.setDefaults(ViewLifecycle.getView(), getPropertyName());
132 }
133
134 List<? extends Component> items = getItems();
135
136 ComponentUtils.clearAndAssignIds(items);
137
138
139 for (Component item : this.getItems()) {
140 initialComponentIds.add(item.getId());
141
142
143 if (item instanceof DataField) {
144 ((DataField) item).setForcedValue(VALUE_TOKEN + item.getId() + VALUE_TOKEN);
145 }
146
147
148 expressionConversionMap = buildExpressionMap(item, expressionConversionMap);
149 }
150 }
151
152
153
154
155
156
157
158
159
160
161 protected Map<String, String> buildExpressionMap(Component item, Map<String, String> expressionMap) {
162 if (item == null) {
163 return expressionMap;
164 }
165
166 List<String> toRemove = new ArrayList<String>();
167
168 if (item.getExpressionGraph() != null && !item.getExpressionGraph().isEmpty()) {
169 for (String name : item.getExpressionGraph().keySet()) {
170 processExpression(name, item, expressionMap, toRemove);
171 }
172 }
173
174
175 item.setId(ID_TOKEN + item.getId() + ID_TOKEN);
176
177 if (item instanceof Group) {
178 ((Group) item).getLayoutManager().setId(ID_TOKEN + ((Group) item).getLayoutManager().getId() + ID_TOKEN);
179 }
180
181 expressionMap = addChildExpressions(ViewLifecycleUtils.getElementsForLifecycle(item).values(), expressionMap);
182
183 for (String name : toRemove) {
184 item.getExpressionGraph().remove(name);
185 }
186
187 return expressionMap;
188 }
189
190
191
192
193
194
195
196
197
198
199 public void processExpression(String name, Component item, Map<String, String> expressionMap,
200 List<String> toRemove) {
201 Class<?> clazz = ObjectPropertyUtils.getPropertyType(item, name);
202 if (clazz == null) {
203 return;
204 }
205
206 if (clazz.isAssignableFrom(String.class)) {
207
208 expressionMap.put(name + SEPARATOR + item.getId(), item.getExpressionGraph().get(name));
209 toRemove.add(name);
210 ObjectPropertyUtils.setPropertyValue(item, name,
211 EXPRESSION_TOKEN + name + SEPARATOR + item.getId() + EXPRESSION_TOKEN);
212
213 } else if (name.endsWith(RENDER) && clazz.isAssignableFrom(boolean.class)) {
214
215 Component renderComponent = item;
216
217
218 if (!name.equals(RENDER)) {
219 renderComponent = ObjectPropertyUtils.getPropertyValue(item, StringUtils.removeEnd(name, ".render"));
220 }
221
222
223 renderIdExpressionMap.put(renderComponent.getId(), item.getExpressionGraph().get(name));
224 toRemove.add(name);
225
226 String renderMarker = A_TOKEN + RENDER + A_TOKEN + renderComponent.getId() + A_TOKEN;
227
228
229 String pre = renderComponent.getPreRenderContent() == null ? "" : renderComponent.getPreRenderContent();
230 renderComponent.setPreRenderContent(renderMarker + pre);
231
232
233 String post = renderComponent.getPostRenderContent() == null ? "" : renderComponent.getPostRenderContent();
234 renderComponent.setPostRenderContent(post + renderMarker);
235
236
237 ObjectPropertyUtils.setPropertyValue(item, name, true);
238 }
239 }
240
241
242
243
244
245
246
247
248 protected Map<String, String> addChildExpressions(Collection<? extends LifecycleElement> components,
249 Map<String, String> expressionMap) {
250 for (LifecycleElement comp : components) {
251 if (comp != null && (comp instanceof Action
252 || comp instanceof Image
253 || comp instanceof Message
254 || comp instanceof Link
255 || comp instanceof Inquiry
256 || comp instanceof Group
257 || comp instanceof Tooltip
258 || comp instanceof InputField
259 || comp instanceof CheckboxControl
260 || comp instanceof TextControl
261 || comp instanceof SelectControl)) {
262 expressionMap = buildExpressionMap((Component) comp, expressionMap);
263 }
264 }
265
266 return expressionMap;
267 }
268
269
270
271
272 @Override
273 public void performFinalize(Object model, LifecycleElement parent) {
274 super.performFinalize(model, parent);
275
276 headerLabels = new ArrayList<Label>();
277 for (Component item : this.getItems()) {
278
279 if (item instanceof Field) {
280 headerLabels.add(ComponentUtils.copy(((Field) item).getFieldLabel()));
281 ((Field) item).getFieldLabel().setRender(false);
282 } else {
283 headerLabels.add(null);
284 }
285
286 if (item instanceof FieldGroup) {
287 ((FieldGroup) item).getGroup().setValidationMessages(null);
288
289 }
290
291 if (item instanceof DataField) {
292 ((DataField) item).getBindingInfo().setBindByNamePrefix(this.getBindingInfo().getBindingPath() + "[0]");
293 }
294 }
295
296 Object collectionValue = ObjectPropertyUtils.getPropertyValue(model, bindingInfo.getBindingPath());
297
298
299 if (collectionValue == null || !(collectionValue instanceof Collection) ||
300 ((Collection<?>) collectionValue).isEmpty()) {
301 emptyTable = true;
302 }
303 }
304
305
306
307
308
309
310
311
312
313 public void buildRows(View view, String rowTemplate, UifFormBase model) {
314 if (StringUtils.isBlank(rowTemplate)) {
315 return;
316 }
317
318 rowTemplate = StringUtils.removeEnd(rowTemplate, ",");
319 rowTemplate = rowTemplate.replace("\n", "");
320 rowTemplate = rowTemplate.replace("\r", "");
321
322 StringBuffer rows = new StringBuffer();
323 List<Object> collectionObjects = ObjectPropertyUtils.getPropertyValue(model, bindingInfo.getBindingPath());
324
325
326 rowTemplate = rowTemplate.replace("checked=\"checked\"", "");
327
328
329 Pattern idPattern = Pattern.compile(ID_TOKEN + "(.*?)" + ID_TOKEN);
330 Pattern expressionPattern = Pattern.compile(EXPRESSION_TOKEN + "(.*?)" + EXPRESSION_TOKEN);
331
332 ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator();
333 expressionEvaluator.initializeEvaluationContext(model);
334
335 int lineIndex = 0;
336 for (Object obj : collectionObjects) {
337
338 String row = idPattern.matcher(rowTemplate).replaceAll("$1" + UifConstants.IdSuffixes.LINE + lineIndex);
339
340
341 Map<String, Object> expandedContext = new HashMap<String, Object>();
342 expandedContext.put(UifConstants.ContextVariableNames.LINE, obj);
343 expandedContext.put(UifConstants.ContextVariableNames.INDEX, lineIndex);
344 expandedContext.put(UifConstants.ContextVariableNames.VIEW, view);
345
346 currentColumnValue = "";
347
348 int itemIndex = 0;
349 for (Component item : this.getItems()) {
350
351 String originalId = initialComponentIds.get(itemIndex);
352
353
354 row = handleDataFieldInRow(item, obj, row, lineIndex, originalId);
355
356
357 row = handleInputFieldInRow(item, obj, row, lineIndex, originalId);
358
359
360 if (item.getContext() != null) {
361 expandedContext.putAll(item.getContext());
362 }
363
364
365 row = evaluateAndReplaceExpressionValues(row, lineIndex, model, expandedContext, expressionPattern,
366 expressionEvaluator);
367
368 if (currentColumnValue == null) {
369 currentColumnValue = "";
370 }
371
372 row = row.replace(SORT_VALUE + itemIndex + A_TOKEN, currentColumnValue);
373
374 itemIndex++;
375 }
376
377
378 boolean isOdd = lineIndex % 2 == 0;
379 String rowCss = KRADUtils.generateRowCssClassString(conditionalRowCssClasses, lineIndex, isOdd,
380 expandedContext, expressionEvaluator);
381
382 row = row.replace("\"", "\\\"");
383 row = row.replace(ROW_CLASS, rowCss);
384 row = "{" + row + "},";
385
386
387 row = evaluateRenderExpressions(row, lineIndex, model, expandedContext, expressionEvaluator);
388
389
390 rows.append(row);
391 lineIndex++;
392 }
393
394 StringBuffer tableToolsColumnOptions = new StringBuffer("[");
395 for (int index = 0; index < this.getItems().size(); index++) {
396 String colOptions = richTable.constructTableColumnOptions(index, true, false, String.class, null);
397 tableToolsColumnOptions.append(colOptions + " , ");
398 }
399
400 String aoColumnDefs = StringUtils.removeEnd(tableToolsColumnOptions.toString(), " , ") + "]";
401 Map<String, String> rtTemplateOptions = richTable.getTemplateOptions();
402
403 if (rtTemplateOptions == null) {
404 richTable.setTemplateOptions(rtTemplateOptions = new HashMap<String, String>());
405 }
406
407 rtTemplateOptions.put(UifConstants.TableToolsKeys.AO_COLUMN_DEFS, aoColumnDefs);
408
409
410 String aaData = StringUtils.removeEnd(rows.toString(), ",");
411 aaData = "[" + aaData + "]";
412 aaData = aaData.replace(KRADConstants.QUOTE_PLACEHOLDER, "\"");
413
414
415 rtTemplateOptions.put(UifConstants.TableToolsKeys.AA_DATA, aaData);
416
417
418 rtTemplateOptions.put(UifConstants.TableToolsKeys.DEFER_RENDER,
419 UifConstants.TableToolsValues.TRUE);
420 }
421
422
423
424
425
426
427
428
429
430
431
432
433 protected String evaluateAndReplaceExpressionValues(String row, int index, Object model,
434 Map<String, Object> expandedContext, Pattern expressionPattern, ExpressionEvaluator expressionEvaluator) {
435
436 Matcher matcher = expressionPattern.matcher(row);
437
438 while (matcher.find()) {
439 String matchingGroup = matcher.group(1);
440 String expression = expressionConversionMap.get(matchingGroup);
441
442
443 expression = expression.replace(UifConstants.LINE_PATH_BIND_ADJUST_PREFIX,
444 this.getBindingInfo().getBindingPath() + "[" + index + "].");
445
446
447 Object value = expressionEvaluator.evaluateExpressionTemplate(expandedContext, expression);
448
449 if (value != null) {
450 row = row.replace(matcher.group(), value.toString());
451 } else {
452 row = row.replace(matcher.group(), "");
453 }
454 }
455
456 return row;
457 }
458
459
460
461
462
463
464
465
466
467
468
469 protected String evaluateRenderExpressions(String row, int index, Object model, Map<String, Object> expandedContext,
470 ExpressionEvaluator expressionEvaluator) {
471 for (String id : renderIdExpressionMap.keySet()) {
472 String expression = renderIdExpressionMap.get(id);
473
474
475 expression = expression.replace(UifConstants.LINE_PATH_BIND_ADJUST_PREFIX,
476 this.getBindingInfo().getBindingPath() + "[" + index + "].");
477
478
479 Object value = expressionEvaluator.evaluateExpressionTemplate(expandedContext, expression);
480
481 String wrap = A_TOKEN + RENDER + A_TOKEN + id + A_TOKEN;
482
483 if (value != null && value instanceof String && Boolean.parseBoolean((String) value) == false) {
484
485 row = row.replaceAll(wrap + "(.|\\s)*?" + wrap, "");
486 } else {
487
488 row = row.replaceAll(wrap, "");
489 }
490 }
491
492 return row;
493 }
494
495
496
497
498
499
500
501
502
503
504
505 protected String handleDataFieldInRow(Component item, Object obj, String row, int index, String originalId) {
506 if (!(item instanceof DataField)) {
507 return row;
508 }
509
510 Object currentValue = ObjectPropertyUtils.getPropertyValue(obj, ((DataField) item).getPropertyName());
511
512 if (currentValue == null) {
513 currentValue = "";
514 }
515
516
517
518 row = row.replaceAll(VALUE_TOKEN + originalId + VALUE_TOKEN, currentValue.toString());
519 currentColumnValue = currentValue.toString();
520
521 Inquiry dataFieldInquiry = ((DataField) item).getInquiry();
522 if (dataFieldInquiry != null && dataFieldInquiry.getInquiryParameters() != null
523 && dataFieldInquiry.getInquiryLink() != null) {
524
525 String inquiryLinkId = dataFieldInquiry.getInquiryLink().getId().replace(ID_TOKEN, "")
526 + UifConstants.IdSuffixes.LINE + index;
527
528
529 for (String key : dataFieldInquiry.getInquiryParameters().keySet()) {
530 String name = dataFieldInquiry.getInquiryParameters().get(key);
531
532
533 key = key.replace(((DataField) item).getBindingInfo().getBindByNamePrefix() + ".", "");
534
535 if (ObjectPropertyUtils.isReadableProperty(obj, key)) {
536 String value = ObjectPropertyUtils.getPropertyValue(obj, key);
537 row = row.replaceFirst("(" + inquiryLinkId + "(.|\\s)*?" + name + ")=.*?([&|\"])",
538 "$1=" + value + "$3");
539 }
540 }
541 }
542
543 return row;
544 }
545
546
547
548
549
550
551
552
553
554
555
556 protected String handleInputFieldInRow(Component item, Object obj, String row, int index, String originalId) {
557 if (!(item instanceof InputField) || ((InputField) item).getControl() == null) {
558 return row;
559 }
560
561 Control control = ((InputField) item).getControl();
562
563
564
565 row = row.replace("name=\"" + ((InputField) item).getBindingInfo().getBindingPath() + "\"",
566 "name=\"" + this.getBindingInfo().getBindingPath() + "[" + index + "]." + ((InputField) item)
567 .getPropertyName() + "\"");
568
569 Object value = ObjectPropertyUtils.getPropertyValue(obj, ((InputField) item).getPropertyName());
570 String stringValue = "";
571
572 if (value == null) {
573 stringValue = "";
574 } else if (value.getClass().isAssignableFrom(boolean.class)) {
575 stringValue = "" + value;
576 } else if (!(value instanceof Collection)) {
577 stringValue = value.toString();
578 }
579
580 String controlId = originalId + "_line" + index + UifConstants.IdSuffixes.CONTROL;
581
582 if (control instanceof CheckboxControl && stringValue.equalsIgnoreCase("true")) {
583
584 row = row.replaceAll("(id(\\s)*?=(\\s)*?\"" + controlId + "\")", "$1 checked=\"checked\" ");
585 } else if (control instanceof TextControl) {
586
587 row = row.replaceAll("(id(\\s)*?=(\\s)*?\"" + controlId + "\"(.|\\s)*?value=\")(.|\\s)*?\"",
588 "$1" + stringValue + "\"");
589 } else if (control instanceof SelectControl && !((SelectControl) control).isMultiple()) {
590
591 Pattern pattern = Pattern.compile("<select(\\s)*?id(\\s)*?=(\\s)*?\"" + controlId + "\"(.|\\s)*?</select>");
592 Matcher matcher = pattern.matcher(row);
593 String replacement = "";
594
595 if (matcher.find()) {
596
597 String selected = "selected=\"selected\"";
598 replacement = matcher.group().replace(selected, "");
599
600
601 String selectedValue = "value=\"" + stringValue + "\"";
602 replacement = replacement.replace(selectedValue, selectedValue + " " + selected);
603 }
604
605
606 if (StringUtils.isNotBlank(replacement)) {
607 row = matcher.replaceAll(replacement);
608 }
609 }
610
611 currentColumnValue = stringValue;
612
613 return row;
614 }
615
616
617
618
619
620
621 @BeanTagAttribute(name = "propertyName")
622 public String getPropertyName() {
623 return propertyName;
624 }
625
626
627
628
629
630
631 public void setPropertyName(String propertyName) {
632 this.propertyName = propertyName;
633 }
634
635
636
637
638
639
640 @BeanTagAttribute(name = "bindingInfo", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
641 public BindingInfo getBindingInfo() {
642 return bindingInfo;
643 }
644
645
646
647
648
649
650 public void setBindingInfo(BindingInfo bindingInfo) {
651 this.bindingInfo = bindingInfo;
652 }
653
654
655
656
657
658
659 public List<Label> getHeaderLabels() {
660 return headerLabels;
661 }
662
663
664
665
666
667
668 @BeanTagAttribute(name = "richTable", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
669 public RichTable getRichTable() {
670 return richTable;
671 }
672
673
674
675
676
677
678 public void setRichTable(RichTable richTable) {
679 this.richTable = richTable;
680 }
681
682
683
684
685
686
687
688
689
690
691
692
693 @BeanTagAttribute(name = "conditionalRowCssClasses", type = BeanTagAttribute.AttributeType.MAPVALUE)
694 public Map<String, String> getConditionalRowCssClasses() {
695 return conditionalRowCssClasses;
696 }
697
698
699
700
701
702
703 public void setConditionalRowCssClasses(Map<String, String> conditionalRowCssClasses) {
704 this.conditionalRowCssClasses = conditionalRowCssClasses;
705 }
706
707
708
709
710
711
712 public boolean isEmptyTable() {
713 return emptyTable;
714 }
715
716 public void setHeaderLabels(List<Label> headerLabels) {
717 this.headerLabels = headerLabels;
718 }
719
720 public void setExpressionConversionMap(Map<String, String> expressionConversionMap) {
721 this.expressionConversionMap = expressionConversionMap;
722 }
723
724 public Map<String, String> getExpressionConversionMap() {
725 return expressionConversionMap;
726 }
727
728 public List<String> getInitialComponentIds() {
729 return initialComponentIds;
730 }
731
732 public Map<String, String> getRenderIdExpressionMap() {
733 return renderIdExpressionMap;
734 }
735
736 public void setInitialComponentIds(List<String> initialComponentIds) {
737 this.initialComponentIds = initialComponentIds;
738 }
739
740 public void setRenderIdExpressionMap(Map<String, String> renderIdExpressionMap) {
741 this.renderIdExpressionMap = renderIdExpressionMap;
742 }
743
744 public void setEmptyTable(boolean emptyTable) {
745 this.emptyTable = emptyTable;
746 }
747
748
749
750
751
752 @BeanTagAttribute(name = "currentColumnValue")
753 protected String getCurrentColumnValue() {
754 return currentColumnValue;
755 }
756
757
758
759
760
761
762 protected void setCurrentColumnValue(String currentColumnValue) {
763 this.currentColumnValue = currentColumnValue;
764 }
765 }