/*******************************************************************************
* Copyright 2015 xWic group (http://www.xwic.de)
*
* 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.
*
*******************************************************************************/
package de.jwic.controls.tableviewer;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.jwic.base.Control;
import de.jwic.base.Field;
import de.jwic.base.IControlRenderer;
import de.jwic.base.ImageRef;
import de.jwic.base.JWicRuntime;
import de.jwic.base.RenderContext;
import de.jwic.data.IContentProvider;
import de.jwic.data.Range;
/**
* Default implementation of the ITableRenderer interface. The default renderer
* generates HTML "by code", without using templates. The reason for this is
* because its faster, costs less resources and the generated HTML is quite
* complex so that templates would be quite ugly anyway.
*
* @author Florian Lippisch
*/
public class DefaultTableRenderer implements ITableRenderer, Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/** default icon used for sort-up image */
public final static ImageRef ICON_SORTUP = new ImageRef("/jwic/gfx/sortup.gif");
/** default icon used for sort-down image */
public final static ImageRef ICON_SORTDOWN = new ImageRef("/jwic/gfx/sortdn.gif");
/** default icon used for expand image */
public final static ImageRef ICON_EXPAND = new ImageRef("/jwic/gfx/expand.png");
/** default icon used for collapse image */
public final static ImageRef ICON_COLLAPSE = new ImageRef("/jwic/gfx/collapse.png");
/** default icon used for indention */
public final static ImageRef ICON_CLEAR = new ImageRef("/jwic/gfx/clear.gif");
protected transient Log log = LogFactory.getLog(getClass());
private boolean expandLinkSpacing = true;
private int expandIconWidth = 19;
private int expandIconHeight = 16;
/*
* (non-Javadoc)
*
* @see de.jwic.ecolib.tableviewer.ITableRenderer#renderTable(de.jwic.base.
* RenderContext, de.jwic.ecolib.tableviewer.TableViewer,
* de.jwic.ecolib.tableviewer.TableModel)
*/
public void renderTable(RenderContext renderContext, TableViewer viewer, TableModel model,
ITableLabelProvider labelProvider) {
// make sure to load the TableViewer library
renderContext.addRequiredJSContent(TableViewer.class.getName().replace('.', '/') + ".static.js");
renderContext.addScript(viewer.getControlID(),
"{ afterUpdate: function(element) {JWic.controls.TableViewer.initialize(element, '"
+ viewer.getControlID() + "', {" + " colResize : " + viewer.isResizeableColumns() + " ,menu : "
+ (viewer.getMenu() != null ? "\'" + viewer.getMenu().getControlID() + "\'" : "null")
+ " ,fitToParent:" + model.isFitToParent() + "});}}");
String tblvGfxPath = JWicRuntime.getJWicRuntime().getContextPath() + "/jwic/gfx/";
PrintWriter writer = renderContext.getWriter();
IContentProvider<?> contentProvider = model.getContentProvider();
int tblWidth = 0;
for (Iterator<TableColumn> it = model.getColumnIterator(); it.hasNext();) {
TableColumn tc = it.next();
if (!tc.isVisible()) {
continue;
}
tblWidth += tc.getWidth();
}
// Add resizer div
// if (viewer.isResizeableColumns()) {
// writer.print("<DIV id=\"tblViewResizer_" + viewer.getControlID() +
// "\" class=\"tblViewResizer\" ");
// /*writer.print("onMouseUp=\"tblViewer_resizeColumnDone()\"" +
// " onMouseMove=\"tblViewer_resizeColumMove()\"");*/
// writer.print(" style=\"height: " + (viewer.getHeight() != 0 ?
// viewer.getHeight() : 20) + "px\"");
// writer.println("></DIV>");
// }
// render main table.
writer.print("<table cellspacing=\"0\" cellpadding=\"0\" class=\"" + viewer.getCssClass() + "\"");
if (viewer.isFillWidth()) {
writer.print(" width=\"100%\"");
} else {
if (viewer.getWidth() != 0) {
writer.print(" width=\"" + viewer.getWidth() + "\"");
}
// FLI: the table does not need a height...
/*
* if (viewer.getHeight() != 0) { writer.print(" height=\"" +
* viewer.getHeight() + "\""); }
*/
}
writer.println(">");
writer.print("<tr><td>");
// render data table.
String divHeight = viewer.getHeight() != 0
? (viewer.isShowStatusBar() ? viewer.getHeight() - 18 : viewer.getHeight()) + "px" : "100%";
String divWidth = viewer.getWidth() != 0 ? viewer.getWidth() + "px" : "100%";
// main table content DIV
writer.print("<div class=\"tblContent\"");
writer.print(" id=\"tblContent_" + viewer.getControlID() + "\"");
writer.print(" style=\"height: " + divHeight + "; width: " + divWidth + "; overflow: hidden\">");
if (viewer.isScrollable() && viewer.isShowHeader()) {
int tmpWidth = viewer.getWidth() != 0 ? viewer.getWidth() : 300;
writer.print("<DIV id=\"tblViewHead_" + viewer.getControlID() + "\" class=\"tblViewHead\" ");
writer.print("style=\"width: " + tmpWidth + "px; ");
writer.print("height: 20px; overflow: hidden;");
writer.print("\">");
}
// create required table attributes.
StringBuffer sbTblSelAttrs = new StringBuffer();
switch (model.getSelectionMode()) {
case TableModel.SELECTION_SINGLE: {
String clearKey = model.getFirstSelectedKey();
if (clearKey == null) {
clearKey = "";
}
sbTblSelAttrs.append(" tbvSelKey=\"" + clearKey + "\"");
sbTblSelAttrs.append(" tbvSelMode=\"single\"");
break;
}
case TableModel.SELECTION_MULTI: {
sbTblSelAttrs.append(" tbvSelKey=\"\"");
sbTblSelAttrs.append(" tbvSelMode=\"multi\"");
break;
}
default: {
sbTblSelAttrs.append(" tbvSelKey=\"\"");
sbTblSelAttrs.append(" tbvSelMode=\"none\"");
}
}
if (viewer.isShowHeader() || !viewer.isScrollable()) {
writer.print("<table");
writer.print(" tbvctrlid=\"" + viewer.getControlID() + "\"");
writer.print(" id=\"tblViewData_" + viewer.getControlID() + "\"");
writer.print(" class=\"tblData\" cellspacing=\"0\" cellpadding=\"0\" ");
if (viewer.isScrollable()) {
// must add a width attribute, otherwise table-layout: fixed
// isnt working on Mozilla
writer.print(" width=\"" + tblWidth + "\" ");
}
writer.print(sbTblSelAttrs);
writer.println(">");
}
// render HEADER columns
if (viewer.isShowHeader()) {
renderHeader(writer, model, viewer, tblvGfxPath);
}
if (viewer.isScrollable()) {
// if scrollable, seperate the data table from the header
// and render it within its own, scrollable DIV. Will only
// look proper if the columns have a fixed width.
int dataHeight = viewer.getHeight() != 0 ? viewer.getHeight() - (viewer.isShowHeader() ? 20 : 0) : 200;
if (viewer.isShowStatusBar()) {
dataHeight -= 18;
}
int dataWidth = viewer.getWidth() != 0 ? viewer.getWidth() : 300;
if (viewer.isShowHeader()) {
writer.println("</TABLE></DIV>");
}
writer.print("<DIV onscroll=\"JWic.controls.TableViewer.handleScroll(event, '" + viewer.getControlID()
+ "')\" style=\"");
writer.print("width: " + dataWidth + "px; height: " + dataHeight + "px; overflow: auto;");
writer.print("\" id=\"tblViewDataLayer_" + viewer.getControlID() + "\" class=\"tblViewDataLayer\" ");
writer.println(">");
writer.print("<table");
writer.print(" tbvctrlid=\"" + viewer.getControlID() + "\"");
writer.print(" id=\"tblViewDataTbl_" + viewer.getControlID() + "\"");
writer.print(" class=\"tblData\" cellspacing=\"0\" cellpadding=\"0\" width=\"" + tblWidth + "\"");
writer.print(sbTblSelAttrs);
writer.println(">");
}
Range range = model.getRange();
if (range.getMax() == 0) { // = Auto
int max = -1;
int rowSpace = viewer.getHeight() - (35 + 20); // - header height +
// scroll bar height
if (rowSpace != 0) {
if (viewer.isShowStatusBar()) {
rowSpace -= 18;
}
max = rowSpace / viewer.getRowHeightHint(); //
if (max < 1) {
max = 1;
}
model.setLastRenderedPageSize(max);
}
range = new Range(range.getStart(), max);
}
// render table BODY
writer.println("<TBODY>");
try {
int count = renderRows(0, false, writer, contentProvider.getContentIterator(range), viewer, labelProvider);
if (count == 0) {
// if no rows exist, then print an empty row just to preserve
// the scrolling of the header
renderEmptyRow(writer, viewer);
}
model.setLastRowRenderCount(count);
} catch (Exception e) {
writer.println("Error reading data from ContentProvider: " + e);
log.error("Error reading data from ContentProvider", e);
}
writer.println("</TBODY>");
writer.println("</table>");
if (viewer.isScrollable()) {
writer.println("</DIV>");
}
writer.println("</div></td></tr>");
// render STATUS BAR
if (viewer.isShowStatusBar()) {
writer.println("<tr><td>");
// render context
Control sb = viewer.getControl("statusBar");
IControlRenderer renderer = JWicRuntime.getRenderer(sb.getRendererId());
renderer.renderControl(sb, renderContext);
writer.print("</td></tr>");
}
writer.println("</table>");
if (viewer.isScrollable()) {
// add scroll fields
Field fldLeft = viewer.getField("left");
Field fldTop = viewer.getField("top");
writer.println(
"<INPUT TYPE=\"HIDDEN\" NAME=\"" + fldLeft.getId() + "\" VALUE=\"" + fldLeft.getValue() + "\">");
writer.println(
"<INPUT TYPE=\"HIDDEN\" NAME=\"" + fldTop.getId() + "\" VALUE=\"" + fldTop.getValue() + "\">");
writer.println("<script language=\"javascript\">");
writer.println("window.setTimeout(\"JWic.restoreScrolling('" + viewer.getControlID()
+ "', 'tblViewDataLayer_" + viewer.getControlID() + "');\", 0);");
writer.println("</script>");
}
}
/**
*
*/
protected void renderHeader(PrintWriter writer, TableModel model, TableViewer viewer, String tblGfxPath) {
boolean isResizable = viewer.isResizeableColumns() && viewer.isEnabled();
boolean isColSelectable = viewer.isSelectableColumns() && viewer.isEnabled();
writer.println("<THEAD>");
writer.println("<tr>");
for (Iterator<TableColumn> itC = model.getColumnIterator(); itC.hasNext();) {
TableColumn column = itC.next();
if (!column.isVisible()) {
continue;
}
if (isResizable && column.getWidth() == 0) {
// must set a default width if resizeable columns is activated
column.setWidth(150);
}
writer.print("<th");
int innerWidth = 0;
if (column.getWidth() > 0) {
writer.print(" width=\"" + column.getWidth() + "\"");
innerWidth = column.getWidth() - (viewer.isResizeableColumns() ? 5 : 0);
innerWidth = innerWidth - (column.getSortIcon() != TableColumn.SORT_ICON_NONE ? 8 : 0);
if (innerWidth < 3) {
innerWidth = 3;
}
}
writer.print(" colIdx=\"" + column.getIndex() + "\"");
// header tooltip
if (column.getToolTip() != null && column.getToolTip().length() > 0) {
writer.print(" title=\"" + column.getToolTip() + "\"");
}
writer.println(">");
// create cell table
writer.print("<TABLE class=\"tbvColHeader\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><TR>");
writer.print("<TD class=\"tbvColHeadCell\" width=\"" + innerWidth + "\"");
if (isColSelectable) {
writer.print(" onClick=\"JWic.fireAction('" + viewer.getControlID() + "', 'columnSelection', '"
+ column.getIndex() + "')\"");
writer.print(" onMouseDown=\"JWic.controls.TableViewer.pushColumn(" + column.getIndex() + ", '"
+ viewer.getControlID() + "')\"");
writer.print(" onMouseUp=\"JWic.controls.TableViewer.releaseColumn()\"");
writer.print(" onMouseOut=\"JWic.controls.TableViewer.releaseColumn()\"");
}
writer.print(">");
writer.print("<NOBR>");
if (column.getImage() != null) {
writer.print("<IMG SRC=\"" + column.getImage().getPath() + "\" border=0/>");
}
writer.print(column.getTitle());
writer.print("</NOBR>");
writer.print("</TD>");
if (column.getSortIcon() != TableColumn.SORT_ICON_NONE) {
ImageRef imgSort = null;
switch (column.getSortIcon()) {
case TableColumn.SORT_ICON_UP:
imgSort = ICON_SORTUP;
break;
case TableColumn.SORT_ICON_DOWN:
imgSort = ICON_SORTDOWN;
break;
}
if (imgSort != null) {
writer.print("<TD class=\"tbvColHeadCell\" width=\"8\">");
writer.print("<IMG SRC=\"" + imgSort.getPath() + "\" border=0>");
writer.print("</TD>");
}
}
if (isResizable) {
writer.print("<TD class=\"tbvColHeadCellPoint\" width=\"3\"><IMG SRC=\"" + tblGfxPath
+ "resizer.gif\" width=\"3\" height=\"13\"");
writer.print(" colIdx=\"" + column.getIndex() + "\"");
// writer.print("
// onMouseDown=\"JWic.controls.TableViewer.resizeColumn(event,
// '" + viewer.getControlID() + "')\"");
writer.print(" class=\"tblResize\" border=0>");
writer.print("</TD>");
}
writer.print("</TR></TABLE>");
writer.println("</th>");
}
// if the width is fixed, we must render an empty column at the end so
// that the
// browser will not adjust the columns width
if (viewer.getWidth() != 0) {
if (viewer.isScrollable()) {
writer.println("<TH width=\"" + viewer.getWidth() + "\"> </TH>");
} else {
writer.println("<TH> </TH>");
}
}
writer.println("</tr>");
writer.println("</THEAD>");
}
/**
* @param writer
* @param rootIterator
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected int renderRows(int level, boolean hasNext, PrintWriter writer, Iterator<?> it, TableViewer viewer,
ITableLabelProvider labelProvider) {
TableModel model = viewer.getModel();
IContentProvider contentProvider = model.getContentProvider();
int count = 0;
while (it.hasNext()) {
Object row = it.next();
count++;
String key = contentProvider.getUniqueKey(row);
boolean expanded = model.isExpanded(key);
writer.print("<tr");
String rowCssClass = "";
if (!(it.hasNext() || hasNext || contentProvider.hasChildren(row))) {
rowCssClass = "lastRow";
}
// handle selection
writer.print(" tbvRowKey=\"" + key + "\"");
if (model.getSelectionMode() != TableModel.SELECTION_NONE) {
if (viewer.isEnabled()) {
writer.print(" onClick=\"JWic.controls.TableViewer.clickRow(this, event)\"");
writer.print(" onDblClick=\"JWic.controls.TableViewer.clickRow(this, event, true)\"");
}
if (model.isSelected(key)) {
rowCssClass = rowCssClass + "selected";
}
}
if (rowCssClass.length() != 0) {
writer.print(" class=\"" + rowCssClass + "\"");
}
writer.println(">");
for (Iterator<TableColumn> itC = model.getColumnIterator(); itC.hasNext();) {
TableColumn column = itC.next();
if (!column.isVisible()) {
continue;
}
CellLabel cell = labelProvider.getCellLabel(row, column, new RowContext(expanded, level));
writer.print("<td");
if (column.getWidth() > 0) {
writer.print(" width=\"" + column.getWidth() + "\"");
}
if (cell.cssClass != null) {
writer.print(" class=\"" + cell.cssClass + "\"");
}
if (cell.toolTip != null && cell.toolTip.length() > 0) {
writer.print(" title=\"" + cell.toolTip + "\"");
}
if (cell.colspan != null && cell.colspan.intValue() > 0) {
writer.print(" colspan=\"" + cell.colspan + "\"");
}
writer.print(">");
boolean innerTable = column.getIndex() == viewer.getExpandableColumn()
|| cell.image != null && cell.text != null;
if (innerTable) {
writer.print("<table class=\"inner\" cellspacing=0 cellpadding=0 border=0><tr><td>");
}
// handle exp/collapse
if (column.getIndex() == viewer.getExpandableColumn()) {
// indention
for (int i = 0; i < level; i++) {
writer.print(ICON_CLEAR.toImgTag(expandIconWidth, expandIconHeight));
}
if (contentProvider.hasChildren(row)) {
if (viewer.isEnabled()) {
writer.print("<a href=\"#\" onClick=\"return ");
writer.print(expanded ? "JWic.controls.TableViewer.collapse(event)"
: "JWic.controls.TableViewer.expand(event)");
writer.print("\";return false;\">");
writer.print((expanded ? ICON_COLLAPSE : ICON_EXPAND).toImgTag(expandIconWidth,
expandIconHeight));
writer.print("</A>");
} else {
writer.print((expanded ? ICON_COLLAPSE : ICON_EXPAND).toImgTag(expandIconWidth,
expandIconHeight));
}
} else {
if (expandLinkSpacing) {
writer.print(ICON_CLEAR.toImgTag(expandIconWidth, expandIconHeight));
}
}
writer.print("</td><td class=\"content\">");
}
// print cell value
if (cell.image != null) {
writer.print(cell.image.toImgTag());
if (cell.text != null) {
writer.print("</td><td class=\"content\">");
}
}
if (cell.text != null) {
writer.print("<NOBR>");
if (cell.text.length() == 0 && cell.image == null) {
writer.print(" ");
} else {
writer.print(cell.text);
}
writer.print("</NOBR>");
}
if (innerTable) {
writer.print("</td></tr></table>");
}
writer.println("</td>");
}
// if its a fixed width, must render an empty column that fills up
// the space.
if (viewer.getWidth() != 0) {
writer.println("<TD> </TD>");
}
writer.println(" </tr>");
if (contentProvider.hasChildren(row) && expanded) {
renderRows(level + 1, hasNext || it.hasNext(), writer, contentProvider.getChildren(row), viewer,
labelProvider);
}
}
return count;
}
/**
* @param writer
* @param rootIterator
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected int renderMRows(int level, boolean hasNext, PrintWriter writer, Iterator<?> it, TableViewer viewer,
ITableLabelProvider labelProvider) {
TableModel model = viewer.getModel();
IContentProvider contentProvider = model.getContentProvider();
int count = 0;
while (it.hasNext()) {
Object row = it.next();
count++;
String key = contentProvider.getUniqueKey(row);
boolean expanded = model.isExpanded(key);
writer.print("<tr>");
for (Iterator<TableColumn> itC = model.getColumnIterator(); itC.hasNext();) {
TableColumn column = itC.next();
if (!column.isVisible()) {
continue;
}
CellLabel cell = labelProvider.getCellLabel(row, column, new RowContext(expanded, level));
writer.print("<td");
if (cell.cssClass != null) {
writer.print(" class=\"" + cell.cssClass + "\"");
}
if (cell.toolTip != null && cell.toolTip.length() > 0) {
writer.print(" title=\"" + cell.toolTip + "\"");
}
if (cell.colspan != null && cell.colspan.intValue() > 0) {
writer.print(" colspan=\"" + cell.colspan + "\"");
}
writer.print(">");
// print cell value
if (cell.image != null) {
writer.print(cell.image.toImgTag());
if (cell.text != null) {
writer.print("</td><td class=\"content\">");
}
}
if (cell.text != null) {
if (cell.text.length() == 0 && cell.image == null) {
writer.print(" ");
} else {
writer.print(cell.text);
}
}
writer.println("</td>");
}
// if its a fixed width, must render an empty column that fills up
// the space.
if (viewer.getWidth() != 0) {
writer.println("<TD> </TD>");
}
writer.println(" </tr>");
if (contentProvider.hasChildren(row) && expanded) {
renderRows(level + 1, hasNext || it.hasNext(), writer, contentProvider.getChildren(row), viewer,
labelProvider);
}
}
return count;
}
/**
* @param writer
* @param viewer
*/
protected void renderEmptyRow(PrintWriter writer, TableViewer viewer) {
writer.print("<tr class=\"lastRow\">");
writer.print("<td colspan=\"" + (viewer.getModel().getColumnsCount() - 1) + "\" style=\"font-style: italic\">");
writer.print("no rows available");
writer.println("</td></tr>");
}
/**
* @return the expandLinkSpacing
*/
public boolean isExpandLinkSpacing() {
return expandLinkSpacing;
}
/**
* @param expandLinkSpacing
* the expandLinkSpacing to set
*/
public void setExpandLinkSpacing(boolean expandLinkSpacing) {
this.expandLinkSpacing = expandLinkSpacing;
}
/**
* @param expandIconHeight
* the expandIconHeight to set
*/
public void setExpandIconHeight(int expandIconHeight) {
this.expandIconHeight = expandIconHeight;
}
/**
* @return the expandIconHeight
*/
public int getExpandIconHeight() {
return expandIconHeight;
}
/**
* @param expandIconWidth
* the expandIconWidth to set
*/
public void setExpandIconWidth(int expandIconWidth) {
this.expandIconWidth = expandIconWidth;
}
/**
* @return the expandIconWidth
*/
public int getExpandIconWidth() {
return expandIconWidth;
}
/**
* Get new logger after deserialization.
*
* @param s
* @throws IOException
*/
private void readObject(ObjectInputStream s) throws IOException {
try {
s.defaultReadObject();
} catch (ClassNotFoundException e) {
throw new IOException("ClassNotFound in readObject");
}
log = LogFactory.getLog(getClass());
}
}