001 /**
002 * Copyright 2005-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.krad.uif.container;
017
018 import java.util.ArrayList;
019 import java.util.Collection;
020 import java.util.HashMap;
021 import java.util.List;
022 import java.util.Map;
023 import java.util.regex.Matcher;
024 import java.util.regex.Pattern;
025
026 import org.apache.commons.lang.StringUtils;
027 import org.kuali.rice.krad.datadictionary.parse.BeanTag;
028 import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
029 import org.kuali.rice.krad.datadictionary.parse.BeanTags;
030 import org.kuali.rice.krad.uif.UifConstants;
031 import org.kuali.rice.krad.uif.component.BindingInfo;
032 import org.kuali.rice.krad.uif.component.Component;
033 import org.kuali.rice.krad.uif.component.DataBinding;
034 import org.kuali.rice.krad.uif.control.CheckboxControl;
035 import org.kuali.rice.krad.uif.control.Control;
036 import org.kuali.rice.krad.uif.control.SelectControl;
037 import org.kuali.rice.krad.uif.control.TextControl;
038 import org.kuali.rice.krad.uif.element.Action;
039 import org.kuali.rice.krad.uif.element.Image;
040 import org.kuali.rice.krad.uif.element.Label;
041 import org.kuali.rice.krad.uif.element.Link;
042 import org.kuali.rice.krad.uif.element.Message;
043 import org.kuali.rice.krad.uif.field.DataField;
044 import org.kuali.rice.krad.uif.field.Field;
045 import org.kuali.rice.krad.uif.field.FieldGroup;
046 import org.kuali.rice.krad.uif.field.InputField;
047 import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
048 import org.kuali.rice.krad.uif.util.ComponentUtils;
049 import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
050 import org.kuali.rice.krad.uif.view.ExpressionEvaluator;
051 import org.kuali.rice.krad.uif.view.View;
052 import org.kuali.rice.krad.uif.widget.Inquiry;
053 import org.kuali.rice.krad.uif.widget.RichTable;
054 import org.kuali.rice.krad.uif.widget.Tooltip;
055 import org.kuali.rice.krad.util.KRADConstants;
056 import org.kuali.rice.krad.util.KRADUtils;
057 import org.kuali.rice.krad.web.form.UifFormBase;
058
059 /**
060 * LightTable is a light-weight collection table implementation that supports a subset of features,
061 * Current known supported features are:
062 *
063 * <ul>
064 * <li>DataField</li>
065 * <li>InputField with TextControl, CheckboxControl, or single SelectControl</li>
066 * <li>MessageField</li>
067 * <li>LinkField</li>
068 * <li>ActionField</li>
069 * <li>ImageField</li>
070 * <li>most RichTable options</li>
071 * <li>FieldGroup containing only Actions, Image, Messages, or Links</li>
072 * <li>SpringEL for String properties on supported components only</li>
073 * <li>SpringEL specifically for the render property</li>
074 * </ul>
075 *
076 * Other features are not guaranteed to work, but may work at your own risk. The intent of this table is to be a
077 * light-weight alternative to the fully featured table already available in KRAD and it is more suited to displaying
078 * large sets of simple data to the user.
079 *
080 * @author Kuali Rice Team (rice.collab@kuali.org)
081 */
082 @BeanTags({@BeanTag(name = "lightTableGroup-bean", parent = "Uif-LightTableGroup"),
083 @BeanTag(name = "lightTableSection-bean", parent = "Uif-LightTableSection"),
084 @BeanTag(name = "lightTableSubSection-bean", parent = "Uif-LightTableSubSection")})
085 public class LightTable extends Group implements DataBinding {
086 private static final long serialVersionUID = -8930885219866835711L;
087
088 private static final String VALUE_TOKEN = "@v@";
089 private static final String EXPRESSION_TOKEN = "@e@";
090 private static final String RENDER = "render";
091 private static final String ID_TOKEN = "@id@";
092 private static final String A_TOKEN = "@";
093 private static final String ROW_CLASS = "@rowClass@";
094 private static final String SORT_VALUE = "@sortVal";
095 private static final String SEPARATOR = "@@@";
096
097 private String propertyName;
098 private BindingInfo bindingInfo;
099 private List<Label> headerLabels;
100 private RichTable richTable;
101 private Map<String, String> conditionalRowCssClasses;
102
103 private Map<String, String> expressionConversionMap;
104 private List<String> initialComponentIds;
105 private Map<String, String> renderIdExpressionMap;
106 private boolean emptyTable;
107 private String currentColumnValue;
108
109 /**
110 * LightTable constructor
111 */
112 public LightTable() {
113 expressionConversionMap = new HashMap<String, String>();
114 initialComponentIds = new ArrayList<String>();
115 renderIdExpressionMap = new HashMap<String, String>();
116 }
117
118 /**
119 * Initialization override that sets up DataField value placeholders for parsing and populates the
120 * expressionConversionMap
121 */
122 @Override
123 public void performInitialization(Object model) {
124 super.performInitialization(model);
125 richTable.setForceLocalJsonData(true);
126
127 //init binding info
128 if (bindingInfo != null) {
129 bindingInfo.setDefaults(ViewLifecycle.getActiveLifecycle().getView(), getPropertyName());
130 }
131
132 //iterate over this collections items to initialize
133 for (Component item : this.getItems()) {
134 initialComponentIds.add(item.getId());
135
136 //if data field, setup a forced placeholder value
137 if (item instanceof DataField) {
138 ((DataField) item).setForcedValue(VALUE_TOKEN + item.getId() + VALUE_TOKEN);
139 }
140
141 ///populate expression map
142 expressionConversionMap = buildExpressionMap(item, expressionConversionMap);
143 }
144 }
145
146 /**
147 * Builds the expression map which contains "propertyName@@@id" and the expression. Also fills the
148 * renderIdExpressionMap which contains all the component ids and expressions for render conditions, and overrides
149 * ids with a placeholder id. This method is recursive for child components which match certain supported types.
150 *
151 * @param item the item to iterate on
152 * @param expressionMap the map holding the expressions for the items of this collection
153 * @return the expressionMap with expressions populated
154 */
155 protected Map<String, String> buildExpressionMap(Component item, Map<String, String> expressionMap) {
156 if (item == null) {
157 return expressionMap;
158 }
159
160 List<String> toRemove = new ArrayList<String>();
161
162 if (item.getExpressionGraph() != null && !item.getExpressionGraph().isEmpty()) {
163 for (String name : item.getExpressionGraph().keySet()) {
164 processExpression(name, item, expressionMap, toRemove);
165 }
166 }
167
168 //id placeholder
169 item.setId(ID_TOKEN + item.getId() + ID_TOKEN);
170
171 if (item instanceof Group) {
172 ((Group) item).getLayoutManager().setId(ID_TOKEN + ((Group) item).getLayoutManager().getId() + ID_TOKEN);
173 }
174
175 expressionMap = addChildExpressions(item.getComponentsForLifecycle(), expressionMap);
176
177 for (String name : toRemove) {
178 item.getExpressionGraph().remove(name);
179 }
180
181 return expressionMap;
182 }
183
184 /**
185 * Process the expression for the item by putting placeholder values in for String properties and adding markers
186 * for render expressions to the component; adds the original expression to the expressionMap
187 *
188 * @param name the property name
189 * @param item the component this expressio is on
190 * @param expressionMap the map to add expressions to
191 * @param toRemove the property name is added this map to be removed later
192 */
193 public void processExpression(String name, Component item, Map<String, String> expressionMap,
194 List<String> toRemove) {
195 Class<?> clazz = ObjectPropertyUtils.getPropertyType(item, name);
196 if (clazz == null) {
197 return;
198 }
199
200 if (clazz.isAssignableFrom(String.class)) {
201 //add expressions for string properties only
202 expressionMap.put(name + SEPARATOR + item.getId(), item.getExpressionGraph().get(name));
203 toRemove.add(name);
204 ObjectPropertyUtils.setPropertyValue(item, name,
205 EXPRESSION_TOKEN + name + SEPARATOR + item.getId() + EXPRESSION_TOKEN);
206
207 } else if (name.endsWith(RENDER) && clazz.isAssignableFrom(boolean.class)) {
208 //setup render tokens to be able to determine where to remove content for render false, if needed
209 Component renderComponent = item;
210
211 //check for nested render (child element)
212 if (!name.equals(RENDER)) {
213 renderComponent = ObjectPropertyUtils.getPropertyValue(item, StringUtils.removeEnd(name, ".render"));
214 }
215
216 //add render expression to the map
217 renderIdExpressionMap.put(renderComponent.getId(), item.getExpressionGraph().get(name));
218 toRemove.add(name);
219
220 String renderMarker = A_TOKEN + RENDER + A_TOKEN + renderComponent.getId() + A_TOKEN;
221
222 //setup pre render content token
223 String pre = renderComponent.getPreRenderContent() == null ? "" : renderComponent.getPreRenderContent();
224 renderComponent.setPreRenderContent(renderMarker + pre);
225
226 //setup post render content token
227 String post = renderComponent.getPostRenderContent() == null ? "" : renderComponent.getPostRenderContent();
228 renderComponent.setPostRenderContent(post + renderMarker);
229
230 //force render to true
231 ObjectPropertyUtils.setPropertyValue(item, name, true);
232 }
233 }
234
235 /**
236 * Add expressions to the expression map for nested components of specific types
237 *
238 * @param components the child components
239 * @param expressionMap the map to add expressions to
240 * @return the map with child component expressions added
241 */
242 protected Map<String, String> addChildExpressions(List<? extends Component> components,
243 Map<String, String> expressionMap) {
244 for (Component comp : components) {
245 if (comp != null && (comp instanceof Action
246 || comp instanceof Image
247 || comp instanceof Message
248 || comp instanceof Link
249 || comp instanceof Inquiry
250 || comp instanceof Group
251 || comp instanceof Tooltip
252 || comp instanceof InputField
253 || comp instanceof CheckboxControl
254 || comp instanceof TextControl
255 || comp instanceof SelectControl)) {
256 expressionMap = buildExpressionMap(comp, expressionMap);
257 }
258 }
259
260 return expressionMap;
261 }
262
263 /**
264 * performFinalize override corrects the binding path for the DataFields and turns off rendering on some components
265 */
266 @Override
267 public void performFinalize(Object model, Component parent) {
268 super.performFinalize(model, parent);
269
270 headerLabels = new ArrayList<Label>();
271 for (Component item : this.getItems()) {
272 //get the header labels
273 if (item instanceof Field) {
274 headerLabels.add(ComponentUtils.copy(((Field) item).getFieldLabel()));
275 ((Field) item).getFieldLabel().setRender(false);
276 } else {
277 headerLabels.add(null);
278 }
279
280 if (item instanceof FieldGroup) {
281 ((FieldGroup) item).getGroup().setValidationMessages(null);
282
283 }
284
285 if (item instanceof DataField) {
286 ((DataField) item).getBindingInfo().setBindByNamePrefix(this.getBindingInfo().getBindingPath() + "[0]");
287 }
288 }
289
290 Object collectionValue = ObjectPropertyUtils.getPropertyValue(model, bindingInfo.getBindingPath());
291
292 //set emptyTable true if null, empty, or not valid collection
293 if (collectionValue == null || !(collectionValue instanceof Collection) ||
294 ((Collection<?>) collectionValue).isEmpty()) {
295 emptyTable = true;
296 }
297 }
298
299 /**
300 * @see org.kuali.rice.krad.uif.component.Component#getComponentsForLifecycle()
301 */
302 @Override
303 public List<Component> getComponentsForLifecycle() {
304 List<Component> components = super.getComponentsForLifecycle();
305
306 components.add(richTable);
307 return components;
308 }
309
310 /**
311 * Build the rows from the rowTemplate passed in. This method uses regex to locate pieces of the row that need
312 * to be replaced with row specific content per row.
313 *
314 * @param view the view instance the table is being built within
315 * @param rowTemplate the first row of the collection in html generated from the ftl
316 * @param model the model
317 * @return the full set of rows for the table in html(String) to be used by the calling ftl
318 */
319 public void buildRows(View view, String rowTemplate, UifFormBase model) {
320 if (StringUtils.isBlank(rowTemplate)) {
321 return;
322 }
323
324 rowTemplate = StringUtils.removeEnd(rowTemplate, ",");
325 rowTemplate = rowTemplate.replace("\n", "");
326 rowTemplate = rowTemplate.replace("\r", "");
327
328 StringBuffer rows = new StringBuffer();
329 List<Object> collectionObjects = ObjectPropertyUtils.getPropertyValue(model, bindingInfo.getBindingPath());
330
331 //uncheck any checked checkboxes globally for this row
332 rowTemplate = rowTemplate.replace("checked=\"checked\"", "");
333
334 //init token patterns
335 Pattern idPattern = Pattern.compile(ID_TOKEN + "(.*?)" + ID_TOKEN);
336 Pattern expressionPattern = Pattern.compile(EXPRESSION_TOKEN + "(.*?)" + EXPRESSION_TOKEN);
337
338 ExpressionEvaluator expressionEvaluator = view.getViewHelperService().getExpressionEvaluator();
339 expressionEvaluator.initializeEvaluationContext(model);
340
341 int lineIndex = 0;
342 for (Object obj : collectionObjects) {
343 //add line index to all ids
344 String row = idPattern.matcher(rowTemplate).replaceAll("$1" + UifConstants.IdSuffixes.LINE + lineIndex);
345
346 //create the expanded context
347 Map<String, Object> expandedContext = new HashMap<String, Object>();
348 expandedContext.put(UifConstants.ContextVariableNames.LINE, obj);
349 expandedContext.put(UifConstants.ContextVariableNames.INDEX, lineIndex);
350 expandedContext.put(UifConstants.ContextVariableNames.VIEW, view);
351
352 currentColumnValue = "";
353
354 int itemIndex = 0;
355 for (Component item : this.getItems()) {
356 //determine original id for this component
357 String originalId = initialComponentIds.get(itemIndex);
358
359 //special DataField handling
360 row = handleDataFieldInRow(item, obj, row, lineIndex, originalId);
361
362 //special InputField handling
363 row = handleInputFieldInRow(item, obj, row, lineIndex, originalId);
364
365 //add item context
366 if (item.getContext() != null) {
367 expandedContext.putAll(item.getContext());
368 }
369
370 //evaluate expressions found by the pattern
371 row = evaluateAndReplaceExpressionValues(row, lineIndex, model, expandedContext, expressionPattern,
372 expressionEvaluator);
373
374 if (currentColumnValue == null) {
375 currentColumnValue = "";
376 }
377
378 row = row.replace(SORT_VALUE + itemIndex + A_TOKEN, currentColumnValue);
379
380 itemIndex++;
381 }
382
383 // get rowCss class
384 boolean isOdd = lineIndex % 2 == 0;
385 String rowCss = KRADUtils.generateRowCssClassString(conditionalRowCssClasses, lineIndex, isOdd,
386 expandedContext, expressionEvaluator);
387
388 row = row.replace("\"", "\\\"");
389 row = row.replace(ROW_CLASS, rowCss);
390 row = "{" + row + "},";
391
392 //special render property expression handling
393 row = evaluateRenderExpressions(row, lineIndex, model, expandedContext, expressionEvaluator);
394
395 //append row
396 rows.append(row);
397 lineIndex++;
398 }
399
400 StringBuffer tableToolsColumnOptions = new StringBuffer("[");
401 for (int index = 0; index < this.getItems().size(); index++) {
402 String colOptions = richTable.constructTableColumnOptions(index, true, false, String.class, null);
403 tableToolsColumnOptions.append(colOptions + " , ");
404 }
405
406 String aoColumnDefs = StringUtils.removeEnd(tableToolsColumnOptions.toString(), " , ") + "]";
407 Map<String, String> rtTemplateOptions = richTable.getTemplateOptions();
408
409 if (rtTemplateOptions == null) {
410 richTable.setTemplateOptions(rtTemplateOptions = new HashMap<String, String>());
411 }
412
413 rtTemplateOptions.put(UifConstants.TableToolsKeys.AO_COLUMN_DEFS, aoColumnDefs);
414
415 // construct aaData option to set data in dataTable options (speed enhancement)
416 String aaData = StringUtils.removeEnd(rows.toString(), ",");
417 aaData = "[" + aaData + "]";
418 aaData = aaData.replace(KRADConstants.QUOTE_PLACEHOLDER, "\"");
419
420 //set the aaData option on datatable for faster rendering
421 rtTemplateOptions.put(UifConstants.TableToolsKeys.AA_DATA, aaData);
422
423 //make sure deferred rendering is forced whether set or not
424 rtTemplateOptions.put(UifConstants.TableToolsKeys.DEFER_RENDER,
425 UifConstants.TableToolsValues.TRUE);
426 }
427
428 /**
429 * Evaluate expressions and replace content found by the expressionPattern in the row
430 *
431 * @param row the row being modified
432 * @param index the line index
433 * @param model the model
434 * @param expandedContext the context to evaluate expressions against
435 * @param expressionPattern the expression pattern used to find expression tokens for value replacement
436 * @param expressionEvaluator the expression service to use for evaluation
437 * @return the modified row
438 */
439 protected String evaluateAndReplaceExpressionValues(String row, int index, Object model,
440 Map<String, Object> expandedContext, Pattern expressionPattern, ExpressionEvaluator expressionEvaluator) {
441
442 Matcher matcher = expressionPattern.matcher(row);
443
444 while (matcher.find()) {
445 String matchingGroup = matcher.group(1);
446 String expression = expressionConversionMap.get(matchingGroup);
447
448 //adjust prefix for evaluation
449 expression = expression.replace(UifConstants.LINE_PATH_BIND_ADJUST_PREFIX,
450 this.getBindingInfo().getBindingPath() + "[" + index + "].");
451
452 //get expression result
453 Object value = expressionEvaluator.evaluateExpressionTemplate(expandedContext, expression);
454
455 if (value != null) {
456 row = row.replace(matcher.group(), value.toString());
457 } else {
458 row = row.replace(matcher.group(), "");
459 }
460 }
461
462 return row;
463 }
464
465 /**
466 * Evaluates the render expressions for the row and removes the content if render is evaluated false
467 *
468 * @param row the row being modified
469 * @param index the line index
470 * @param model the model
471 * @param expandedContext the context to evaluate expressions against
472 * @param expressionEvaluator the expression service to use for evaluation
473 * @return the modified row
474 */
475 protected String evaluateRenderExpressions(String row, int index, Object model, Map<String, Object> expandedContext,
476 ExpressionEvaluator expressionEvaluator) {
477 for (String id : renderIdExpressionMap.keySet()) {
478 String expression = renderIdExpressionMap.get(id);
479
480 //adjust prefix for evaluation
481 expression = expression.replace(UifConstants.LINE_PATH_BIND_ADJUST_PREFIX,
482 this.getBindingInfo().getBindingPath() + "[" + index + "].");
483
484 //get expression result
485 Object value = expressionEvaluator.evaluateExpressionTemplate(expandedContext, expression);
486
487 String wrap = A_TOKEN + RENDER + A_TOKEN + id + A_TOKEN;
488
489 if (value != null && value instanceof String && Boolean.parseBoolean((String) value) == false) {
490 //do not render this component - remove content between render wrappers
491 row = row.replaceAll(wrap + "(.|\\s)*?" + wrap, "");
492 } else {
493 //remove render wrappers only - keep content
494 row = row.replaceAll(wrap, "");
495 }
496 }
497
498 return row;
499 }
500
501 /**
502 * Special handling of the DataField in the row, replaces necessary content with row specific content
503 *
504 * @param item the item being processed
505 * @param obj the row's object model
506 * @param row the row in html
507 * @param index the current row index
508 * @param originalId the original id of the component item
509 * @return the updated row
510 */
511 protected String handleDataFieldInRow(Component item, Object obj, String row, int index, String originalId) {
512 if (!(item instanceof DataField)) {
513 return row;
514 }
515
516 Object currentValue = ObjectPropertyUtils.getPropertyValue(obj, ((DataField) item).getPropertyName());
517
518 if (currentValue == null) {
519 currentValue = "";
520 }
521
522 //for readOnly DataFields replace the value marked with the value on the current object
523 row = row.replaceAll(VALUE_TOKEN + originalId + VALUE_TOKEN, currentValue.toString());
524 currentColumnValue = currentValue.toString();
525
526 if (((DataField) item).getInquiry() != null
527 && ((DataField) item).getInquiry().getInquiryParameters() != null
528 && ((DataField) item).getInquiry().getInquiryLink() != null) {
529
530 String inquiryLinkId = ((DataField) item).getInquiry().getInquiryLink().getBaseId().replace(ID_TOKEN, "")
531 + UifConstants.IdSuffixes.LINE
532 + index;
533
534 //process each Inquiry link parameter by replacing each in the inquiry url with their
535 //current value
536 for (String key : ((DataField) item).getInquiry().getInquiryParameters().keySet()) {
537 String name = ((DataField) item).getInquiry().getInquiryParameters().get(key);
538
539 //omit the binding prefix fromt he key to get the path relative to the current object
540 key = key.replace(((DataField) item).getBindingInfo().getBindByNamePrefix() + ".", "");
541
542 if (ObjectPropertyUtils.isReadableProperty(obj, key)) {
543 String value = ObjectPropertyUtils.getPropertyValue(obj, key);
544 row = row.replaceFirst("(" + inquiryLinkId + "(.|\\s)*?" + name + ")=.*?([&|\"])",
545 "$1=" + value + "$3");
546 }
547
548 }
549 }
550
551 return row;
552 }
553
554 /**
555 * Special handling of the InputField in the row, replaces necessary content with row specific content
556 *
557 * @param item the item being processed
558 * @param obj the row's object model
559 * @param row the row in html
560 * @param index the current row index
561 * @param originalId the original id of the component item
562 * @return the updated row
563 */
564 protected String handleInputFieldInRow(Component item, Object obj, String row, int index, String originalId) {
565 if (!(item instanceof InputField) || ((InputField) item).getControl() == null) {
566 return row;
567 }
568
569 Control control = ((InputField) item).getControl();
570
571 //updates the name path to the current path for any instance this item's propertyName with
572 //a collection binding prefix
573 row = row.replace("name=\"" + ((InputField) item).getBindingInfo().getBindingPath() + "\"",
574 "name=\"" + this.getBindingInfo().getBindingPath() + "[" + index + "]." + ((InputField) item)
575 .getPropertyName() + "\"");
576
577 Object value = ObjectPropertyUtils.getPropertyValue(obj, ((InputField) item).getPropertyName());
578 String stringValue = "";
579
580 if (value == null) {
581 stringValue = "";
582 } else if (value.getClass().isAssignableFrom(boolean.class)) {
583 stringValue = "" + value;
584 } else if (!(value instanceof Collection)) {
585 stringValue = value.toString();
586 }
587
588 String controlId = originalId + "_line" + index + UifConstants.IdSuffixes.CONTROL;
589
590 if (control instanceof CheckboxControl && stringValue.equalsIgnoreCase("true")) {
591 //CheckboxControl handling - only replace if true with a checked attribute appended
592 row = row.replaceAll("(id(\\s)*?=(\\s)*?\"" + controlId + "\")", "$1 checked=\"checked\" ");
593 } else if (control instanceof TextControl) {
594 //TextControl handling - replace with
595 row = row.replaceAll("(id(\\s)*?=(\\s)*?\"" + controlId + "\"(.|\\s)*?value=\")(.|\\s)*?\"",
596 "$1" + stringValue + "\"");
597 } else if (control instanceof SelectControl && !((SelectControl) control).isMultiple()) {
598 //SelectControl handling (single item only)
599 Pattern pattern = Pattern.compile("<select(\\s)*?id(\\s)*?=(\\s)*?\"" + controlId + "\"(.|\\s)*?</select>");
600 Matcher matcher = pattern.matcher(row);
601 String replacement = "";
602
603 if (matcher.find()) {
604 //remove selected from select options
605 String selected = "selected=\"selected\"";
606 replacement = matcher.group().replace(selected, "");
607
608 //put selected on only the selected option
609 String selectedValue = "value=\"" + stringValue + "\"";
610 replacement = replacement.replace(selectedValue, selectedValue + " " + selected);
611 }
612
613 //replace the old select tag with the old one
614 if (StringUtils.isNotBlank(replacement)) {
615 row = matcher.replaceAll(replacement);
616 }
617 }
618
619 currentColumnValue = stringValue;
620
621 return row;
622 }
623
624 /**
625 * The propertyName of the list backing this collection
626 *
627 * @return the propertyName of this collection
628 */
629 @BeanTagAttribute(name = "propertyName")
630 public String getPropertyName() {
631 return propertyName;
632 }
633
634 /**
635 * Set the propertyName
636 *
637 * @param propertyName
638 */
639 public void setPropertyName(String propertyName) {
640 this.propertyName = propertyName;
641 }
642
643 /**
644 * The bindingInfo for this collection table, containg the property path and other options
645 *
646 * @return the bindingInfo
647 */
648 @BeanTagAttribute(name = "bindingInfo", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
649 public BindingInfo getBindingInfo() {
650 return bindingInfo;
651 }
652
653 /**
654 * Set the bindingInfo
655 *
656 * @param bindingInfo
657 */
658 public void setBindingInfo(BindingInfo bindingInfo) {
659 this.bindingInfo = bindingInfo;
660 }
661
662 /**
663 * The labels for the header derived from the items of this collection (the fields)
664 *
665 * @return the header labels
666 */
667 public List<Label> getHeaderLabels() {
668 return headerLabels;
669 }
670
671 /**
672 * The richTable widget definition for this table for setting dataTable javascript options
673 *
674 * @return the RichTable widget
675 */
676 @BeanTagAttribute(name = "richTable", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
677 public RichTable getRichTable() {
678 return richTable;
679 }
680
681 /**
682 * Set the richTable widget
683 *
684 * @param richTable
685 */
686 public void setRichTable(RichTable richTable) {
687 this.richTable = richTable;
688 }
689
690 /**
691 * The row css classes for the rows of this layout
692 *
693 * <p>To set a css class on all rows, use "all" as a key. To set a
694 * class for even rows, use "even" as a key, for odd rows, use "odd".
695 * Use a one-based index to target a specific row by index. SpringEL can be
696 * used as a key and the expression will be evaluated; if evaluated to true, the
697 * class(es) specified will be applied.</p>
698 *
699 * @return a map which represents the css classes of the rows of this layout
700 */
701 @BeanTagAttribute(name = "conditionalRowCssClasses", type = BeanTagAttribute.AttributeType.MAPVALUE)
702 public Map<String, String> getConditionalRowCssClasses() {
703 return conditionalRowCssClasses;
704 }
705
706 /**
707 * Set the conditionalRowCssClasses
708 *
709 * @param conditionalRowCssClasses
710 */
711 public void setConditionalRowCssClasses(Map<String, String> conditionalRowCssClasses) {
712 this.conditionalRowCssClasses = conditionalRowCssClasses;
713 }
714
715 /**
716 * True if this table is empty, false otherwise
717 *
718 * @return true if the collection backing this table is empty
719 */
720 public boolean isEmptyTable() {
721 return emptyTable;
722 }
723
724 public void setHeaderLabels(List<Label> headerLabels) {
725 this.headerLabels = headerLabels;
726 }
727
728 public void setExpressionConversionMap(Map<String, String> expressionConversionMap) {
729 this.expressionConversionMap = expressionConversionMap;
730 }
731
732 public Map<String, String> getExpressionConversionMap() {
733 return expressionConversionMap;
734 }
735
736 public List<String> getInitialComponentIds() {
737 return initialComponentIds;
738 }
739
740 public Map<String, String> getRenderIdExpressionMap() {
741 return renderIdExpressionMap;
742 }
743
744 public void setInitialComponentIds(List<String> initialComponentIds) {
745 this.initialComponentIds = initialComponentIds;
746 }
747
748 public void setRenderIdExpressionMap(Map<String, String> renderIdExpressionMap) {
749 this.renderIdExpressionMap = renderIdExpressionMap;
750 }
751
752 public void setEmptyTable(boolean emptyTable) {
753 this.emptyTable = emptyTable;
754 }
755
756 /**
757 *
758 * @return the current column value
759 */
760 @BeanTagAttribute(name = "currentColumnValue")
761 protected String getCurrentColumnValue() {
762 return currentColumnValue;
763 }
764
765 /**
766 * Set the current column value
767 *
768 * @param currentColumnValue
769 */
770 protected void setCurrentColumnValue(String currentColumnValue) {
771 this.currentColumnValue = currentColumnValue;
772 }
773
774 /**
775 * @see org.kuali.rice.krad.datadictionary.DictionaryBeanBase#copyProperties(Object)
776 */
777 @Override
778 protected <T> void copyProperties(T component) {
779 super.copyProperties(component);
780
781 LightTable lightTableCopy = (LightTable) component;
782
783 lightTableCopy.setPropertyName(this.propertyName);
784
785 if (this.bindingInfo != null) {
786 lightTableCopy.setBindingInfo((BindingInfo) this.bindingInfo.copy());
787 }
788
789 if (headerLabels != null) {
790 List<Label> headerLabelsCopy = ComponentUtils.copy(headerLabels);
791 lightTableCopy.setHeaderLabels(headerLabelsCopy);
792 }
793
794 if (this.richTable != null) {
795 lightTableCopy.setRichTable((RichTable) this.richTable.copy());
796 }
797
798 if (expressionConversionMap != null) {
799 lightTableCopy.setExpressionConversionMap(new HashMap<String, String>(expressionConversionMap));
800 }
801
802 if (renderIdExpressionMap != null) {
803 lightTableCopy.setRenderIdExpressionMap(new HashMap<String, String>(renderIdExpressionMap));
804 }
805
806 if (initialComponentIds != null) {
807 lightTableCopy.setInitialComponentIds(new ArrayList<String>(initialComponentIds));
808 }
809
810 if (this.conditionalRowCssClasses != null) {
811 lightTableCopy.setConditionalRowCssClasses(new HashMap<String, String>(this.conditionalRowCssClasses));
812 }
813
814 lightTableCopy.setEmptyTable(this.emptyTable);
815 lightTableCopy.setCurrentColumnValue(this.currentColumnValue);
816 }
817 }