/*
* #%L
* carewebframework
* %%
* Copyright (C) 2008 - 2016 Regenstrief Institute, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This Source Code Form is also subject to the terms of the Health-Related
* Additional Disclaimer of Warranty and Limitation of Liability available at
*
* http://www.carewebframework.org/licensing/disclaimer.
*
* #L%
*/
package org.carewebframework.ui.zk;
import org.apache.commons.lang.StringUtils;
import org.carewebframework.common.StrUtil;
import org.zkoss.zk.ui.Component;
import org.zkoss.zul.Cell;
import org.zkoss.zul.Column;
import org.zkoss.zul.Columns;
import org.zkoss.zul.Detail;
import org.zkoss.zul.Grid;
import org.zkoss.zul.Group;
import org.zkoss.zul.Row;
import org.zkoss.zul.RowRenderer;
import org.zkoss.zul.Rows;
/**
* Base row renderer.
*
* @param <T> Data type of row-associated object.
* @param <G> Data type of group-associated object.
*/
public abstract class AbstractRowRenderer<T, G> extends AbstractRenderer implements RowRenderer<T> {
private static final String ATTR_DETAIL = AbstractRowRenderer.class.getName() + ".detail";
private static final String ATTR_DETAIL_EXPAND = ATTR_DETAIL + ".expand";
private static final String ATTR_GROUP_EXPAND = AbstractRowRenderer.class.getName() + ".group.expand";
/**
* Associates the detail view with the specified row. This allows better performance when
* changing the expand detail state by avoiding iterating over the component tree to find the
* detail component.
*
* @param row Row with which to associate the detail.
* @param detail The detail.
*/
private static void associateDetail(Row row, Detail detail) {
row.setAttribute(ATTR_DETAIL, detail);
}
/**
* Returns the detail associated with the row.
*
* @param row The row whose associated detail is sought.
* @return The associated detail, or null if none.
*/
private static Detail getDetail(Row row) {
return row.hasAttribute(ATTR_DETAIL) ? (Detail) row.getAttribute(ATTR_DETAIL) : row.getDetailChild();
}
/**
* Updates the detail open state for all detail views in the grid.
*
* @param grid The grid.
* @param detailOpen The open state for detail views. If null, the open state for detail views
* is unaffected.
* @param groupOpen The open state for groups. If null, the open state for groups is unaffected.
*/
public static void expandAll(Grid grid, Boolean detailOpen, Boolean groupOpen) {
Rows rows = grid.getRows();
if (rows != null) {
for (Row row : rows.<Row> getChildren()) {
if (groupOpen != null && row instanceof Group) {
((Group) row).setOpen(groupOpen);
}
if (detailOpen != null) {
Detail detail = getDetail(row);
if (detail != null) {
detail.setOpen(detailOpen);
}
}
}
}
}
/**
* Returns the default detail expansion state for the grid.
*
* @param grid The grid.
* @return The default detail expansion state.
*/
public static boolean getExpandDetail(Grid grid) {
Boolean expandDetail = (Boolean) grid.getAttribute(ATTR_DETAIL_EXPAND);
return expandDetail != null && expandDetail;
}
/**
* Sets the default detail expansion state for the grid and for any existing detail views within
* the grid.
*
* @param grid The grid.
* @param value The default detail expansion state.
*/
public static void setExpandDetail(Grid grid, boolean value) {
boolean oldValue = getExpandDetail(grid);
if (oldValue != value) {
grid.setAttribute(ATTR_DETAIL_EXPAND, value);
expandAll(grid, value, null);
}
}
/**
* Returns the default group expansion state for the grid.
*
* @param grid The grid.
* @return The default group expansion state.
*/
public static boolean getExpandGroup(Grid grid) {
Boolean expandGroup = (Boolean) grid.getAttribute(ATTR_GROUP_EXPAND);
return expandGroup == null || expandGroup;
}
/**
* Sets the default group expansion state for all groups within the grid.
*
* @param grid The grid.
* @param value The default group expansion state.
*/
public static void setExpandGroup(Grid grid, boolean value) {
boolean oldValue = getExpandGroup(grid);
if (oldValue != value) {
grid.setAttribute(ATTR_GROUP_EXPAND, value);
expandAll(grid, null, value);
}
}
/**
* No args Constructor
*/
public AbstractRowRenderer() {
super();
}
/**
* @param rowStyle Style to be applied to each rendered row.
* @param cellStyle Style to be applied to each cell.
*/
public AbstractRowRenderer(String rowStyle, String cellStyle) {
super(rowStyle, cellStyle);
}
/**
* Row rendering logic.
*
* @param row Row being rendered.
* @param object The data object associated with the row.
* @return Parent component for detail. If null, no detail will be created.
*/
protected abstract Component renderRow(Row row, T object);
/**
* Groups rendering logic.
*
* @param group Group being rendered.
* @param object The data object associated with the group.
*/
protected void renderGroup(Group group, G object) {
group.setLabel(object.toString());
}
/**
* Detail rendering logic.
*
* @param detail The detail being rendered.
* @param object The data object associated with the row.
*/
protected void renderDetail(Detail detail, T object) {
}
/**
* @see org.zkoss.zul.RowRenderer#render(org.zkoss.zul.Row, java.lang.Object, int)
*/
@SuppressWarnings("unchecked")
@Override
public final void render(Row row, Object object, int index) throws Exception {
row.setValue(object);
if (row instanceof Group) {
Group group = (Group) row;
group.setOpen(getExpandGroup(row.getGrid()));
renderGroup(group, (G) object);
return;
}
row.setStyle(compStyle);
row.setValign("middle");
Component detailParent = renderRow(row, (T) object);
if (detailParent != null) {
Detail detail = createDetail(row, detailParent);
renderDetail(detail, (T) object);
if (detail.getFirstChild() == null) {
detail.setVisible(false);
}
} else {
associateDetail(row, null);
}
}
/**
* Creates a cell containing a label with the specified parameters.
*
* @param parent Component that will be the parent of the cell.
* @param value Value to be used as label text.
* @return The newly created cell.
*/
public Cell createCell(Component parent, Object value) {
return createCell(parent, value, null);
}
/**
* Creates a cell containing a label with the specified parameters.
*
* @param parent Component that will be the parent of the cell.
* @param value Value to be used as label text.
* @param prefix Value to be used as a prefix for the label text.
* @return The newly created cell.
*/
public Cell createCell(Component parent, Object value, String prefix) {
return createCell(parent, value, prefix, null);
}
/**
* Creates a cell containing a label with the specified parameters.
*
* @param parent Component that will be the parent of the cell.
* @param value Value to be used as label text.
* @param prefix Value to be used as a prefix for the label text.
* @param style Style to be applied to the label.
* @return The newly created cell.
*/
public Cell createCell(Component parent, Object value, String prefix, String style) {
return createCell(parent, value, prefix, style, null);
}
/**
* Creates a cell containing a label with the specified parameters.
*
* @param parent Component that will be the parent of the cell.
* @param value Value to be used as label text.
* @param prefix Value to be used as a prefix for the label text.
* @param style Style to be applied to the label.
* @param width Width of the cell.
* @return The newly created cell.
*/
public Cell createCell(Component parent, Object value, String prefix, String style, String width) {
return createCell(parent, value, prefix, style, width, Cell.class);
}
/**
* If a value to be added is empty or null, rather than creating a new cell with no content,
* will increase the span count of the preceding cell by one.
*
* @param parent Parent for a newly created cell.
* @param cell The preceding cell, or null to force new cell creation.
* @param value The content for the new cell.
* @return If the previous cell was re-used, this is returned. Otherwise, returns a new cell.
*/
public Cell createOrMergeCell(Component parent, Cell cell, Object value) {
return createOrMergeCell(parent, cell, value, null);
}
/**
* If a value to be added is empty or null, rather than creating a new cell with no content,
* will increase the span count of the preceding cell by one.
*
* @param parent Parent for a newly created cell.
* @param cell The preceding cell, or null to force new cell creation.
* @param value The content for the new cell.
* @param prefix Text prefix for content.
* @return If the previous cell was re-used, this is returned. Otherwise, returns a new cell.
*/
public Cell createOrMergeCell(Component parent, Cell cell, Object value, String prefix) {
return createOrMergeCell(parent, cell, value, prefix, null);
}
/**
* If a value to be added is empty or null, rather than creating a new cell with no content,
* will increase the span count of the preceding cell by one.
*
* @param parent Parent for a newly created cell.
* @param cell The preceding cell, or null to force new cell creation.
* @param value The content for the new cell.
* @param prefix Text prefix for content.
* @param style Optional style for label.
* @return If the previous cell was re-used, this is returned. Otherwise, returns a new cell.
*/
public Cell createOrMergeCell(Component parent, Cell cell, Object value, String prefix, String style) {
value = value instanceof String ? StringUtils.trimToNull(createLabelText(value, prefix)) : value;
if (cell == null || value != null) {
cell = createCell(parent, value, null, style, "100%");
cell.setColspan(1);
} else {
cell.setColspan(cell.getColspan() + 1);
}
return cell;
}
/**
* Creates a grid for detail view.
*
* @param parent The detail parent.
* @param colWidths Array of column widths.
* @return The detail grid.
*/
public Grid createDetailGrid(Component parent, String[] colWidths) {
return createDetailGrid(parent, colWidths, null);
}
/**
* Creates a grid for detail view.
*
* @param parent The detail parent.
* @param colWidths Array of column widths.
* @param colLabels Array of column labels (may be null).
* @return The detail grid.
*/
public Grid createDetailGrid(Component parent, String[] colWidths, String[] colLabels) {
Grid detailGrid = new Grid();
detailGrid.setOddRowSclass("none");
detailGrid.setWidth("100%");
detailGrid.setParent(parent);
Columns detailColumns = new Columns();
detailColumns.setSizable(true);
detailColumns.setParent(detailGrid);
int cols = Math.max(colWidths == null ? 0 : colWidths.length, colLabels == null ? 0 : colLabels.length);
for (int i = 0; i < cols; i++) {
String colLabel = colLabels == null || i >= colLabels.length ? " " : StrUtil.formatMessage(colLabels[i]);
Column col = new Column(colLabel);
String colWidth = colWidths == null || i >= colWidths.length ? null : colWidths[i];
col.setWidth(colWidth);
col.setParent(detailColumns);
}
detailGrid.appendChild(new Rows());
return detailGrid;
}
private Detail createDetail(Row row, Component parent) {
Detail detail = new Detail();
detail.setParent(parent);
associateDetail(row, detail);
detail.setOpen(getExpandDetail(row.getGrid()));
return detail;
}
}