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 = "lightTable", parent = "Uif-LightTableGroup"),
85 @BeanTag(name = "lightTableSection", parent = "Uif-LightTableSection"),
86 @BeanTag(name = "lightTableSubSection", 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 if (item instanceof InputField) {
296 ViewLifecycle.getViewPostMetadata().addAccessibleBindingPath(this.getBindingInfo().getBindingPath() + "[*]." + ((DataField) item).getPropertyName());
297 }
298 }
299
300 Object collectionValue = ObjectPropertyUtils.getPropertyValue(model, bindingInfo.getBindingPath());
301
302
303 if (collectionValue == null || !(collectionValue instanceof Collection) ||
304 ((Collection<?>) collectionValue).isEmpty()) {
305 emptyTable = true;
306 }
307 }
308
309
310
311
312
313
314
315
316
317 public void buildRows(View view, String rowTemplate, UifFormBase model) {
318 if (StringUtils.isBlank(rowTemplate)) {
319 return;
320 }
321
322 rowTemplate = StringUtils.removeEnd(rowTemplate, ",");
323 rowTemplate = rowTemplate.replace("\n", "");
324 rowTemplate = rowTemplate.replace("\r", "");
325
326 StringBuffer rows = new StringBuffer();
327 List<Object> collectionObjects = ObjectPropertyUtils.getPropertyValue(model, bindingInfo.getBindingPath());
328
329
330 rowTemplate = rowTemplate.replace("checked=\"checked\"", "");
331
332
333 Pattern idPattern = Pattern.compile(ID_TOKEN + "(.*?)" + ID_TOKEN);
334 Pattern expressionPattern = Pattern.compile(EXPRESSION_TOKEN + "(.*?)" + EXPRESSION_TOKEN);
335
336 ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator();
337 expressionEvaluator.initializeEvaluationContext(model);
338
339 int lineIndex = 0;
340 for (Object obj : collectionObjects) {
341
342 String row = idPattern.matcher(rowTemplate).replaceAll("$1" + UifConstants.IdSuffixes.LINE + lineIndex);
343
344
345 Map<String, Object> expandedContext = new HashMap<String, Object>();
346 expandedContext.put(UifConstants.ContextVariableNames.LINE, obj);
347 expandedContext.put(UifConstants.ContextVariableNames.INDEX, lineIndex);
348 expandedContext.put(UifConstants.ContextVariableNames.VIEW, view);
349
350 currentColumnValue = "";
351
352 int itemIndex = 0;
353 for (Component item : this.getItems()) {
354
355 String originalId = initialComponentIds.get(itemIndex);
356
357
358 row = handleDataFieldInRow(item, obj, row, lineIndex, originalId);
359
360
361 row = handleInputFieldInRow(item, obj, row, lineIndex, originalId);
362
363
364 if (item.getContext() != null) {
365 expandedContext.putAll(item.getContext());
366 }
367
368
369 row = evaluateAndReplaceExpressionValues(row, lineIndex, model, expandedContext, expressionPattern,
370 expressionEvaluator);
371
372 if (currentColumnValue == null) {
373 currentColumnValue = "";
374 }
375
376 row = row.replace(SORT_VALUE + itemIndex + A_TOKEN, currentColumnValue);
377
378 itemIndex++;
379 }
380
381
382 boolean isOdd = lineIndex % 2 == 0;
383 String rowCss = KRADUtils.generateRowCssClassString(conditionalRowCssClasses, lineIndex, isOdd,
384 expandedContext, expressionEvaluator);
385
386 row = row.replace("\"", "\\\"");
387 row = row.replace(ROW_CLASS, rowCss);
388 row = "{" + row + "},";
389
390
391 row = evaluateRenderExpressions(row, lineIndex, model, expandedContext, expressionEvaluator);
392
393
394 rows.append(row);
395 lineIndex++;
396 }
397
398 StringBuffer tableToolsColumnOptions = new StringBuffer("[");
399 for (int index = 0; index < this.getItems().size(); index++) {
400 String colOptions = richTable.constructTableColumnOptions(index, true, false, String.class, null);
401 tableToolsColumnOptions.append(colOptions + " , ");
402 }
403
404 String aoColumnDefs = StringUtils.removeEnd(tableToolsColumnOptions.toString(), " , ") + "]";
405 Map<String, String> rtTemplateOptions = richTable.getTemplateOptions();
406
407 if (rtTemplateOptions == null) {
408 richTable.setTemplateOptions(rtTemplateOptions = new HashMap<String, String>());
409 }
410
411 rtTemplateOptions.put(UifConstants.TableToolsKeys.AO_COLUMN_DEFS, aoColumnDefs);
412
413
414 String aaData = StringUtils.removeEnd(rows.toString(), ",");
415 aaData = "[" + aaData + "]";
416 aaData = aaData.replace(KRADConstants.QUOTE_PLACEHOLDER, "\"");
417
418
419 rtTemplateOptions.put(UifConstants.TableToolsKeys.AA_DATA, aaData);
420
421
422 rtTemplateOptions.put(UifConstants.TableToolsKeys.DEFER_RENDER,
423 UifConstants.TableToolsValues.TRUE);
424 }
425
426
427
428
429
430
431
432
433
434
435
436
437 protected String evaluateAndReplaceExpressionValues(String row, int index, Object model,
438 Map<String, Object> expandedContext, Pattern expressionPattern, ExpressionEvaluator expressionEvaluator) {
439
440 Matcher matcher = expressionPattern.matcher(row);
441
442 while (matcher.find()) {
443 String matchingGroup = matcher.group(1);
444 String expression = expressionConversionMap.get(matchingGroup);
445
446
447 expression = expression.replace(UifConstants.LINE_PATH_BIND_ADJUST_PREFIX,
448 this.getBindingInfo().getBindingPath() + "[" + index + "].");
449
450
451 Object value = expressionEvaluator.evaluateExpressionTemplate(expandedContext, expression);
452
453 if (value != null) {
454 row = row.replace(matcher.group(), value.toString());
455 } else {
456 row = row.replace(matcher.group(), "");
457 }
458 }
459
460 return row;
461 }
462
463
464
465
466
467
468
469
470
471
472
473 protected String evaluateRenderExpressions(String row, int index, Object model, Map<String, Object> expandedContext,
474 ExpressionEvaluator expressionEvaluator) {
475 for (String id : renderIdExpressionMap.keySet()) {
476 String expression = renderIdExpressionMap.get(id);
477
478
479 expression = expression.replace(UifConstants.LINE_PATH_BIND_ADJUST_PREFIX,
480 this.getBindingInfo().getBindingPath() + "[" + index + "].");
481
482
483 Object value = expressionEvaluator.evaluateExpressionTemplate(expandedContext, expression);
484
485 String wrap = A_TOKEN + RENDER + A_TOKEN + id + A_TOKEN;
486
487 if (value != null && value instanceof String && Boolean.parseBoolean((String) value) == false) {
488
489 row = row.replaceAll(wrap + "(.|\\s)*?" + wrap, "");
490 } else {
491
492 row = row.replaceAll(wrap, "");
493 }
494 }
495
496 return row;
497 }
498
499
500
501
502
503
504
505
506
507
508
509 protected String handleDataFieldInRow(Component item, Object obj, String row, int index, String originalId) {
510 if (!(item instanceof DataField)) {
511 return row;
512 }
513
514 String currentValue = ObjectPropertyUtils.getPropertyValueAsText(obj, ((DataField) item).getPropertyName());
515
516 if (currentValue == null) {
517 currentValue = "";
518 }
519
520
521 row = row.replaceAll(VALUE_TOKEN + originalId + VALUE_TOKEN, currentValue);
522 currentColumnValue = currentValue;
523
524 Inquiry dataFieldInquiry = ((DataField) item).getInquiry();
525 if (dataFieldInquiry != null && dataFieldInquiry.getInquiryParameters() != null
526 && dataFieldInquiry.getInquiryLink() != null) {
527
528 String inquiryLinkId = dataFieldInquiry.getInquiryLink().getId().replace(ID_TOKEN, "")
529 + UifConstants.IdSuffixes.LINE + index;
530
531
532 for (String key : dataFieldInquiry.getInquiryParameters().keySet()) {
533 String name = dataFieldInquiry.getInquiryParameters().get(key);
534
535
536 key = key.replace(((DataField) item).getBindingInfo().getBindByNamePrefix() + ".", "");
537
538 if (ObjectPropertyUtils.isReadableProperty(obj, key)) {
539 String value = ObjectPropertyUtils.getPropertyValueAsText(obj, key);
540 row = row.replaceFirst("(" + inquiryLinkId + "(.|\\s)*?" + name + ")=.*?([&|\"])",
541 "$1=" + value + "$3");
542 }
543 }
544 }
545
546 return row;
547 }
548
549
550
551
552
553
554
555
556
557
558
559 protected String handleInputFieldInRow(Component item, Object obj, String row, int index, String originalId) {
560 if (!(item instanceof InputField) || ((InputField) item).getControl() == null) {
561 return row;
562 }
563
564 Control control = ((InputField) item).getControl();
565
566
567
568 row = row.replace("name=\"" + ((InputField) item).getBindingInfo().getBindingPath() + "\"",
569 "name=\"" + this.getBindingInfo().getBindingPath() + "[" + index + "]." + ((InputField) item)
570 .getPropertyName() + "\"");
571
572 Object value = ObjectPropertyUtils.getPropertyValue(obj, ((InputField) item).getPropertyName());
573 String stringValue = "";
574
575 if (value == null) {
576 stringValue = "";
577 } else if (value.getClass().isAssignableFrom(boolean.class)) {
578 stringValue = "" + value;
579 } else if (!(value instanceof Collection)) {
580 stringValue = ObjectPropertyUtils.getPropertyValueAsText(obj, ((InputField) item).getPropertyName());
581 }
582
583 String controlId = originalId + "_line" + index + UifConstants.IdSuffixes.CONTROL;
584
585 if (control instanceof CheckboxControl && stringValue.equalsIgnoreCase("true")) {
586
587 row = row.replaceAll("(id(\\s)*?=(\\s)*?\"" + controlId + "\")", "$1 checked=\"checked\" ");
588 } else if (control instanceof TextControl) {
589
590 row = row.replaceAll("(id(\\s)*?=(\\s)*?\"" + controlId + "\"(.|\\s)*?value=\")(.|\\s)*?\"",
591 "$1" + stringValue + "\"");
592 } else if (control instanceof SelectControl && !((SelectControl) control).isMultiple()) {
593
594 Pattern pattern = Pattern.compile("<select(\\s)*?id(\\s)*?=(\\s)*?\"" + controlId + "\"(.|\\s)*?</select>");
595 Matcher matcher = pattern.matcher(row);
596 String replacement = "";
597
598 if (matcher.find()) {
599
600 String selected = "selected=\"selected\"";
601 replacement = matcher.group().replace(selected, "");
602
603
604 String selectedValue = "value=\"" + stringValue + "\"";
605 replacement = replacement.replace(selectedValue, selectedValue + " " + selected);
606 }
607
608
609 if (StringUtils.isNotBlank(replacement)) {
610 row = matcher.replaceAll(replacement);
611 }
612 }
613
614 currentColumnValue = stringValue;
615
616 return row;
617 }
618
619
620
621
622
623
624 @BeanTagAttribute
625 public String getPropertyName() {
626 return propertyName;
627 }
628
629
630
631
632
633
634 public void setPropertyName(String propertyName) {
635 this.propertyName = propertyName;
636 }
637
638
639
640
641
642
643 @BeanTagAttribute
644 public BindingInfo getBindingInfo() {
645 return bindingInfo;
646 }
647
648
649
650
651
652
653 public void setBindingInfo(BindingInfo bindingInfo) {
654 this.bindingInfo = bindingInfo;
655 }
656
657
658
659
660
661
662 public List<Label> getHeaderLabels() {
663 return headerLabels;
664 }
665
666
667
668
669
670
671 @BeanTagAttribute
672 public RichTable getRichTable() {
673 return richTable;
674 }
675
676
677
678
679
680
681 public void setRichTable(RichTable richTable) {
682 this.richTable = richTable;
683 }
684
685
686
687
688
689
690
691
692
693
694
695
696 @BeanTagAttribute
697 public Map<String, String> getConditionalRowCssClasses() {
698 return conditionalRowCssClasses;
699 }
700
701
702
703
704
705
706 public void setConditionalRowCssClasses(Map<String, String> conditionalRowCssClasses) {
707 this.conditionalRowCssClasses = conditionalRowCssClasses;
708 }
709
710
711
712
713
714
715 public boolean isEmptyTable() {
716 return emptyTable;
717 }
718
719 public void setHeaderLabels(List<Label> headerLabels) {
720 this.headerLabels = headerLabels;
721 }
722
723 public void setExpressionConversionMap(Map<String, String> expressionConversionMap) {
724 this.expressionConversionMap = expressionConversionMap;
725 }
726
727 public Map<String, String> getExpressionConversionMap() {
728 return expressionConversionMap;
729 }
730
731 public List<String> getInitialComponentIds() {
732 return initialComponentIds;
733 }
734
735 public Map<String, String> getRenderIdExpressionMap() {
736 return renderIdExpressionMap;
737 }
738
739 public void setInitialComponentIds(List<String> initialComponentIds) {
740 this.initialComponentIds = initialComponentIds;
741 }
742
743 public void setRenderIdExpressionMap(Map<String, String> renderIdExpressionMap) {
744 this.renderIdExpressionMap = renderIdExpressionMap;
745 }
746
747 public void setEmptyTable(boolean emptyTable) {
748 this.emptyTable = emptyTable;
749 }
750
751
752
753
754
755 @BeanTagAttribute(name = "currentColumnValue")
756 protected String getCurrentColumnValue() {
757 return currentColumnValue;
758 }
759
760
761
762
763
764
765 protected void setCurrentColumnValue(String currentColumnValue) {
766 this.currentColumnValue = currentColumnValue;
767 }
768 }