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 }