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 }