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