001 /** 002 * Copyright 2005-2014 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.layout; 017 018 import java.util.ArrayList; 019 import java.util.Arrays; 020 import java.util.Collections; 021 import java.util.HashMap; 022 import java.util.List; 023 import java.util.Map; 024 import java.util.Set; 025 import java.util.TreeMap; 026 027 import org.apache.commons.lang.StringUtils; 028 import org.kuali.rice.krad.datadictionary.parse.BeanTag; 029 import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 030 import org.kuali.rice.krad.datadictionary.validator.ValidationTrace; 031 import org.kuali.rice.krad.uif.CssConstants; 032 import org.kuali.rice.krad.uif.UifConstants; 033 import org.kuali.rice.krad.uif.UifParameters; 034 import org.kuali.rice.krad.uif.UifPropertyPaths; 035 import org.kuali.rice.krad.uif.component.Component; 036 import org.kuali.rice.krad.uif.component.DataBinding; 037 import org.kuali.rice.krad.uif.component.KeepExpression; 038 import org.kuali.rice.krad.uif.container.CollectionGroup; 039 import org.kuali.rice.krad.uif.container.Container; 040 import org.kuali.rice.krad.uif.container.Group; 041 import org.kuali.rice.krad.uif.container.collections.LineBuilderContext; 042 import org.kuali.rice.krad.uif.element.Action; 043 import org.kuali.rice.krad.uif.element.Label; 044 import org.kuali.rice.krad.uif.element.Message; 045 import org.kuali.rice.krad.uif.field.DataField; 046 import org.kuali.rice.krad.uif.field.Field; 047 import org.kuali.rice.krad.uif.field.FieldGroup; 048 import org.kuali.rice.krad.uif.field.InputField; 049 import org.kuali.rice.krad.uif.field.MessageField; 050 import org.kuali.rice.krad.uif.layout.collections.CollectionPagingHelper; 051 import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle; 052 import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleRestriction; 053 import org.kuali.rice.krad.uif.util.ColumnCalculationInfo; 054 import org.kuali.rice.krad.uif.util.ComponentFactory; 055 import org.kuali.rice.krad.uif.util.ComponentUtils; 056 import org.kuali.rice.krad.uif.util.LifecycleElement; 057 import org.kuali.rice.krad.uif.view.ExpressionEvaluator; 058 import org.kuali.rice.krad.uif.view.View; 059 import org.kuali.rice.krad.uif.view.ViewModel; 060 import org.kuali.rice.krad.uif.widget.Pager; 061 import org.kuali.rice.krad.uif.widget.RichTable; 062 import org.kuali.rice.krad.util.KRADUtils; 063 import org.kuali.rice.krad.uif.layout.collections.DataTablesPagingHelper; 064 import org.kuali.rice.krad.web.form.UifFormBase; 065 066 /** 067 * Implementation of table layout manager. 068 * 069 * <p>Based on the fields defined, the {@code TableLayoutManager} will dynamically create instances of 070 * the fields for each collection row. In addition, the manager can create standard fields like the 071 * action and sequence fields for each row. The manager supports options inherited from the 072 * {@code GridLayoutManager} such as rowSpan, colSpan, and cell width settings.</p> 073 * 074 * {@inheritDoc} 075 * 076 * @author Kuali Rice Team (rice.collab@kuali.org) 077 */ 078 @BeanTag(name = "tableCollectionLayout-bean", parent = "Uif-TableCollectionLayout") 079 public class TableLayoutManagerBase extends GridLayoutManagerBase implements TableLayoutManager { 080 private static final long serialVersionUID = 3622267585541524208L; 081 082 private boolean useShortLabels; 083 private boolean repeatHeader; 084 private Label headerLabelPrototype; 085 086 private boolean renderSequenceField; 087 private boolean generateAutoSequence; 088 private Field sequenceFieldPrototype; 089 090 private FieldGroup actionFieldPrototype; 091 private FieldGroup subCollectionFieldGroupPrototype; 092 private Field selectFieldPrototype; 093 094 private boolean separateAddLine; 095 private Group addLineGroup; 096 097 // internal counter for the data columns (not including sequence, action) 098 private int numberOfDataColumns; 099 100 private List<Label> headerLabels; 101 private List<Field> allRowFields; 102 private List<Field> firstRowFields; 103 104 private Pager pagerWidget; 105 private RichTable richTable; 106 private boolean headerAdded; 107 108 private int actionColumnIndex = -1; 109 private String actionColumnPlacement; 110 111 //row details properties 112 private Group rowDetailsGroup; 113 private boolean rowDetailsOpen; 114 private boolean showToggleAllDetails; 115 private Action toggleAllDetailsAction; 116 private boolean ajaxDetailsRetrieval; 117 private Action expandDetailsActionPrototype; 118 119 //grouping properties 120 @KeepExpression 121 private String groupingTitle; 122 private String groupingPrefix; 123 private int groupingColumnIndex; 124 private List<String> groupingPropertyNames; 125 126 //total properties 127 private boolean renderOnlyLeftTotalLabels; 128 private boolean showTotal; 129 private boolean showPageTotal; 130 private boolean showGroupTotal; 131 private boolean generateGroupTotalRows; 132 private Label totalLabel; 133 private Label pageTotalLabel; 134 private Label groupTotalLabelPrototype; 135 136 private List<String> columnsToCalculate; 137 private List<ColumnCalculationInfo> columnCalculations; 138 private List<Component> footerCalculationComponents; 139 140 //row css 141 private Map<String, String> conditionalRowCssClasses; 142 143 public TableLayoutManagerBase() { 144 useShortLabels = false; 145 repeatHeader = false; 146 renderSequenceField = true; 147 generateAutoSequence = false; 148 separateAddLine = false; 149 rowDetailsOpen = false; 150 151 headerLabels = new ArrayList<Label>(); 152 allRowFields = new ArrayList<Field>(); 153 firstRowFields = new ArrayList<Field>(); 154 columnsToCalculate = new ArrayList<String>(); 155 columnCalculations = new ArrayList<ColumnCalculationInfo>(); 156 conditionalRowCssClasses = new HashMap<String, String>(); 157 } 158 159 /** 160 * The following actions are performed: 161 * 162 * <ul> 163 * <li>Sets sequence field prototype if auto sequence is true</li> 164 * <li>Initializes the prototypes</li> 165 * </ul> 166 * 167 * {@inheritDoc} 168 */ 169 @Override 170 public void performInitialization(Object model) { 171 CollectionGroup collectionGroup = (CollectionGroup) ViewLifecycle.getPhase().getElement(); 172 173 if (collectionGroup.isReadOnly()) { 174 addLineGroup.setReadOnly(true); 175 actionFieldPrototype.setReadOnly(true); 176 } 177 178 this.setupDetails(collectionGroup); 179 this.setupGrouping(model, collectionGroup); 180 181 if (collectionGroup.isAddViaLightBox()) { 182 setSeparateAddLine(true); 183 } 184 185 super.performInitialization(model); 186 187 getRowCssClasses().clear(); 188 189 if (generateAutoSequence && !(getSequenceFieldPrototype() instanceof MessageField)) { 190 sequenceFieldPrototype = ComponentFactory.getMessageField(); 191 } 192 } 193 194 /** 195 * performApplyModel override. Takes expressions that may be set in the columnCalculation 196 * objects and populates them correctly into those component's propertyExpressions. 197 * 198 * @param view view instance to which the layout manager belongs 199 * @param model Top level object containing the data (could be the form or a top level business 200 * object, dto) 201 * @param container 202 * 203 * {@inheritDoc} 204 */ 205 @Override 206 public void performApplyModel(Object model, LifecycleElement parent) { 207 super.performApplyModel(model, parent); 208 209 for (ColumnCalculationInfo cInfo : columnCalculations) { 210 ViewLifecycle.getExpressionEvaluator().populatePropertyExpressionsFromGraph(cInfo, false); 211 } 212 } 213 214 /** 215 * Sets up the final column count for rendering based on whether the sequence and action fields 216 * have been generated, sets up column calculations, and richTable rowGrouping options 217 * 218 * {@inheritDoc} 219 */ 220 @Override 221 public void performFinalize(Object model, LifecycleElement parent) { 222 super.performFinalize(model, parent); 223 224 UifFormBase formBase = (UifFormBase) model; 225 226 CollectionGroup collectionGroup = (CollectionGroup) ViewLifecycle.getPhase().getElement(); 227 228 int totalColumns = getNumberOfDataColumns(); 229 if (renderSequenceField) { 230 totalColumns++; 231 } 232 233 if (collectionGroup.isIncludeLineSelectionField()) { 234 totalColumns++; 235 } 236 237 if (collectionGroup.isRenderLineActions() && !collectionGroup.isReadOnly()) { 238 totalColumns++; 239 } 240 241 setNumberOfColumns(totalColumns); 242 243 // Default equal cell widths class 244 if (this.isApplyDefaultCellWidths()){ 245 this.addStyleClass("uif-table-fixed"); 246 } 247 248 // if add line event, add highlighting for added row 249 if (UifConstants.ActionEvents.ADD_LINE.equals(formBase.getActionEvent())) { 250 String highlightScript = "jQuery(\"#" + parent.getId() + " tr:first\").effect(\"highlight\",{}, 6000);"; 251 String onReadyScript = collectionGroup.getOnDocumentReadyScript(); 252 if (StringUtils.isNotBlank(onReadyScript)) { 253 highlightScript = onReadyScript + highlightScript; 254 } 255 collectionGroup.setOnDocumentReadyScript(highlightScript); 256 } 257 258 //setup the column calculations functionality and components 259 if (columnCalculations != null && !columnCalculations.isEmpty() && richTable != null && 260 this.getAllRowFields() != null && !this.getAllRowFields().isEmpty()) { 261 setupColumnCalculations(model, collectionGroup, totalColumns); 262 } 263 264 //set the js properties for rowGrouping on richTables 265 if ((groupingPropertyNames != null || StringUtils.isNotBlank(this.getGroupingTitle())) && richTable != null) { 266 richTable.setGroupingOptionsJSString("{iGroupingColumnIndex: " 267 + groupingColumnIndex 268 + ", bGenerateGroupTotalRows:" 269 + this.generateGroupTotalRows 270 + ", bSetGroupingClassOnTR: true" 271 + ", sGroupingClass: 'uif-groupRow'" 272 + (this.getGroupingPrefix() != null ? ", sGroupLabelPrefix: '" + this.getGroupingPrefix() + "'" : 273 "") 274 + "}"); 275 } 276 277 // Calculate the number of pages for the pager widget if we are using server paging 278 if ((this.getRichTable() == null || !this.getRichTable().isRender()) && 279 collectionGroup.isUseServerPaging() && this.getPagerWidget() != null) { 280 // Set the appropriate page, total pages, and link script into the Pager 281 CollectionLayoutUtils.setupPagerWidget(pagerWidget, collectionGroup, model); 282 } 283 284 // Add toggle all details action data in applicable 285 if (toggleAllDetailsAction != null) { 286 toggleAllDetailsAction.addDataAttribute("open", Boolean.toString(this.rowDetailsOpen)); 287 toggleAllDetailsAction.addDataAttribute("tableid", this.getId()); 288 } 289 } 290 291 /** 292 * Sets up the grouping MessageField to be used in the first column of the table layout for 293 * grouping collection content into groups based on values of the line's fields. 294 * 295 * @param model The model for the active lifecycle 296 * @param collectionGroup collection group for this layout 297 */ 298 protected void setupGrouping(Object model, CollectionGroup collectionGroup) { 299 String groupingTitleExpression = ""; 300 301 if (StringUtils.isNotBlank(this.getPropertyExpression(UifPropertyPaths.GROUPING_TITLE))) { 302 groupingTitleExpression = this.getPropertyExpression(UifPropertyPaths.GROUPING_TITLE); 303 304 this.setGroupingTitle(this.getPropertyExpression(UifPropertyPaths.GROUPING_TITLE)); 305 } else if (this.getGroupingPropertyNames() != null) { 306 for (String propertyName : this.getGroupingPropertyNames()) { 307 groupingTitleExpression = groupingTitleExpression + ", " + propertyName; 308 } 309 310 groupingTitleExpression = groupingTitleExpression.replaceFirst(", ", 311 "@{" + UifConstants.LINE_PATH_BIND_ADJUST_PREFIX); 312 groupingTitleExpression = groupingTitleExpression.replace(", ", 313 "}, @{" + UifConstants.LINE_PATH_BIND_ADJUST_PREFIX); 314 groupingTitleExpression = groupingTitleExpression.trim() + "}"; 315 } 316 317 if (StringUtils.isNotBlank(groupingTitleExpression)) { 318 MessageField groupingMessageField = ComponentFactory.getColGroupingField(); 319 320 groupingMessageField.getMessage().getPropertyExpressions().put(UifPropertyPaths.MESSAGE_TEXT, 321 groupingTitleExpression); 322 323 groupingMessageField.addDataAttribute(UifConstants.DataAttributes.ROLE, 324 UifConstants.RoleTypes.ROW_GROUPING); 325 326 List<Component> theItems = new ArrayList<Component>(); 327 theItems.add(groupingMessageField); 328 theItems.addAll(collectionGroup.getItems()); 329 collectionGroup.setItems(theItems); 330 } 331 } 332 333 /** 334 * Setup the column calculations functionality and components 335 * 336 * @param model the model 337 * @param container the parent container 338 * @param totalColumns total number of columns in the table 339 */ 340 protected void setupColumnCalculations(Object model, Container container, int totalColumns) { 341 footerCalculationComponents = new ArrayList<Component>(totalColumns); 342 343 //add nulls for each column to start - nulls will be processed by the ftl as a blank cell 344 for (int i = 0; i < totalColumns; i++) { 345 footerCalculationComponents.add(null); 346 } 347 348 int leftLabelColumnIndex = 0; 349 if (groupingPropertyNames != null || StringUtils.isNotBlank(this.getGroupingTitle())) { 350 leftLabelColumnIndex = 1; 351 } 352 353 //process each column calculation 354 for (ColumnCalculationInfo cInfo : columnCalculations) { 355 //propertyName is REQUIRED throws exception if not set 356 if (StringUtils.isNotBlank(cInfo.getPropertyName())) { 357 for (int i = 0; i < this.getNumberOfColumns(); i++) { 358 Component component = this.getAllRowFields().get(i); 359 if (component != null && component instanceof DataField && 360 ((DataField) component).getPropertyName().equals(cInfo.getPropertyName())) { 361 cInfo.setColumnNumber(i); 362 } 363 } 364 365 this.getColumnsToCalculate().add(cInfo.getColumnNumber().toString()); 366 } else { 367 throw new RuntimeException("TableLayoutManager(" + container.getId() + "->" + this.getId() + 368 ") ColumnCalculationInfo MUST have a propertyName set"); 369 } 370 371 // create a new field group to hold the totals fields 372 FieldGroup calculationFieldGroup = ComponentFactory.getFieldGroup(); 373 calculationFieldGroup.addDataAttribute(UifConstants.DataAttributes.ROLE, 374 UifConstants.RoleTypes.TOTALS_BLOCK); 375 376 List<Component> calculationFieldGroupItems = new ArrayList<Component>(); 377 378 //setup page total field and add it to footer's group for this column 379 if (cInfo.isShowPageTotal()) { 380 Field pageTotalDataField = cInfo.getPageTotalField().copy(); 381 setupTotalField(pageTotalDataField, cInfo, this.isShowPageTotal(), getPageTotalLabel(), 382 UifConstants.RoleTypes.PAGE_TOTAL, leftLabelColumnIndex); 383 calculationFieldGroupItems.add(pageTotalDataField); 384 } 385 386 //setup total field and add it to footer's group for this column 387 if (cInfo.isShowTotal()) { 388 Field totalDataField = cInfo.getTotalField().copy(); 389 setupTotalField(totalDataField, cInfo, this.isShowTotal(), getTotalLabel(), 390 UifConstants.RoleTypes.TOTAL, leftLabelColumnIndex); 391 392 if (!cInfo.isRecalculateTotalClientSide()) { 393 totalDataField.addDataAttribute(UifConstants.DataAttributes.SKIP_TOTAL, "true"); 394 } 395 396 calculationFieldGroupItems.add(totalDataField); 397 } 398 399 //setup total field and add it to footer's group for this column 400 //do not generate group total rows if group totals are not being shown 401 if (cInfo.isShowGroupTotal()) { 402 Field groupTotalDataField = cInfo.getGroupTotalFieldPrototype().copy(); 403 setupTotalField(groupTotalDataField, cInfo, this.isShowGroupTotal(), getGroupTotalLabelPrototype(), 404 UifConstants.RoleTypes.GROUP_TOTAL, leftLabelColumnIndex); 405 groupTotalDataField.setId(container.getId() + "_gTotal" + cInfo.getColumnNumber()); 406 groupTotalDataField.setStyle("display: none;"); 407 408 calculationFieldGroupItems.add(groupTotalDataField); 409 410 if (this.isRenderOnlyLeftTotalLabels() && !this.isShowGroupTotal()) { 411 generateGroupTotalRows = false; 412 } else { 413 generateGroupTotalRows = true; 414 } 415 } 416 417 calculationFieldGroup.setItems(calculationFieldGroupItems); 418 419 //Determine if there is already a fieldGroup present for this column's footer 420 //if so create a new group and add the new calculation fields to the already existing ones 421 //otherwise just add it 422 Component component = footerCalculationComponents.get(cInfo.getColumnNumber()); 423 if (component != null && component instanceof FieldGroup) { 424 Group verticalComboCalcGroup = ComponentFactory.getVerticalBoxGroup(); 425 426 List<Component> comboGroupItems = new ArrayList<Component>(); 427 comboGroupItems.add(component); 428 comboGroupItems.add(calculationFieldGroup); 429 verticalComboCalcGroup.setItems(comboGroupItems); 430 431 footerCalculationComponents.set(cInfo.getColumnNumber(), verticalComboCalcGroup); 432 } else if (component != null && component instanceof Group) { 433 List<Component> comboGroupItems = new ArrayList<Component>(); 434 comboGroupItems.addAll(((Group) component).getItems()); 435 comboGroupItems.add(calculationFieldGroup); 436 437 ((Group) component).setItems(comboGroupItems); 438 439 footerCalculationComponents.set(cInfo.getColumnNumber(), component); 440 } else { 441 footerCalculationComponents.set(cInfo.getColumnNumber(), calculationFieldGroup); 442 } 443 } 444 445 //special processing for the left labels - when there are no total fields in this column 446 //add the label to the column footer directly 447 if (this.renderOnlyLeftTotalLabels && footerCalculationComponents.get(leftLabelColumnIndex) == null) { 448 449 List<Component> groupItems = new ArrayList<Component>(); 450 Group labelGroup = ComponentFactory.getVerticalBoxGroup(); 451 452 if (this.isShowGroupTotal()) { 453 //display none - this label is copied by the javascript 454 Label groupTotalLabel = groupTotalLabelPrototype.copy(); 455 groupTotalLabel.setViewStatus(UifConstants.ViewStatus.CREATED); 456 groupTotalLabel.setStyle("display: none;"); 457 groupTotalLabel.addDataAttribute(UifConstants.DataAttributes.ROLE, "groupTotalLabel"); 458 groupItems.add(groupTotalLabel); 459 } 460 461 if (this.isShowPageTotal()) { 462 Label pageTotalLabel = this.pageTotalLabel.copy(); 463 pageTotalLabel.setViewStatus(UifConstants.ViewStatus.CREATED); 464 pageTotalLabel.addDataAttribute(UifConstants.DataAttributes.ROLE, "pageTotal"); 465 groupItems.add(pageTotalLabel); 466 } 467 468 if (this.isShowTotal()) { 469 Label totalLabel = this.totalLabel.copy(); 470 totalLabel.setViewStatus(UifConstants.ViewStatus.CREATED); 471 groupItems.add(totalLabel); 472 } 473 474 labelGroup.setItems(groupItems); 475 476 footerCalculationComponents.set(leftLabelColumnIndex, labelGroup); 477 } 478 } 479 480 /** 481 * Setup the totalField with the columnCalculationInfo(cInfo) passed in. Param show represents 482 * the tableLayoutManager's setting for the type of total being processed. 483 * 484 * @param totalField the field to setup 485 * @param cInfo ColumnCalculation info to use to setup the field 486 * @param show show the field (if renderOnlyLeftTotalLabels is true, otherwise uses value in 487 * cInfo) 488 * @param leftLabel the leftLabel, not used if renderOnlyLeftTotalLabels is false 489 * @param type type used to set the dataAttribute role - used by the js for selection 490 * @param leftLabelColumnIndex index of the leftLabelColumn (0 or 1 if grouping enabled - hidden 491 * column) 492 * @return the field with cInfo and tableLayoutManager settings applied as appropriate 493 */ 494 protected Field setupTotalField(Field totalField, ColumnCalculationInfo cInfo, boolean show, Label leftLabel, 495 String type, int leftLabelColumnIndex) { 496 //setup the totals field 497 Field totalDataField = totalField; 498 totalDataField.addDataAttribute(UifConstants.DataAttributes.ROLE, type); 499 totalDataField.addDataAttribute("function", cInfo.getCalculationFunctionName()); 500 totalDataField.addDataAttribute("params", cInfo.getCalculationFunctionExtraData()); 501 502 if (cInfo.getColumnNumber() != leftLabelColumnIndex) { 503 //do not render labels for columns which have totals and the renderOnlyLeftTotalLabels 504 //flag is set 505 totalDataField.getFieldLabel().setRender(!this.isRenderOnlyLeftTotalLabels()); 506 } else if (cInfo.getColumnNumber() == leftLabelColumnIndex && this.isRenderOnlyLeftTotalLabels()) { 507 //renderOnlyLeftTotalLabel is set to true, but the column has a total itself - set the layout 508 //manager settings directly into the field 509 totalDataField.setFieldLabel(leftLabel.<Label> copy()); 510 } 511 512 if (this.isRenderOnlyLeftTotalLabels()) { 513 totalDataField.setRender(show); 514 } 515 516 return totalDataField; 517 } 518 519 /** 520 * Assembles the field instances for the collection line. 521 * 522 * <p>The given sequence field prototype is copied for the line sequence field. Likewise a copy of 523 * the actionFieldPrototype is made and the given actions are set as the items for the action field. 524 * Finally the generated items are assembled together into the allRowFields list with the given 525 * lineFields.</p> 526 * 527 * {@inheritDoc} 528 */ 529 @Override 530 public void buildLine(LineBuilderContext lineBuilderContext) { 531 View view = ViewLifecycle.getView(); 532 533 List<Field> lineFields = lineBuilderContext.getLineFields(); 534 CollectionGroup collectionGroup = lineBuilderContext.getCollectionGroup(); 535 int lineIndex = lineBuilderContext.getLineIndex(); 536 String idSuffix = lineBuilderContext.getIdSuffix(); 537 Object currentLine = lineBuilderContext.getCurrentLine(); 538 List<? extends Component> actions = lineBuilderContext.getLineActions(); 539 String bindingPath = lineBuilderContext.getBindingPath(); 540 541 // since expressions are not evaluated on child components yet, we need to evaluate any properties 542 // we are going to read for building the table 543 ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator(); 544 for (Field lineField : lineFields) { 545 lineField.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, collectionGroup); 546 lineField.pushAllToContext(view.getContext()); 547 lineField.pushObjectToContext(UifConstants.ContextVariableNames.THEME_IMAGES, 548 view.getTheme().getImageDirectory()); 549 lineField.pushObjectToContext(UifConstants.ContextVariableNames.COMPONENT, lineField); 550 551 expressionEvaluator.evaluatePropertyExpression(view, lineField.getContext(), lineField, 552 UifPropertyPaths.ROW_SPAN, true); 553 expressionEvaluator.evaluatePropertyExpression(view, lineField.getContext(), lineField, 554 UifPropertyPaths.COL_SPAN, true); 555 expressionEvaluator.evaluatePropertyExpression(view, lineField.getContext(), lineField, 556 UifPropertyPaths.REQUIRED, true); 557 expressionEvaluator.evaluatePropertyExpression(view, lineField.getContext(), lineField, 558 UifPropertyPaths.READ_ONLY, true); 559 } 560 561 // if first line for table set number of data columns 562 if (allRowFields.isEmpty()) { 563 if (isSuppressLineWrapping()) { 564 setNumberOfDataColumns(lineFields.size()); 565 } else { 566 setNumberOfDataColumns(getNumberOfColumns()); 567 } 568 } 569 570 boolean isAddLine = false; 571 572 // If first row or row wrap is happening 573 if (lineIndex == -1 || (lineFields.size() != numberOfDataColumns 574 && ((lineIndex + 1) * numberOfDataColumns) < lineFields.size())) { 575 isAddLine = true; 576 } 577 578 // capture the first row of fields for widgets that build off the table 579 if (lineIndex == 0 || this.firstRowFields.isEmpty()) { 580 this.firstRowFields = lineFields; 581 } 582 583 boolean renderActions = collectionGroup.isRenderLineActions() && !collectionGroup.isReadOnly(); 584 int extraColumns = 0; 585 String rowCss = ""; 586 boolean addLineInTable = 587 collectionGroup.isRenderAddLine() && !collectionGroup.isReadOnly() && !isSeparateAddLine(); 588 589 if (collectionGroup.isHighlightNewItems() && ((UifFormBase) lineBuilderContext.getModel()) 590 .isAddedCollectionItem(currentLine)) { 591 rowCss = collectionGroup.getNewItemsCssClass(); 592 } else if (isAddLine && addLineInTable) { 593 rowCss = collectionGroup.getAddItemCssClass(); 594 this.addStyleClass(CssConstants.Classes.HAS_ADD_LINE); 595 } 596 597 // do not allow null rowCss 598 if (rowCss == null) { 599 rowCss = ""; 600 } 601 602 // conditionalRowCssClass generation logic, if applicable 603 if (conditionalRowCssClasses != null && !conditionalRowCssClasses.isEmpty()) { 604 int oddRemainder = 1; 605 if (!addLineInTable) { 606 oddRemainder = 0; 607 } 608 609 boolean isOdd = lineIndex % 2 == oddRemainder || lineIndex == -1; 610 Map<String, Object> lineContext = new HashMap<String, Object>(); 611 612 lineContext.putAll(this.getContext()); 613 lineContext.put(UifConstants.ContextVariableNames.LINE, currentLine); 614 lineContext.put(UifConstants.ContextVariableNames.MANAGER, this); 615 lineContext.put(UifConstants.ContextVariableNames.VIEW, view); 616 lineContext.put(UifConstants.ContextVariableNames.LINE_SUFFIX, idSuffix); 617 lineContext.put(UifConstants.ContextVariableNames.INDEX, Integer.valueOf(lineIndex)); 618 lineContext.put(UifConstants.ContextVariableNames.COLLECTION_GROUP, collectionGroup); 619 lineContext.put(UifConstants.ContextVariableNames.IS_ADD_LINE, isAddLine && !isSeparateAddLine()); 620 lineContext.put(UifConstants.ContextVariableNames.READONLY_LINE, collectionGroup.isReadOnly()); 621 622 // get row css based on conditionalRowCssClasses map 623 rowCss = rowCss + " " + KRADUtils.generateRowCssClassString(conditionalRowCssClasses, lineIndex, isOdd, 624 lineContext, expressionEvaluator); 625 } 626 627 // if separate add line prepare the add line group 628 if (isAddLine && separateAddLine) { 629 if (StringUtils.isBlank(addLineGroup.getTitle()) && StringUtils.isBlank( 630 addLineGroup.getHeader().getHeaderText())) { 631 addLineGroup.getHeader().setHeaderText(collectionGroup.getAddLabel()); 632 } 633 addLineGroup.setItems(lineFields); 634 635 List<Component> footerItems = new ArrayList<Component>(actions); 636 footerItems.addAll(addLineGroup.getFooter().getItems()); 637 addLineGroup.getFooter().setItems(footerItems); 638 639 if (collectionGroup.isAddViaLightBox()) { 640 String actionScript = "showLightboxComponent('" + addLineGroup.getId() + "');"; 641 if (StringUtils.isNotBlank(collectionGroup.getAddViaLightBoxAction().getActionScript())) { 642 actionScript = collectionGroup.getAddViaLightBoxAction().getActionScript() + actionScript; 643 } 644 collectionGroup.getAddViaLightBoxAction().setActionScript(actionScript); 645 addLineGroup.setStyle("display: none"); 646 } 647 648 // Note that a RowCssClass was not added to the LayoutManager for the collection for the separateAddLine 649 return; 650 } 651 652 rowCss = StringUtils.removeStart(rowCss, " "); 653 this.getRowCssClasses().add(rowCss); 654 655 // TODO: implement repeat header 656 if (!headerAdded) { 657 headerLabels = new ArrayList<Label>(); 658 allRowFields = new ArrayList<Field>(); 659 660 buildTableHeaderRows(collectionGroup, lineFields); 661 ComponentUtils.pushObjectToContext(headerLabels, UifConstants.ContextVariableNames.LINE, currentLine); 662 ComponentUtils.pushObjectToContext(headerLabels, UifConstants.ContextVariableNames.INDEX, new Integer( 663 lineIndex)); 664 headerAdded = true; 665 } 666 667 // set label field rendered to true on line fields and adjust cell properties 668 for (Field field : lineFields) { 669 field.setLabelRendered(true); 670 field.setFieldLabel(null); 671 672 setCellAttributes(field); 673 } 674 675 int rowCount = calculateNumberOfRows(lineFields); 676 int rowSpan = rowCount; 677 678 List<FieldGroup> subCollectionFields = lineBuilderContext.getSubCollectionFields(); 679 if (subCollectionFields != null) { 680 rowSpan += subCollectionFields.size(); 681 } 682 683 if (actionColumnIndex == 1 && renderActions) { 684 addActionColumn(collectionGroup, idSuffix, currentLine, lineIndex, rowSpan, actions); 685 } 686 687 // sequence field is always first and should span all rows for the line 688 if (renderSequenceField) { 689 Field sequenceField = null; 690 if (!isAddLine) { 691 sequenceField = ComponentUtils.copy(getSequenceFieldPrototype(), idSuffix); 692 693 //Ignore in validation processing 694 sequenceField.addDataAttribute(UifConstants.DataAttributes.VIGNORE, "yes"); 695 696 if (generateAutoSequence && (sequenceField instanceof MessageField)) { 697 ((MessageField) sequenceField).setMessageText(Integer.toString(lineIndex + 1)); 698 } 699 } else { 700 sequenceField = ComponentFactory.getMessageField(); 701 702 Message sequenceMessage = ComponentUtils.copy(collectionGroup.getAddLineLabel(), idSuffix); 703 ((MessageField) sequenceField).setMessage(sequenceMessage); 704 705 // adjusting add line label to match sequence prototype cells attributes 706 sequenceField.setCellWidth(getSequenceFieldPrototype().getCellWidth()); 707 sequenceField.setWrapperStyle(getSequenceFieldPrototype().getWrapperStyle()); 708 } 709 710 sequenceField.setRowSpan(rowSpan); 711 712 if (sequenceField instanceof DataBinding) { 713 ((DataBinding) sequenceField).getBindingInfo().setBindByNamePrefix(bindingPath); 714 } 715 716 setCellAttributes(sequenceField); 717 718 ComponentUtils.updateContextForLine(sequenceField, collectionGroup, currentLine, lineIndex, idSuffix); 719 allRowFields.add(sequenceField); 720 721 extraColumns++; 722 723 if (actionColumnIndex == 2 && renderActions) { 724 addActionColumn(collectionGroup, idSuffix, currentLine, lineIndex, rowSpan, actions); 725 } 726 } 727 728 // select field will come after sequence field (if enabled) or be first column 729 if (collectionGroup.isIncludeLineSelectionField()) { 730 Field selectField = ComponentUtils.copy(getSelectFieldPrototype(), idSuffix); 731 CollectionLayoutUtils.prepareSelectFieldForLine(selectField, collectionGroup, bindingPath, currentLine); 732 733 ComponentUtils.updateContextForLine(selectField, collectionGroup, currentLine, lineIndex, idSuffix); 734 setCellAttributes(selectField); 735 736 allRowFields.add(selectField); 737 738 extraColumns++; 739 740 if (renderActions) { 741 if ((actionColumnIndex == 3 && renderSequenceField) || (actionColumnIndex == 2 742 && !renderSequenceField)) { 743 addActionColumn(collectionGroup, idSuffix, currentLine, lineIndex, rowSpan, actions); 744 } 745 } 746 } 747 748 // now add the fields in the correct position 749 int cellPosition = 0; 750 751 boolean renderActionsLast = actionColumnIndex == -1 || actionColumnIndex > lineFields.size() + extraColumns; 752 boolean hasGrouping = (groupingPropertyNames != null || StringUtils.isNotBlank(this.getGroupingTitle())); 753 boolean insertActionField = false; 754 755 for (Field lineField : lineFields) { 756 //Check to see if ActionField needs to be inserted before this lineField because of wrapping. 757 // Since actionField has a colSpan of 1 add that to the previous cellPosition instead of the 758 // current lineField's colSpan. 759 // Only insert if ActionField has to be placed at the end. Else the specification of actionColumnIndex should 760 // take care of putting it in the right location 761 insertActionField = (cellPosition != 0 && lineFields.size() != numberOfDataColumns) 762 && renderActions 763 && renderActionsLast 764 && ((cellPosition % numberOfDataColumns) == 0); 765 766 cellPosition += lineField.getColSpan(); 767 768 //special handling for grouping field - this field MUST be first 769 Map<String, String> lineFieldDataAttributes = lineField.getDataAttributes(); 770 if (hasGrouping && (lineField instanceof MessageField) && 771 lineFieldDataAttributes != null && UifConstants.RoleTypes.ROW_GROUPING.equals( 772 lineFieldDataAttributes.get(UifConstants.DataAttributes.ROLE))) { 773 int groupFieldIndex = allRowFields.size() - extraColumns; 774 allRowFields.add(groupFieldIndex, lineField); 775 groupingColumnIndex = 0; 776 if (isAddLine) { 777 ((MessageField) lineField).getMessage().getPropertyExpressions().remove( 778 UifPropertyPaths.MESSAGE_TEXT); 779 ((MessageField) lineField).getMessage().setMessageText("addLine"); 780 } 781 } else { 782 // If the row wraps before the last element 783 if (insertActionField) { 784 addActionColumn(collectionGroup, idSuffix, currentLine, lineIndex, rowSpan, actions); 785 } 786 787 allRowFields.add(lineField); 788 } 789 790 // action field 791 if (!renderActionsLast && cellPosition == (actionColumnIndex - extraColumns - 1)) { 792 addActionColumn(collectionGroup, idSuffix, currentLine, lineIndex, rowSpan, actions); 793 } 794 795 //details action 796 if (lineField instanceof FieldGroup && ((FieldGroup) lineField).getItems() != null) { 797 for (Component component : ((FieldGroup) lineField).getItems()) { 798 if (component != null 799 && component instanceof Action 800 && (component.getDataAttributes() != null) 801 && component.getDataAttributes().get("role") != null 802 && component.getDataAttributes().get("role").equals("detailsLink") 803 && StringUtils.isBlank(((Action) component).getActionScript())) { 804 ((Action) component).setActionScript("rowDetailsActionHandler(this,'" + this.getId() + "');"); 805 } 806 } 807 } 808 809 //special column calculation handling to identify what type of handler will be attached 810 //and add special styling 811 if (lineField instanceof InputField && columnCalculations != null) { 812 for (ColumnCalculationInfo cInfo : columnCalculations) { 813 if (cInfo.getPropertyName().equals(((InputField) lineField).getPropertyName())) { 814 if (cInfo.isCalculateOnKeyUp()) { 815 lineField.addDataAttribute(UifConstants.DataAttributes.TOTAL, "keyup"); 816 } else { 817 lineField.addDataAttribute(UifConstants.DataAttributes.TOTAL, "change"); 818 } 819 lineField.addStyleClass("uif-calculationField"); 820 } 821 } 822 } 823 } 824 825 if (lineFields.size() == numberOfDataColumns && renderActions && renderActionsLast) { 826 addActionColumn(collectionGroup, idSuffix, currentLine, lineIndex, rowSpan, actions); 827 } 828 829 // update colspan on sub-collection fields 830 if (subCollectionFields != null) { 831 for (FieldGroup subCollectionField : subCollectionFields) { 832 subCollectionField.setColSpan(numberOfDataColumns); 833 } 834 835 // add sub-collection fields to end of data fields 836 allRowFields.addAll(subCollectionFields); 837 } 838 } 839 840 /** 841 * Creates a field group wrapper for the given actions based on 842 * {@link TableLayoutManagerBase#getActionFieldPrototype()}. 843 * 844 * @param collectionGroup collection group being built 845 * @param idSuffix id suffix for the action field 846 * @param currentLine line object for the current line being built 847 * @param lineIndex index of the line being built 848 * @param rowSpan number of rows the action field should span 849 * @param actions action components that should be to the field group 850 */ 851 protected void addActionColumn(CollectionGroup collectionGroup, String idSuffix, Object currentLine, int lineIndex, 852 int rowSpan, List<? extends Component> actions) { 853 FieldGroup lineActionsField = ComponentUtils.copy(getActionFieldPrototype(), idSuffix); 854 855 ComponentUtils.updateContextForLine(lineActionsField, collectionGroup, currentLine, lineIndex, idSuffix); 856 857 lineActionsField.setRowSpan(rowSpan); 858 lineActionsField.setItems(actions); 859 if (lineActionsField.getWrapperCssClasses() != null && !lineActionsField.getWrapperCssClasses().contains( 860 CssConstants.Classes.ACTION_COLUMN_STYLE_CLASS)) { 861 lineActionsField.getWrapperCssClasses().add(CssConstants.Classes.ACTION_COLUMN_STYLE_CLASS); 862 } else { 863 lineActionsField.setWrapperCssClasses(Arrays.asList(CssConstants.Classes.ACTION_COLUMN_STYLE_CLASS)); 864 } 865 866 setCellAttributes(lineActionsField); 867 868 allRowFields.add(lineActionsField); 869 } 870 871 /** 872 * Create the {@code Label} instances that will be used to render the table header 873 * 874 * <p> 875 * For each column, a copy of headerLabelPrototype is made that determines the label 876 * configuration. The actual label text comes from the field for which the header applies to. 877 * The first column is always the sequence (if enabled) and the last column contains the 878 * actions. Both the sequence and action header fields will span all rows for the header. 879 * </p> 880 * 881 * <p> 882 * The headerLabels list will contain the final list of header fields built 883 * </p> 884 * 885 * @param collectionGroup CollectionGroup container the table applies to 886 * @param lineFields fields for the data columns from which the headers are pulled 887 */ 888 protected void buildTableHeaderRows(CollectionGroup collectionGroup, List<Field> lineFields) { 889 // row count needed to determine the row span for the sequence and 890 // action fields, since they should span all rows for the line 891 int rowCount = calculateNumberOfRows(lineFields); 892 893 boolean renderActions = collectionGroup.isRenderLineActions() && !collectionGroup.isReadOnly(); 894 895 String idSuffix = collectionGroup.getSubCollectionSuffix(); 896 897 int extraColumns = 0; 898 899 if (actionColumnIndex == 1 && renderActions) { 900 addActionHeader(rowCount, idSuffix, 1); 901 } 902 903 // first column is sequence label (if action column not 1) 904 if (renderSequenceField) { 905 getSequenceFieldPrototype().setLabelRendered(true); 906 getSequenceFieldPrototype().setRowSpan(rowCount); 907 addHeaderField(getSequenceFieldPrototype(), idSuffix, 1); 908 extraColumns++; 909 910 if (actionColumnIndex == 2 && renderActions) { 911 addActionHeader(rowCount, idSuffix, 2); 912 } 913 } 914 915 // next is select field 916 if (collectionGroup.isIncludeLineSelectionField()) { 917 getSelectFieldPrototype().setLabelRendered(true); 918 getSelectFieldPrototype().setRowSpan(rowCount); 919 addHeaderField(getSelectFieldPrototype(), idSuffix, 1); 920 extraColumns++; 921 922 if (actionColumnIndex == 3 && renderActions && renderSequenceField) { 923 addActionHeader(rowCount, idSuffix, 3); 924 } else if (actionColumnIndex == 2 && renderActions) { 925 addActionHeader(rowCount, idSuffix, 2); 926 } 927 } 928 929 // pull out label fields from the container's items 930 int cellPosition = 0; 931 boolean renderActionsLast = actionColumnIndex == -1 || actionColumnIndex > lineFields.size() + extraColumns; 932 boolean insertActionHeader = false; 933 for (Field field : lineFields) { 934 if (!field.isRender() && StringUtils.isEmpty(field.getProgressiveRender())) { 935 continue; 936 } 937 938 //Check to see if ActionField needs to be inserted before this lineField because of wrapping. 939 // Since actionField has a colSpan of 1 add that to the previous cellPosition instead of the 940 // current lineField's colSpan. 941 // Only Insert if ActionField has to be placed at the end. Else the specification of actionColumnIndex 942 // should take care of putting it in the right location 943 insertActionHeader = (cellPosition != 0 944 && lineFields.size() != numberOfDataColumns 945 && renderActions 946 && renderActionsLast 947 && ((cellPosition % numberOfDataColumns) == 0)); 948 949 if (insertActionHeader) { 950 addActionHeader(rowCount, idSuffix, cellPosition); 951 } 952 953 cellPosition += field.getColSpan(); 954 addHeaderField(field, idSuffix, cellPosition); 955 956 // add action header 957 if (renderActions && !renderActionsLast && cellPosition == actionColumnIndex - extraColumns - 1) { 958 cellPosition += 1; 959 addActionHeader(rowCount, idSuffix, cellPosition); 960 } 961 } 962 963 if (lineFields.size() == numberOfDataColumns && renderActions && renderActionsLast) { 964 cellPosition += 1; 965 addActionHeader(rowCount, idSuffix, cellPosition); 966 } 967 } 968 969 /** 970 * Adds the action header 971 * 972 * @param rowCount 973 * @param idSuffix suffix for the header id, also column will be added 974 * @param cellPosition 975 */ 976 protected void addActionHeader(int rowCount, String idSuffix, int cellPosition) { 977 getActionFieldPrototype().setLabelRendered(true); 978 getActionFieldPrototype().setRowSpan(rowCount); 979 if (getActionFieldPrototype().getWrapperCssClasses() != null && !getActionFieldPrototype() 980 .getWrapperCssClasses().contains(CssConstants.Classes.ACTION_COLUMN_STYLE_CLASS)) { 981 getActionFieldPrototype().getWrapperCssClasses().add(CssConstants.Classes.ACTION_COLUMN_STYLE_CLASS); 982 } else { 983 getActionFieldPrototype().setWrapperCssClasses(Arrays.asList( 984 CssConstants.Classes.ACTION_COLUMN_STYLE_CLASS)); 985 } 986 987 addHeaderField(getActionFieldPrototype(), idSuffix, cellPosition); 988 } 989 990 /** 991 * Creates a new instance of the header field prototype and then sets the label to the short (if 992 * useShortLabels is set to true) or long label of the given component. After created the header 993 * field is added to the list making up the table header 994 * 995 * @param field field instance the header field is being created for 996 * @param idSuffix suffix for the header id, also column will be added 997 * @param column column number for the header, used for setting the id 998 */ 999 protected void addHeaderField(Field field, String idSuffix, int column) { 1000 String labelSuffix = UifConstants.IdSuffixes.COLUMN + column; 1001 if (StringUtils.isNotBlank(idSuffix)) { 1002 labelSuffix = idSuffix + labelSuffix; 1003 } 1004 1005 Label headerLabel = ComponentUtils.copy(getHeaderLabelPrototype(), labelSuffix); 1006 1007 if (useShortLabels) { 1008 headerLabel.setLabelText(field.getShortLabel()); 1009 } else { 1010 headerLabel.setLabelText(field.getLabel()); 1011 } 1012 1013 headerLabel.setInlineComponents(field.getFieldLabel().getInlineComponents()); 1014 1015 headerLabel.setRowSpan(field.getRowSpan()); 1016 headerLabel.setColSpan(field.getColSpan()); 1017 1018 if ((field.getRequired() != null) && field.getRequired().booleanValue()) { 1019 headerLabel.setRenderRequiredIndicator(!field.isReadOnly()); 1020 } else { 1021 headerLabel.setRenderRequiredIndicator(false); 1022 } 1023 1024 setCellAttributes(field); 1025 1026 // copy cell attributes from the field to the label 1027 headerLabel.setWrapperCssClasses(field.getWrapperCssClasses()); 1028 headerLabel.setWrapperStyle(field.getWrapperStyle()); 1029 headerLabel.setCellWidth(field.getCellWidth()); 1030 1031 headerLabels.add(headerLabel); 1032 } 1033 1034 /** 1035 * Calculates how many rows will be needed per collection line to display the list of fields. 1036 * Assumption is made that the total number of cells the fields take up is evenly divisible by 1037 * the configured number of columns 1038 * 1039 * @param items list of items that make up one collection line 1040 * @return number of rows 1041 */ 1042 protected int calculateNumberOfRows(List<? extends Field> items) { 1043 int rowCount = 0; 1044 1045 // check flag that indicates only one row should be created 1046 if (isSuppressLineWrapping()) { 1047 return 1; 1048 } 1049 1050 // If Overflow is greater than 0 then calculate the col span for the last item in the overflowed row 1051 if (items.size() % getNumberOfDataColumns() > 0) { 1052 //get the last line item 1053 Field field = items.get(items.size() - 1); 1054 1055 int colSize = 0; 1056 for (Field f : items) { 1057 colSize += f.getColSpan(); 1058 } 1059 1060 field.setColSpan(1 + (numberOfDataColumns - (colSize % numberOfDataColumns))); 1061 rowCount = ((items.size() / getNumberOfDataColumns()) + 1); 1062 } else { 1063 rowCount = items.size() / getNumberOfDataColumns(); 1064 } 1065 return rowCount; 1066 } 1067 1068 /** 1069 * Invokes instance of {@link org.kuali.rice.krad.uif.layout.collections.DataTablesPagingHelper} to carry out 1070 * the paging request using data tables API. 1071 * 1072 * <p>There are two types of paging supported in the table layout, one that uses data tables paging API, and one 1073 * that handles basic table paging.</p> 1074 * 1075 * {@inheritDoc} 1076 */ 1077 @Override 1078 public void processPagingRequest(Object model, CollectionGroup collectionGroup) { 1079 boolean richTableEnabled = ((getRichTable() != null) && (getRichTable().isRender())); 1080 1081 if (richTableEnabled) { 1082 DataTablesPagingHelper.DataTablesInputs dataTablesInputs = new DataTablesPagingHelper.DataTablesInputs( 1083 ViewLifecycle.getRequest()); 1084 1085 DataTablesPagingHelper.processPagingRequest(ViewLifecycle.getView(), (ViewModel) model, collectionGroup, 1086 dataTablesInputs); 1087 } else { 1088 String pageNumber = ViewLifecycle.getRequest().getParameter(UifConstants.PageRequest.PAGE_NUMBER); 1089 1090 CollectionPagingHelper pagingHelper = new CollectionPagingHelper(); 1091 pagingHelper.processPagingRequest(ViewLifecycle.getView(), collectionGroup, (UifFormBase) model, 1092 pageNumber); 1093 } 1094 } 1095 1096 /** 1097 * {@inheritDoc} 1098 */ 1099 @Override 1100 public Class<? extends Container> getSupportedContainer() { 1101 return CollectionGroup.class; 1102 } 1103 1104 /** 1105 * Indicates whether the short label for the collection field should be used as the table header 1106 * or the regular label 1107 * 1108 * @return true if short label should be used, false if long label should be used 1109 */ 1110 @Override 1111 public List<Component> getColumnCalculationComponents() { 1112 if (columnCalculations == null) { 1113 return Collections.emptyList(); 1114 } 1115 1116 List<Component> components = new ArrayList<Component>(columnCalculations.size() * 3); 1117 for (ColumnCalculationInfo cInfo : columnCalculations) { 1118 components.add(cInfo.getTotalField()); 1119 components.add(cInfo.getPageTotalField()); 1120 components.add(cInfo.getGroupTotalFieldPrototype()); 1121 } 1122 return components; 1123 } 1124 1125 /** 1126 * {@inheritDoc} 1127 */ 1128 @Override 1129 @BeanTagAttribute(name = "useShortLabels") 1130 public boolean isUseShortLabels() { 1131 return this.useShortLabels; 1132 } 1133 1134 /** 1135 * {@inheritDoc} 1136 */ 1137 @Override 1138 public void setUseShortLabels(boolean useShortLabels) { 1139 this.useShortLabels = useShortLabels; 1140 } 1141 1142 /** 1143 * {@inheritDoc} 1144 */ 1145 @Override 1146 @BeanTagAttribute(name = "repeatHeader") 1147 public boolean isRepeatHeader() { 1148 return this.repeatHeader; 1149 } 1150 1151 /** 1152 * {@inheritDoc} 1153 */ 1154 @Override 1155 public void setRepeatHeader(boolean repeatHeader) { 1156 this.repeatHeader = repeatHeader; 1157 } 1158 1159 /** 1160 * {@inheritDoc} 1161 */ 1162 @Override 1163 @ViewLifecycleRestriction(UifConstants.ViewPhases.INITIALIZE) 1164 @BeanTagAttribute(name = "headerLabelPrototype", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1165 public Label getHeaderLabelPrototype() { 1166 return this.headerLabelPrototype; 1167 } 1168 1169 /** 1170 * {@inheritDoc} 1171 */ 1172 @Override 1173 public void setHeaderLabelPrototype(Label headerLabelPrototype) { 1174 this.headerLabelPrototype = headerLabelPrototype; 1175 } 1176 1177 /** 1178 * {@inheritDoc} 1179 */ 1180 @Override 1181 public List<Label> getHeaderLabels() { 1182 return this.headerLabels; 1183 } 1184 1185 /** 1186 * {@inheritDoc} 1187 */ 1188 @Override 1189 @BeanTagAttribute(name = "renderSequenceField") 1190 public boolean isRenderSequenceField() { 1191 return this.renderSequenceField; 1192 } 1193 1194 /** 1195 * {@inheritDoc} 1196 */ 1197 @Override 1198 public void setRenderSequenceField(boolean renderSequenceField) { 1199 this.renderSequenceField = renderSequenceField; 1200 } 1201 1202 /** 1203 * {@inheritDoc} 1204 */ 1205 @Override 1206 @BeanTagAttribute(name = "sequencePropertyName") 1207 public String getSequencePropertyName() { 1208 if ((getSequenceFieldPrototype() != null) && (getSequenceFieldPrototype() instanceof DataField)) { 1209 return ((DataField) getSequenceFieldPrototype()).getPropertyName(); 1210 } 1211 1212 return null; 1213 } 1214 1215 /** 1216 * {@inheritDoc} 1217 */ 1218 @Override 1219 public void setSequencePropertyName(String sequencePropertyName) { 1220 if ((getSequenceFieldPrototype() != null) && (getSequenceFieldPrototype() instanceof DataField)) { 1221 ((DataField) getSequenceFieldPrototype()).setPropertyName(sequencePropertyName); 1222 } 1223 } 1224 1225 /** 1226 * {@inheritDoc} 1227 */ 1228 @Override 1229 @BeanTagAttribute(name = "generateAutoSequence") 1230 public boolean isGenerateAutoSequence() { 1231 return this.generateAutoSequence; 1232 } 1233 1234 /** 1235 * {@inheritDoc} 1236 */ 1237 @Override 1238 public void setGenerateAutoSequence(boolean generateAutoSequence) { 1239 this.generateAutoSequence = generateAutoSequence; 1240 } 1241 1242 /** 1243 * {@inheritDoc} 1244 */ 1245 @Override 1246 @ViewLifecycleRestriction(UifConstants.ViewPhases.INITIALIZE) 1247 @BeanTagAttribute(name = "sequenceFieldPrototype", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1248 public Field getSequenceFieldPrototype() { 1249 return this.sequenceFieldPrototype; 1250 } 1251 1252 /** 1253 * {@inheritDoc} 1254 */ 1255 @Override 1256 public void setSequenceFieldPrototype(Field sequenceFieldPrototype) { 1257 this.sequenceFieldPrototype = sequenceFieldPrototype; 1258 } 1259 1260 /** 1261 * {@inheritDoc} 1262 */ 1263 @Override 1264 @ViewLifecycleRestriction(UifConstants.ViewPhases.INITIALIZE) 1265 @BeanTagAttribute(name = "actionFieldPrototype", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1266 public FieldGroup getActionFieldPrototype() { 1267 return this.actionFieldPrototype; 1268 } 1269 1270 /** 1271 * {@inheritDoc} 1272 */ 1273 @Override 1274 public void setActionFieldPrototype(FieldGroup actionFieldPrototype) { 1275 this.actionFieldPrototype = actionFieldPrototype; 1276 } 1277 1278 /** 1279 * {@inheritDoc} 1280 */ 1281 @Override 1282 @ViewLifecycleRestriction(UifConstants.ViewPhases.INITIALIZE) 1283 @BeanTagAttribute(name = "subCollectionFieldGroupPrototype", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1284 public FieldGroup getSubCollectionFieldGroupPrototype() { 1285 return this.subCollectionFieldGroupPrototype; 1286 } 1287 1288 /** 1289 * {@inheritDoc} 1290 */ 1291 @Override 1292 public void setSubCollectionFieldGroupPrototype(FieldGroup subCollectionFieldGroupPrototype) { 1293 this.subCollectionFieldGroupPrototype = subCollectionFieldGroupPrototype; 1294 } 1295 1296 /** 1297 * {@inheritDoc} 1298 */ 1299 @Override 1300 @ViewLifecycleRestriction(UifConstants.ViewPhases.INITIALIZE) 1301 @BeanTagAttribute(name = "selectFieldPrototype", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1302 public Field getSelectFieldPrototype() { 1303 return selectFieldPrototype; 1304 } 1305 1306 /** 1307 * {@inheritDoc} 1308 */ 1309 @Override 1310 public void setSelectFieldPrototype(Field selectFieldPrototype) { 1311 this.selectFieldPrototype = selectFieldPrototype; 1312 } 1313 1314 /** 1315 * {@inheritDoc} 1316 */ 1317 @Override 1318 @BeanTagAttribute(name = "separateAddLine") 1319 public boolean isSeparateAddLine() { 1320 return separateAddLine; 1321 } 1322 1323 /** 1324 * {@inheritDoc} 1325 */ 1326 @Override 1327 public void setSeparateAddLine(boolean separateAddLine) { 1328 this.separateAddLine = separateAddLine; 1329 } 1330 1331 /** 1332 * {@inheritDoc} 1333 */ 1334 @Override 1335 @BeanTagAttribute(name = "addLineGroup", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1336 public Group getAddLineGroup() { 1337 return addLineGroup; 1338 } 1339 1340 /** 1341 * {@inheritDoc} 1342 */ 1343 @Override 1344 public void setAddLineGroup(Group addLineGroup) { 1345 this.addLineGroup = addLineGroup; 1346 } 1347 1348 /** 1349 * {@inheritDoc} 1350 */ 1351 @Override 1352 public List<Field> getAllRowFields() { 1353 return this.allRowFields; 1354 } 1355 1356 /** 1357 * {@inheritDoc} 1358 */ 1359 @Override 1360 @ViewLifecycleRestriction 1361 public List<Field> getFirstRowFields() { 1362 return firstRowFields; 1363 } 1364 1365 /** 1366 * {@inheritDoc} 1367 */ 1368 @Override 1369 public Pager getPagerWidget() { 1370 return pagerWidget; 1371 } 1372 1373 /** 1374 * {@inheritDoc} 1375 */ 1376 @Override 1377 public void setPagerWidget(Pager pagerWidget) { 1378 this.pagerWidget = pagerWidget; 1379 } 1380 1381 /** 1382 * {@inheritDoc} 1383 */ 1384 @Override 1385 @BeanTagAttribute(name = "richTable", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1386 public RichTable getRichTable() { 1387 return this.richTable; 1388 } 1389 1390 /** 1391 * {@inheritDoc} 1392 */ 1393 @Override 1394 public void setRichTable(RichTable richTable) { 1395 this.richTable = richTable; 1396 } 1397 1398 /** 1399 * {@inheritDoc} 1400 */ 1401 @Override 1402 @BeanTagAttribute(name = "numberOfDataColumns") 1403 public int getNumberOfDataColumns() { 1404 return this.numberOfDataColumns; 1405 } 1406 1407 /** 1408 * {@inheritDoc} 1409 */ 1410 @Override 1411 public void setNumberOfDataColumns(int numberOfDataColumns) { 1412 this.numberOfDataColumns = numberOfDataColumns; 1413 } 1414 1415 /** 1416 * {@inheritDoc} 1417 */ 1418 @Override 1419 @BeanTagAttribute(name = "hiddenColumns", type = BeanTagAttribute.AttributeType.SETVALUE) 1420 public Set<String> getHiddenColumns() { 1421 if (richTable != null) { 1422 return richTable.getHiddenColumns(); 1423 } 1424 1425 return null; 1426 } 1427 1428 /** 1429 * {@inheritDoc} 1430 */ 1431 @Override 1432 public void setHiddenColumns(Set<String> hiddenColumns) { 1433 if (richTable != null) { 1434 richTable.setHiddenColumns(hiddenColumns); 1435 } 1436 } 1437 1438 /** 1439 * {@inheritDoc} 1440 */ 1441 @Override 1442 @BeanTagAttribute(name = "sortableColumns", type = BeanTagAttribute.AttributeType.SETVALUE) 1443 public Set<String> getSortableColumns() { 1444 if (richTable != null) { 1445 return richTable.getSortableColumns(); 1446 } 1447 1448 return null; 1449 } 1450 1451 /** 1452 * {@inheritDoc} 1453 */ 1454 @Override 1455 public void setSortableColumns(Set<String> sortableColumns) { 1456 if (richTable != null) { 1457 richTable.setSortableColumns(sortableColumns); 1458 } 1459 } 1460 1461 /** 1462 * {@inheritDoc} 1463 */ 1464 @Override 1465 @BeanTagAttribute(name = "actionColumnIndex") 1466 public int getActionColumnIndex() { 1467 return actionColumnIndex; 1468 } 1469 1470 /** 1471 * {@inheritDoc} 1472 */ 1473 @Override 1474 @BeanTagAttribute(name = "actionColumnPlacement") 1475 public String getActionColumnPlacement() { 1476 return actionColumnPlacement; 1477 } 1478 1479 /** 1480 * {@inheritDoc} 1481 */ 1482 @Override 1483 public void setActionColumnPlacement(String actionColumnPlacement) { 1484 this.actionColumnPlacement = actionColumnPlacement; 1485 1486 if ("LEFT".equals(actionColumnPlacement)) { 1487 actionColumnIndex = 1; 1488 } else if ("RIGHT".equals(actionColumnPlacement)) { 1489 actionColumnIndex = -1; 1490 } else if (StringUtils.isNumeric(actionColumnPlacement)) { 1491 actionColumnIndex = Integer.parseInt(actionColumnPlacement); 1492 } 1493 } 1494 1495 /** 1496 * {@inheritDoc} 1497 */ 1498 @Override 1499 @ViewLifecycleRestriction(UifConstants.ViewPhases.PRE_PROCESS) 1500 @BeanTagAttribute(name = "rowDetailsGroup", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1501 public Group getRowDetailsGroup() { 1502 return rowDetailsGroup; 1503 } 1504 1505 /** 1506 * @see TableLayoutManagerBase#getRowDetailsGroup() 1507 */ 1508 @Override 1509 public void setRowDetailsGroup(Group rowDetailsGroup) { 1510 this.rowDetailsGroup = rowDetailsGroup; 1511 } 1512 1513 /** 1514 * Creates the details group for the line using the information setup through the setter methods 1515 * of this interface. Line details are currently only supported in TableLayoutManagers which use 1516 * richTable. 1517 * 1518 * @param collectionGroup the CollectionGroup for this TableLayoutManager 1519 */ 1520 public void setupDetails(CollectionGroup collectionGroup) { 1521 if (getRowDetailsGroup() == null || this.getRichTable() == null || !this.getRichTable().isRender()) { 1522 return; 1523 } 1524 1525 //data attribute to mark this group to open itself when rendered 1526 collectionGroup.addDataAttribute(UifConstants.DataAttributes.DETAILS_DEFAULT_OPEN, Boolean.toString( 1527 this.rowDetailsOpen)); 1528 1529 FieldGroup detailsFieldGroup = ComponentFactory.getFieldGroup(); 1530 1531 TreeMap<String, String> dataAttributes = new TreeMap<String, String>(); 1532 dataAttributes.put(UifConstants.DataAttributes.ROLE, "detailsFieldGroup"); 1533 detailsFieldGroup.setDataAttributes(dataAttributes); 1534 1535 Action rowDetailsAction = this.getExpandDetailsActionPrototype(); 1536 rowDetailsAction.addDataAttribute(UifConstants.DataAttributes.ROLE, "detailsLink"); 1537 rowDetailsAction.setId(collectionGroup.getId() + UifConstants.IdSuffixes.DETAIL_LINK); 1538 1539 List<Component> detailsItems = new ArrayList<Component>(); 1540 detailsItems.add(rowDetailsAction); 1541 1542 dataAttributes = new TreeMap<String, String>(); 1543 dataAttributes.put("role", "details"); 1544 dataAttributes.put("open", Boolean.toString(this.rowDetailsOpen)); 1545 this.getRowDetailsGroup().setDataAttributes(dataAttributes); 1546 1547 detailsItems.add(getRowDetailsGroup()); 1548 detailsFieldGroup.setItems(detailsItems); 1549 detailsFieldGroup.setId(collectionGroup.getId() + UifConstants.IdSuffixes.DETAIL_GROUP); 1550 1551 if (ajaxDetailsRetrieval && !this.rowDetailsOpen) { 1552 this.getRowDetailsGroup().setRetrieveViaAjax(true); 1553 } else { 1554 this.getRowDetailsGroup().setHidden(true); 1555 } 1556 1557 detailsFieldGroup.setReadOnly(collectionGroup.isReadOnly()); 1558 1559 List<Component> theItems = new ArrayList<Component>(); 1560 theItems.add(detailsFieldGroup); 1561 theItems.addAll(collectionGroup.getItems()); 1562 1563 collectionGroup.setItems(theItems); 1564 } 1565 1566 /** 1567 * {@inheritDoc} 1568 */ 1569 @Override 1570 public List<String> getColumnsToCalculate() { 1571 return columnsToCalculate; 1572 } 1573 1574 /** 1575 * {@inheritDoc} 1576 */ 1577 @Override 1578 @BeanTagAttribute(name = "showTotal") 1579 public boolean isShowTotal() { 1580 return showTotal; 1581 } 1582 1583 /** 1584 * {@inheritDoc} 1585 */ 1586 @Override 1587 public void setShowTotal(boolean showTotal) { 1588 this.showTotal = showTotal; 1589 } 1590 1591 /** 1592 * {@inheritDoc} 1593 */ 1594 @Override 1595 @BeanTagAttribute(name = "showPageTotal") 1596 public boolean isShowPageTotal() { 1597 return showPageTotal; 1598 } 1599 1600 /** 1601 * {@inheritDoc} 1602 */ 1603 @Override 1604 public void setShowPageTotal(boolean showPageTotal) { 1605 this.showPageTotal = showPageTotal; 1606 } 1607 1608 /** 1609 * {@inheritDoc} 1610 */ 1611 @Override 1612 @BeanTagAttribute(name = "showGroupTotal") 1613 public boolean isShowGroupTotal() { 1614 return showGroupTotal; 1615 } 1616 1617 /** 1618 * {@inheritDoc} 1619 */ 1620 @Override 1621 public void setShowGroupTotal(boolean showGroupTotal) { 1622 this.showGroupTotal = showGroupTotal; 1623 } 1624 1625 /** 1626 * {@inheritDoc} 1627 */ 1628 @Override 1629 @BeanTagAttribute(name = "totalLabel", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1630 public Label getTotalLabel() { 1631 return totalLabel; 1632 } 1633 1634 /** 1635 * {@inheritDoc} 1636 */ 1637 @Override 1638 public void setTotalLabel(Label totalLabel) { 1639 this.totalLabel = totalLabel; 1640 } 1641 1642 /** 1643 * {@inheritDoc} 1644 */ 1645 @Override 1646 @BeanTagAttribute(name = "pageTotalLabel", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1647 public Label getPageTotalLabel() { 1648 return pageTotalLabel; 1649 } 1650 1651 /** 1652 * {@inheritDoc} 1653 */ 1654 @Override 1655 public void setPageTotalLabel(Label pageTotalLabel) { 1656 this.pageTotalLabel = pageTotalLabel; 1657 } 1658 1659 /** 1660 * {@inheritDoc} 1661 */ 1662 @Override 1663 @BeanTagAttribute(name = "groupTotalLabelPrototype", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1664 public Label getGroupTotalLabelPrototype() { 1665 return groupTotalLabelPrototype; 1666 } 1667 1668 /** 1669 * {@inheritDoc} 1670 */ 1671 @Override 1672 public void setGroupTotalLabelPrototype(Label groupTotalLabelPrototype) { 1673 this.groupTotalLabelPrototype = groupTotalLabelPrototype; 1674 } 1675 1676 /** 1677 * {@inheritDoc} 1678 */ 1679 @Override 1680 @BeanTagAttribute(name = "columnCalculations", type = BeanTagAttribute.AttributeType.LISTBEAN) 1681 public List<ColumnCalculationInfo> getColumnCalculations() { 1682 return columnCalculations; 1683 } 1684 1685 /** 1686 * {@inheritDoc} 1687 */ 1688 @Override 1689 public void setColumnCalculations(List<ColumnCalculationInfo> columnCalculations) { 1690 this.columnCalculations = columnCalculations; 1691 } 1692 1693 /** 1694 * {@inheritDoc} 1695 */ 1696 @Override 1697 @BeanTagAttribute(name = "renderOnlyLeftTotalLabels") 1698 public boolean isRenderOnlyLeftTotalLabels() { 1699 return renderOnlyLeftTotalLabels; 1700 } 1701 1702 /** 1703 * {@inheritDoc} 1704 */ 1705 @Override 1706 public void setRenderOnlyLeftTotalLabels(boolean renderOnlyLeftTotalLabels) { 1707 this.renderOnlyLeftTotalLabels = renderOnlyLeftTotalLabels; 1708 } 1709 1710 /** 1711 * {@inheritDoc} 1712 */ 1713 @Override 1714 public List<Component> getFooterCalculationComponents() { 1715 return footerCalculationComponents; 1716 } 1717 1718 /** 1719 * {@inheritDoc} 1720 */ 1721 @Override 1722 @BeanTagAttribute(name = "groupingPropertyNames", type = BeanTagAttribute.AttributeType.LISTVALUE) 1723 public List<String> getGroupingPropertyNames() { 1724 return groupingPropertyNames; 1725 } 1726 1727 /** 1728 * {@inheritDoc} 1729 */ 1730 @Override 1731 public void setGroupingPropertyNames(List<String> groupingPropertyNames) { 1732 this.groupingPropertyNames = groupingPropertyNames; 1733 } 1734 1735 /** 1736 * {@inheritDoc} 1737 */ 1738 @Override 1739 @BeanTagAttribute(name = "groupingTitle") 1740 public String getGroupingTitle() { 1741 return groupingTitle; 1742 } 1743 1744 /** 1745 * {@inheritDoc} 1746 */ 1747 @Override 1748 public void setGroupingTitle(String groupingTitle) { 1749 if (groupingTitle != null && !groupingTitle.contains("@{")) { 1750 throw new RuntimeException("groupingTitle MUST contain a springEL expression to uniquely" 1751 + " identify a collection group (often related to some value of the line). " 1752 + "Value provided: " 1753 + this.getGroupingTitle()); 1754 } 1755 this.groupingTitle = groupingTitle; 1756 } 1757 1758 /** 1759 * {@inheritDoc} 1760 */ 1761 @Override 1762 @BeanTagAttribute(name = "groupingPrefix") 1763 public String getGroupingPrefix() { 1764 return groupingPrefix; 1765 } 1766 1767 /** 1768 * {@inheritDoc} 1769 */ 1770 @Override 1771 public void setGroupingPrefix(String groupingPrefix) { 1772 this.groupingPrefix = groupingPrefix; 1773 } 1774 1775 /** 1776 * {@inheritDoc} 1777 */ 1778 @Override 1779 public boolean isRowDetailsOpen() { 1780 return rowDetailsOpen; 1781 } 1782 1783 /** 1784 * {@inheritDoc} 1785 */ 1786 @Override 1787 public void setRowDetailsOpen(boolean rowDetailsOpen) { 1788 this.rowDetailsOpen = rowDetailsOpen; 1789 } 1790 1791 /** 1792 * {@inheritDoc} 1793 */ 1794 @Override 1795 public boolean isShowToggleAllDetails() { 1796 return showToggleAllDetails; 1797 } 1798 1799 /** 1800 * {@inheritDoc} 1801 */ 1802 @Override 1803 public void setShowToggleAllDetails(boolean showToggleAllDetails) { 1804 this.showToggleAllDetails = showToggleAllDetails; 1805 } 1806 1807 /** 1808 * {@inheritDoc} 1809 */ 1810 @Override 1811 public Action getToggleAllDetailsAction() { 1812 return toggleAllDetailsAction; 1813 } 1814 1815 /** 1816 * {@inheritDoc} 1817 */ 1818 @Override 1819 public void setToggleAllDetailsAction(Action toggleAllDetailsAction) { 1820 this.toggleAllDetailsAction = toggleAllDetailsAction; 1821 } 1822 1823 /** 1824 * {@inheritDoc} 1825 */ 1826 @Override 1827 public boolean isAjaxDetailsRetrieval() { 1828 return ajaxDetailsRetrieval; 1829 } 1830 1831 /** 1832 * {@inheritDoc} 1833 */ 1834 @Override 1835 public void setAjaxDetailsRetrieval(boolean ajaxDetailsRetrieval) { 1836 this.ajaxDetailsRetrieval = ajaxDetailsRetrieval; 1837 } 1838 1839 /** 1840 * {@inheritDoc} 1841 */ 1842 @ViewLifecycleRestriction(UifConstants.ViewPhases.INITIALIZE) 1843 @Override 1844 public Action getExpandDetailsActionPrototype() { 1845 return expandDetailsActionPrototype; 1846 } 1847 1848 /** 1849 * {@inheritDoc} 1850 */ 1851 @Override 1852 public int getGroupingColumnIndex() { 1853 return groupingColumnIndex; 1854 } 1855 1856 /** 1857 * {@inheritDoc} 1858 */ 1859 @Override 1860 public void setExpandDetailsActionPrototype(Action expandDetailsActionPrototype) { 1861 this.expandDetailsActionPrototype = expandDetailsActionPrototype; 1862 } 1863 1864 /** 1865 * Set the header labels 1866 * 1867 * @param headerLabels 1868 */ 1869 protected void setHeaderLabels(List<Label> headerLabels) { 1870 this.headerLabels = headerLabels; 1871 } 1872 1873 /** 1874 * Set the row fields 1875 * 1876 * @param allRowFields 1877 */ 1878 protected void setAllRowFields(List<Field> allRowFields) { 1879 this.allRowFields = allRowFields; 1880 } 1881 1882 /** 1883 * Set the first row fields 1884 * 1885 * @param firstRowFields 1886 */ 1887 protected void setFirstRowFields(List<Field> firstRowFields) { 1888 this.firstRowFields = firstRowFields; 1889 } 1890 1891 /** 1892 * Set flag of whether a header is added 1893 * 1894 * @param headerAdded 1895 */ 1896 protected void setHeaderAdded(boolean headerAdded) { 1897 this.headerAdded = headerAdded; 1898 } 1899 1900 /** 1901 * Sets action column index 1902 * 1903 * @param actionColumnIndex 1904 */ 1905 protected void setActionColumnIndex(int actionColumnIndex) { 1906 this.actionColumnIndex = actionColumnIndex; 1907 } 1908 1909 /** 1910 * Set grouping column index 1911 * 1912 * @param groupingColumnIndex 1913 */ 1914 protected void setGroupingColumnIndex(int groupingColumnIndex) { 1915 this.groupingColumnIndex = groupingColumnIndex; 1916 } 1917 1918 /** 1919 * Set flag generate group total rows 1920 * 1921 * @param generateGroupTotalRows 1922 */ 1923 protected void setGenerateGroupTotalRows(boolean generateGroupTotalRows) { 1924 this.generateGroupTotalRows = generateGroupTotalRows; 1925 } 1926 1927 /** 1928 * Set columns to calculate 1929 * 1930 * @param columnsToCalculate 1931 */ 1932 protected void setColumnsToCalculate(List<String> columnsToCalculate) { 1933 this.columnsToCalculate = columnsToCalculate; 1934 } 1935 1936 /** 1937 * Set footer calculation components 1938 * 1939 * @param footerCalculationComponents 1940 */ 1941 protected void setFooterCalculationComponents(List<Component> footerCalculationComponents) { 1942 this.footerCalculationComponents = footerCalculationComponents; 1943 } 1944 1945 /** 1946 * The row css classes for the rows of this layout 1947 * 1948 * <p> 1949 * To set a css class on all rows, use "all" as a key. To set a class for even rows, use "even" 1950 * as a key, for odd rows, use "odd". Use a one-based index to target a specific row by index. 1951 * SpringEL can be used as a key and the expression will be evaluated; if evaluated to true, the 1952 * class(es) specified will be applied. 1953 * </p> 1954 * 1955 * @return a map which represents the css classes of the rows of this layout 1956 */ 1957 @BeanTagAttribute(name = "conditionalRowCssClasses", type = BeanTagAttribute.AttributeType.MAPVALUE) 1958 public Map<String, String> getConditionalRowCssClasses() { 1959 return conditionalRowCssClasses; 1960 } 1961 1962 /** 1963 * Set the conditionalRowCssClasses 1964 * 1965 * @param conditionalRowCssClasses 1966 */ 1967 public void setConditionalRowCssClasses(Map<String, String> conditionalRowCssClasses) { 1968 this.conditionalRowCssClasses = conditionalRowCssClasses; 1969 } 1970 1971 /** 1972 * Validates different requirements of component compiling a series of reports detailing 1973 * information on errors found in the component. Used by the RiceDictionaryValidator. 1974 * 1975 * @param tracer record of component's location 1976 */ 1977 public void completeValidation(ValidationTrace tracer) { 1978 tracer.addBean("TableLayoutManager", getId()); 1979 1980 if (getRowDetailsGroup() != null) { 1981 boolean validTable = false; 1982 if (getRichTable() != null) { 1983 if (getRichTable().isRender()) { 1984 validTable = true; 1985 } 1986 } 1987 if (!validTable) { 1988 String currentValues[] = {"rowDetailsGroup =" + getRowDetailsGroup(), "richTable =" + getRichTable()}; 1989 tracer.createError("If rowDetailsGroup is set richTable must be set and its render true", 1990 currentValues); 1991 } 1992 1993 } 1994 } 1995 1996 }