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 }