Package org.openfaces.renderkit.table

Source Code of org.openfaces.renderkit.table.TableBody$TableBodyRenderer$RowRenderer

/*
* OpenFaces - JSF Component Library 2.0
* Copyright (C) 2007-2012, TeamDev Ltd.
* licensing@openfaces.org
* Unless agreed in writing the contents of this file are subject to
* the GNU Lesser General Public License Version 2.1 (the "LGPL" License).
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* Please visit http://openfaces.org/licensing/ for more details.
*/
package org.openfaces.renderkit.table;

import org.openfaces.component.ContextDependentComponent;
import org.openfaces.component.TableStyles;
import org.openfaces.component.table.*;
import org.openfaces.component.table.impl.GroupFooterRow;
import org.openfaces.component.table.impl.GroupHeaderOrFooterRow;
import org.openfaces.component.table.impl.GroupHeaderRow;
import org.openfaces.component.table.impl.GroupingStructureColumn;
import org.openfaces.component.table.impl.InGroupFooterRow;
import org.openfaces.component.table.impl.InGroupHeaderRow;
import org.openfaces.org.json.JSONArray;
import org.openfaces.org.json.JSONException;
import org.openfaces.org.json.JSONObject;
import org.openfaces.renderkit.TableUtil;
import org.openfaces.util.Rendering;
import org.openfaces.util.StyleGroup;
import org.openfaces.util.Styles;

import javax.el.ELContext;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
* @author Dmitry Pikhulya
*/
public class TableBody extends TableSection {
    public static final String CUSTOM_ROW_INDEX_ATTRIBUTE = "_customRowIndex";
    public static final String CUSTOM_CELL_INDEX_ATTRIBUTE = "_customCellIndex";

    private static final String COLUMN_ATTR_ORIGINAL_INDEX = "_originalIndex";

    private static final String DEFAULT_NO_RECORDS_MSG = "No records";
    private static final String DEFAULT_NO_FILTER_RECORDS_MSG = "No records satisfying the filtering criteria";
    private static final String CUSTOM_CELL_RENDERING_INFO_ATTRIBUTE = "_customCellRenderingInfo";

    private TableStructure tableStructure;
    private boolean noDataRows;
    private List<String> clientRowKeys;
    private BaseColumn currentlyRenderedColumn;

    public TableBody(TableStructure tableStructure) {
        super(tableStructure);
        this.tableStructure = tableStructure;
    }

    protected void fillInitParam(JSONObject result, TableStyles defaultStyles) throws JSONException {
        TableStyles table = tableStructure.getTableStyles();
        FacesContext context = FacesContext.getCurrentInstance();
        UIComponent component = tableStructure.getComponent();
        String bodyClass = Styles.getCSSClass(context,
                component, table.getBodySectionStyle(), table.getBodySectionClass());
        result.put("className", bodyClass);
        Rendering.addJsonParam(result, "rowClassName", Styles.getCSSClass(context, component, table.getBodyRowStyle(),
                StyleGroup.regularStyleGroup(), table.getBodyRowClass(),
                defaultStyles != null ? defaultStyles.getBodyRowClass() : null));
        Rendering.addJsonParam(result, "oddRowClassName", Styles.getCSSClass(context, component, table.getBodyOddRowStyle(),
                getBodyOddRowClass(table, defaultStyles)));
        if (table instanceof AbstractTable) {
            AbstractTable at = (AbstractTable) table;
            Rendering.addJsonParam(result, "rolloverRowClassName", Styles.getCSSClass(context, component,
                    at.getRolloverRowStyle(), StyleGroup.rolloverStyleGroup(), at.getRolloverRowClass()));
        }

        result.put("noDataRows", getNoDataRows());
        result.put("rowKeys", clientRowKeys);
    }

    public boolean getNoDataRows() {
        return noDataRows;
    }

    public List<String> getClientRowKeys() {
        return clientRowKeys;
    }

    private String getBodyOddRowClass(TableStyles table, TableStyles defaultStyles) {
        String bodyOddRowClass = table.getBodyOddRowClass();
        return TableUtil.getClassWithDefaultStyleClass(
                defaultStyles != null,
                defaultStyles != null ? defaultStyles.getBodyOddRowStyle() : null,
                bodyOddRowClass);
    }

    protected void renderRows(FacesContext context, HeaderCell.AdditionalContentWriter additionalContentWriter) throws IOException {
        AbstractTable table = (AbstractTable) tableStructure.getComponent();
        List<BaseColumn> columns = table.getAdaptedRenderedColumns();
        int first = table.getFirst();
        if (table.getRows() != 0)
            throw new IllegalStateException("table.getRows() should always be equal to 0 in OpenFaces tables, but it is equal to " + table.getRows());

        int rowCount = table.getRowCount();
        int rows = (rowCount != -1) ? rowCount : Integer.MAX_VALUE;
        if (rows == 0)
            noDataRows = true;
        if (table.getDeferBodyLoading()) {
            rows = 0;
        }

        List<BodyRow> bodyRows = createRows(context, first, rows, columns);
        TableFooter footer = tableStructure.getFooter();
        boolean hasFooter = footer != null && footer.isContentSpecified();
        for (int i = 0, count = bodyRows.size(); i < count; i++) {
            BodyRow row = bodyRows.get(i);
            row.render(context, (!hasFooter && i == count - 1) ? additionalContentWriter : null);
        }
        table.setRowIndex(-1);
    }

    protected String getSectionName() {
        return "tbody";
    }

    protected List<BodyRow> createRows(
            FacesContext context,
            int firstRowIndex,
            int rowCount,
            List<BaseColumn> columns
    ) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        StringWriter stringWriter = new StringWriter();
        List<BodyRow> rows = new ArrayList<BodyRow>();
        context.setResponseWriter(writer.cloneWithWriter(stringWriter));
        try {
            if (tableStructure.getScrolling() == null)
                rows = createNonScrollableRows(context, stringWriter, firstRowIndex, rowCount, columns);
            else
                rows = createScrollableRows(context, stringWriter, firstRowIndex, rowCount, columns);
        } finally {
            context.setResponseWriter(writer);
        }
        return rows;
    }

    private List<BodyRow> createScrollableRows(
            FacesContext context,
            StringWriter stringWriter,
            int firstRowIndex,
            int rowCount,
            List<BaseColumn> columns
    ) throws IOException {
        List<BodyRow> leftRows, rows, rightRows;
        AbstractTable table = (AbstractTable) tableStructure.getComponent();
        int fixedLeftColumns = tableStructure.getLeftFixedCols();
        int fixedRightColumns = tableStructure.getRightFixedCols();
        int allColCount = columns.size();
        if (rowCount > 0) {
            List<BodyRow>[] rowsForAreas = createDataRows(context, stringWriter, firstRowIndex, rowCount, columns);
            leftRows = rowsForAreas[0];
            rows = rowsForAreas[1];
            rightRows = rowsForAreas[2];
        } else if (table.getNoDataMessageAllowed()) {
            leftRows = fixedLeftColumns > 0 ? Collections.singletonList(createEmptyNoDataRow(table, fixedLeftColumns)) : null;
            rows = Collections.singletonList(createNoDataRow(context, stringWriter, allColCount - fixedLeftColumns - fixedRightColumns));
            rightRows = fixedRightColumns > 0 ? Collections.singletonList(createEmptyNoDataRow(table, fixedRightColumns)) : null;
        } else {
            leftRows = fixedLeftColumns > 0 ? Collections.singletonList(createFakeRow(fixedLeftColumns)) : null;
            rows = Collections.singletonList(createFakeRow(allColCount - fixedLeftColumns - fixedRightColumns));
            rightRows = fixedRightColumns > 0 ? Collections.singletonList(createFakeRow(fixedRightColumns)) : null;
        }

        List<BodyCell> cells = new ArrayList<BodyCell>();
        if (fixedLeftColumns > 0)
            cells.add(scrollingAreaCell(columns, leftRows, 0, fixedLeftColumns, TableScrollingArea.ScrollingType.VERTICAL));
        cells.add(scrollingAreaCell(columns, rows, fixedLeftColumns, allColCount - fixedRightColumns, TableScrollingArea.ScrollingType.BOTH));
        if (fixedRightColumns > 0)
            cells.add(scrollingAreaCell(columns, rightRows, allColCount - fixedRightColumns, allColCount, TableScrollingArea.ScrollingType.VERTICAL));

        BodyRow containingRow = new BodyRow(this);
        containingRow.setCells(cells);
        return Collections.singletonList(containingRow);
    }

    private BodyCell scrollingAreaCell(List<BaseColumn> columns, List<BodyRow> rows, int startCol, int endCol,
                                       TableScrollingArea.ScrollingType scrolling) {
        BodyCell cell = new BodyCell();
        TableScrollingArea scrollingArea =
                new TableScrollingArea(cell, columns.subList(startCol, endCol), rows, scrolling, false);
        scrollingArea.setCellpadding(tableStructure.getTableCellPadding());
        scrollingArea.setIndefiniteHeight(true);
        cell.setContent(scrollingArea);
        return cell;
    }

    private List<BodyRow> createNonScrollableRows(
            FacesContext context,
            StringWriter stringWriter,
            int firstRowIndex,
            int rowCount,
            List<BaseColumn> columns
    ) throws IOException {
        List<BodyRow> rows;
        AbstractTable table = (AbstractTable) tableStructure.getComponent();
        if (rowCount > 0)
            rows = createDataRows(context, stringWriter, firstRowIndex, rowCount, columns)[1];
        else if (table.getNoDataMessageAllowed())
            rows = Collections.singletonList(
                    createNoDataRow(context, stringWriter, columns.size()));
        else
            rows = Collections.singletonList(
                    createFakeRow(columns.size()));
        return rows;
    }

    private List<BodyRow>[] createDataRows(
            FacesContext context,
            StringWriter stringWriter,
            int firstRowIndex,
            int rowsToRender,
            List<BaseColumn> columns
    ) throws IOException {
        TableBodyRenderer renderer = new TableBodyRenderer(context, stringWriter, firstRowIndex, columns);
        return renderer.createDataRows(rowsToRender);
    }

    private BodyRow createFakeRow(int span) throws IOException {
        BodyRow row = new BodyRow();
        BodyCell cell = new BodyCell();
        row.setCells(Collections.singletonList(cell));
        cell.setSpan(span);
        cell.setStyle("display: none;");
        return row;
    }

    private BodyRow createNoDataRow(FacesContext context, StringWriter stringWriter, int span) throws IOException {
        AbstractTable table = (AbstractTable) tableStructure.getComponent();
        BodyRow row = createEmptyNoDataRow(table, span);

        BodyCell cell = row.getCells().get(0);

        StringBuffer buf = stringWriter.getBuffer();
        int startIdx = buf.length();
        boolean dataSourceEmpty = table.isDataSourceEmpty();
        UIComponent noDataMessage = dataSourceEmpty ? table.getNoDataMessage() : table.getNoFilterDataMessage();
        if (noDataMessage != null) {
            noDataMessage.encodeAll(context);
        } else {
            String message = (dataSourceEmpty)
                    ? DEFAULT_NO_RECORDS_MSG
                    : DEFAULT_NO_FILTER_RECORDS_MSG;
            ResponseWriter writer = context.getResponseWriter();
            writer.writeText(message, null);
        }
        int endIdx = buf.length();
        String content = buf.substring(startIdx, endIdx);
        cell.setContent(content);

        Map<Object, String> rowStylesMap = tableStructure.getRowStylesMap();
        rowStylesMap.put(0, TableStructure.getNoDataRowClassName(context, table));
        return row;
    }

    private BodyRow createEmptyNoDataRow(AbstractTable table, int span) {
        BodyRow row = new BodyRow();
        BodyCell cell = new BodyCell();
        row.setCells(Collections.singletonList(cell));
        if (span > 0)
            cell.setSpan(span);
        cell.setStyle(table.getNoDataRowStyle());
        cell.setStyleClass(table.getNoDataRowClass());
        return row;
    }


    public BaseColumn getCurrentlyRenderedColumn() {
        return currentlyRenderedColumn;
    }

    public void setCurrentlyRenderedColumn(BaseColumn currentlyRenderedColumn) {
        this.currentlyRenderedColumn = currentlyRenderedColumn;
    }

    private class TableBodyRenderer {
        private final FacesContext context;
        private final ResponseWriter writer;
        private final Map<String, Object> requestMap;
        private final StringWriter stringWriter;
        private final int firstRowIndex;
        private final AbstractTable table;
        private final List<BaseColumn> columns;
        private final List<BodyRow> leftRows;
        private final List<BodyRow> rows;
        private final List<BodyRow> rightRows;
        private final int centerAreaStart;
        private final int rightAreaStart;
        private final List<Row> customRows;
        private final Map<Integer, CustomRowRenderingInfo> customRowRenderingInfos;
        private final Map<Integer, Integer> renderedColIndexesByOriginalIndexes;
        private final MethodExpression cellSelectableME;
        private final MethodExpression selectableCellsME;
        private final JSONArray collectedSelectableCells;
        private final AbstractCellSelection cellSelection;
        private final Iterable<Summary> summaries;

        private TableBodyRenderer(
                FacesContext context,
                StringWriter stringWriter,
                int firstRowIndex,
                List<BaseColumn> columns) {

            this.context = context;
            this.stringWriter = stringWriter;
            this.firstRowIndex = firstRowIndex;
            this.columns = columns;

            writer = context.getResponseWriter();
            requestMap = context.getExternalContext().getRequestMap();
            table = (AbstractTable) tableStructure.getComponent();

            Scrolling scrolling = tableStructure.getScrolling();
            leftRows = scrolling != null && tableStructure.getLeftFixedCols() > 0 ? new ArrayList<BodyRow>() : null;
            rows = new ArrayList<BodyRow>();
            rightRows = scrolling != null && tableStructure.getRightFixedCols() > 0 ? new ArrayList<BodyRow>() : null;
            centerAreaStart = scrolling != null ? tableStructure.getLeftFixedCols() : 0;
            rightAreaStart = scrolling != null ? columns.size() - tableStructure.getRightFixedCols() : 0;

            customRows = getCustomRows(table);
            customRowRenderingInfos = (Map<Integer, CustomRowRenderingInfo>) table.getAttributes().get(
                    TableStructure.CUSTOM_ROW_RENDERING_INFOS_KEY);

            final AbstractTableSelection selection = table.getSelection();
            cellSelection = selection instanceof AbstractCellSelection
                    ? (AbstractCellSelection) selection : null;
            collectedSelectableCells = new JSONArray();

            cellSelectableME = (cellSelection == null) ? null : cellSelection.getCellSelectable();
            selectableCellsME = (cellSelection == null) ? null : cellSelection.getSelectableCells();
            if (cellSelectableME != null && selectableCellsME != null) throw new IllegalStateException(
                    "The \"cellSelectable\" and \"selectableCells\" attributes are " +
                            "mutually exclusive and cannot be used at the same time.");
            renderedColIndexesByOriginalIndexes = getRenderedColIndexesByOriginalIndexesMap(table);

            summaries = table.getSummaryComponents();
        }

        private List<Row> getCustomRows(AbstractTable table) {
            List<Row> customRows = new ArrayList<Row>();

            if (table instanceof DataTable) {
                DataTable dataTable = (DataTable) table;
                RowGrouping rowGrouping = dataTable.getRowGrouping();
                if (rowGrouping != null) {
                    customRows.add(new GroupHeaderRow(dataTable));
                    customRows.add(new InGroupHeaderRow(dataTable));
                    customRows.add(new InGroupFooterRow(dataTable));
                    customRows.add(new GroupFooterRow(dataTable));
                }
            }

            List<UIComponent> children = table.getChildren();
            int customRowIndex = 0;
            int customCellIndex = 0;
            for (UIComponent child : children) {
                if (child instanceof Row) {
                    Row customRow = (Row) child;
                    customRows.add(customRow);
                    customRow.getAttributes().put(CUSTOM_ROW_INDEX_ATTRIBUTE, customRowIndex++);
                    List<UIComponent> customRowChildren = customRow.getChildren();
                    for (UIComponent rowChild : customRowChildren) {
                        if (rowChild instanceof Cell) {
                            Cell customCell = (Cell) rowChild;
                            customCell.getAttributes().put(CUSTOM_CELL_INDEX_ATTRIBUTE, customCellIndex++);
                        }
                    }
                }
            }
            return customRows;
        }

        private Map<Integer, Integer> getRenderedColIndexesByOriginalIndexesMap(AbstractTable table) {
            final List<BaseColumn> renderedColumns = table.getRenderedColumns();
            final List<BaseColumn> allColumns = table.getAllColumns();
            final Map<Integer, Integer> renderedColIndexesByOriginalIndexes =
                    new LinkedHashMap<Integer, Integer>(allColumns.size());
            for (int originalColIndex = 0; originalColIndex < allColumns.size(); originalColIndex++) {
                for (int renderedColIndex = 0; renderedColIndex < renderedColumns.size(); renderedColIndex++) {
                    if (allColumns.get(originalColIndex) == renderedColumns.get(renderedColIndex)) {
                        renderedColIndexesByOriginalIndexes.put(originalColIndex, renderedColIndex);
                        break;
                    }
                }
            }
            return renderedColIndexesByOriginalIndexes;
        }

        public List<BodyRow>[] createDataRows(int rowsToRender) throws IOException {
            int lastRowIndex = firstRowIndex + rowsToRender - 1;
            for (int rowIndex = firstRowIndex; rowIndex <= lastRowIndex; rowIndex++) {
                table.setRowIndex(rowIndex);
                if (!table.isRowAvailable())
                    break;

                RowRenderer rowRenderer = new RowRenderer(rowIndex);
                rowRenderer.renderRow();
            }
            if (cellSelection != null) {
                cellSelection.setCollectedSelectableCells(collectedSelectableCells);
            }
            table.setRowIndex(-1);

            return new List[]{leftRows, rows, rightRows};
        }


        private class RowRenderer {
            private final List<BodyCell> leftCells;
            private final List<BodyCell> cells;
            private final List<BodyCell> rightCells;
            private final int bodyRowIndex;
            private final int columnCount;
            private final List<SpannedTableCell> alreadyProcessedSpannedCells;
            private final List<Object>[] applicableCustomCells;
            private final JSONArray selectableCellsInColumn;

            private CustomRowRenderingInfo customRowRenderingInfo;
            private int rowIndex;
            private ELContext elContext;

            private RowRenderer(int rowIndex) throws IOException {
                this.elContext = FacesContext.getCurrentInstance().getELContext();
                this.rowIndex = rowIndex;

                String clientRowKey = table.getClientRowKey();
                if (clientRowKeys == null) clientRowKeys = new ArrayList<String>();
                clientRowKeys.add(clientRowKey);

                this.columnCount = columns.size();

                collectSelectableCells(columnCount);

                this.bodyRowIndex = rowIndex - firstRowIndex;
                List<Row> applicableCustomRows = getApplicableCustomRows(customRows);
                this.applicableCustomCells =
                        getApplicableCustomCells(applicableCustomRows, bodyRowIndex, columnCount);

                this.selectableCellsInColumn = new JSONArray();
                BodyRow leftRow = leftRows != null ? new BodyRow() : null;
                BodyRow row = new BodyRow();
                BodyRow rightRow = rightRows != null ? new BodyRow() : null;

                if (leftRows != null)
                    leftRows.add(leftRow);
                rows.add(row);
                if (rightRows != null)
                    rightRows.add(rightRow);

                if (leftRow != null)
                    leftRow.extractCustomEvents(applicableCustomRows);
                row.extractCustomEvents(applicableCustomRows);
                if (rightRow != null)
                    rightRow.extractCustomEvents(applicableCustomRows);

                String[][] attributes = tableStructure.getBodyRowAttributes(context, table);
                if (leftRow != null)
                    leftRow.setAttributes(attributes);
                row.setAttributes(attributes);
                if (rightRow != null)
                    rightRow.setAttributes(attributes);

                this.leftCells = leftRow != null ? new ArrayList<BodyCell>() : null;
                this.cells = new ArrayList<BodyCell>();
                this.rightCells = rightRow != null ? new ArrayList<BodyCell>() : null;

                if (leftRow != null)
                    leftRow.setCells(leftCells);
                row.setCells(cells);
                if (rightRow != null)
                    rightRow.setCells(rightCells);

                this.alreadyProcessedSpannedCells = new ArrayList<SpannedTableCell>();
            }

            public void renderRow() throws IOException {
                Object rowData = table.getRowData();
                if (!(rowData instanceof GroupHeaderOrFooter)) {
                    for (Summary summary : summaries) {
                        summary.addCurrentRowValue();
                    }
                }

                for (int colIndex = 0; colIndex < columnCount; ) {
                    List<BodyCell> targetCells;
                    if (leftCells != null && colIndex < centerAreaStart)
                        targetCells = leftCells;
                    else if (rightCells != null && colIndex >= rightAreaStart)
                        targetCells = rightCells;
                    else
                        targetCells = cells;

                    BaseColumn column = columns.get(colIndex);
                    if (!column.isRendered())
                        throw new IllegalStateException("Only rendered columns are expected in columns list. " +
                                "column id: " + column.getId() + "; column index = " + colIndex);

                    String columnIndexVar = table.getColumnIndexVar();
                    String columnIdVar = table.getColumnIdVar();
                    Object prevColumnIndexVarValue = null;
                    Object prevColumnIdVarValue = null;
                    int originalColIndex = getOriginalColumnIndex(column);
                    if (columnIndexVar != null)
                        prevColumnIndexVarValue = requestMap.put(columnIndexVar, originalColIndex);
                    if (columnIdVar != null) {
                        String columnId = column.getId();
                        prevColumnIdVarValue = requestMap.put(columnIdVar, columnId);
                    }
                    BodyCell cell = renderCell(column, colIndex);
                    targetCells.add(cell);

                    if (columnIndexVar != null)
                        requestMap.put(columnIndexVar, prevColumnIndexVarValue);
                    if (columnIdVar != null)
                        requestMap.put(columnIdVar, prevColumnIdVarValue);

                    colIndex += cell.getSpan();
                }

                if (cellSelectableME != null) {
                    collectedSelectableCells.put(selectableCellsInColumn);
                }

                if (customRowRenderingInfo != null)
                    customRowRenderingInfos.put(rowIndex, customRowRenderingInfo);
            }

            private BodyCell renderCell(BaseColumn column, int colIndex) throws IOException {
                if (cellSelectableME != null) {
                    selectableCellsInColumn.put(cellSelectableME.invoke(elContext, null));
                }

                List<?> customCells = applicableCustomCells[colIndex];
                SpannedTableCell spannedTableCell =
                        customCells != null && customCells.size() == 1 && customCells.get(0) instanceof SpannedTableCell
                                ? (SpannedTableCell) customCells.get(0) : null;
                boolean remainingPortionOfABrokenSpannedCell = false;
                int span = 1;
                if (spannedTableCell != null) {
                    int testedColIndex = colIndex;
                    while (true) {
                        testedColIndex++;
                        if (testedColIndex == columnCount)
                            break;
                        List testedCells = applicableCustomCells[testedColIndex];
                        if (testedCells == null) break;
                        SpannedTableCell testedSpannedCell =
                                testedCells.size() == 1 && testedCells.get(0) instanceof SpannedTableCell
                                        ? (SpannedTableCell) testedCells.get(0) : null;
                        if (spannedTableCell != testedSpannedCell)
                            break;
                        span++;
                    }
                    column = spannedTableCell.getColumn();
                    customCells = spannedTableCell.getApplicableTableCells();
                    if (alreadyProcessedSpannedCells.contains(spannedTableCell))
                        remainingPortionOfABrokenSpannedCell = true;
                    else
                        alreadyProcessedSpannedCells.add(spannedTableCell);
                }

                setCurrentlyRenderedColumn(column);
                try {
                    UIComponent cellContentsContainer = applyCustomCellDefinitions(
                        column, colIndex, customCells, remainingPortionOfABrokenSpannedCell, span);

                    BodyCell cell = new BodyCell();
                    cell.setSpan(span);
                    cell.extractCustomEvents((List<UIComponent>) customCells);

                    String content = renderCellContent(column, remainingPortionOfABrokenSpannedCell, cellContentsContainer);
                    cell.setContent(content);
                    return cell;
                } finally {
                    setCurrentlyRenderedColumn(null);
                }

            }

            private String renderCellContent(
                    BaseColumn column,
                    boolean remainingPortionOfABrokenSpannedCell,
                    UIComponent cellContentsContainer) throws IOException {

                StringBuffer buf = stringWriter.getBuffer();
                int startIdx = buf.length();
                if (remainingPortionOfABrokenSpannedCell) {
                    if (tableStructure.isEmptyCellsTreatmentRequired())
                        Rendering.writeNonBreakableSpace(writer);
                } else {
                    renderCellContent(column, cellContentsContainer);
                }
                int endIdx = buf.length();
                return buf.substring(startIdx, endIdx);
            }

            private void renderCellContent(BaseColumn column, UIComponent cellContentsContainer) throws IOException {
                boolean renderCustomTreeCell = column instanceof TreeColumn && cellContentsContainer instanceof Cell;
                Column displayedColumn = cellContentsContainer instanceof Column
                        ? (Column) cellContentsContainer
                        : null;
                Object prevColumnValue = null;
                if (displayedColumn != null) {
                    ValueExpression valueExpression = displayedColumn.getValueExpression();
                    if (valueExpression != null) {
                        Object columnValue = valueExpression.getValue(elContext);
                        prevColumnValue = requestMap.put(Column.COLUMN_VALUE_VAR, columnValue);
                    }
                }

                if (!renderCustomTreeCell) {
                    cellContentsContainer.encodeAll(context);
                    if (!(column instanceof Column)) {
                        TableStructure.writeNonBreakableSpaceForEmptyCell(writer, table, cellContentsContainer);
                    }
                } else {
                    column.getAttributes().put(TreeColumnRenderer.ATTR_CUSTOM_CELL, cellContentsContainer);
                    try {
                        column.encodeAll(context);
                    } finally {
                        column.getAttributes().remove(TreeColumnRenderer.ATTR_CUSTOM_CELL);
                    }
                }

                requestMap.put(Column.COLUMN_VALUE_VAR, prevColumnValue);
            }

            private void collectSelectableCells(int columnCount) {
                if (selectableCellsME != null) {
                    boolean[] selectableFlagsForAllColumns = (boolean[]) selectableCellsME.invoke(elContext, null);
                    boolean[] selectableFlagsForRenderedColumns = new boolean[columnCount];
                    for (int i = 0, count = selectableFlagsForAllColumns.length; i < count; i++) {
                        final Integer renderedColIndex = renderedColIndexesByOriginalIndexes.get(i);
                        if (renderedColIndex != null) {
                            selectableFlagsForRenderedColumns[renderedColIndex] = selectableFlagsForAllColumns[i];
                        }
                    }
                    collectedSelectableCells.put(selectableFlagsForRenderedColumns);
                }
            }

            private List<Row> getApplicableCustomRows(List<Row> customRows) {
                List<Row> applicableRows = new ArrayList<Row>(customRows.size());
                for (Row tableRow : customRows) {
                    if (tableRow.getCondition())
                        applicableRows.add(tableRow);
                }
                return applicableRows;
            }

            private List<Object>[] getApplicableCustomCells(
                    List<Row> applicableCustomRows, int bodyRowIndex, int columnCount) {
                Object rowData = table.getRowData();

                List<String> rowStyles = getApplicableRowStyles(context, customRows, table);
                String rowStyleClass = (rowStyles != null && rowStyles.size() > 0)
                        ? classNamesToClass(rowStyles)
                        : null;
                String additionalClass = tableStructure.getAdditionalRowClass(context, table, rowData, bodyRowIndex);
                if (additionalClass != null)
                    rowStyleClass = Styles.mergeClassNames(rowStyleClass, additionalClass);
                if (!Rendering.isNullOrEmpty(rowStyleClass)) {
                    Map<Object, String> rowStylesMap = tableStructure.getRowStylesMap();
                    rowStylesMap.put(bodyRowIndex, rowStyleClass);
                }


                List<Integer> applicableRowDeclarationIndexes = new ArrayList<Integer>();
                for (Row tableRow : applicableCustomRows) {
                    if (Rendering.isComponentWithA4jSupport(tableRow)) {
                        if (customRowRenderingInfo == null)
                            customRowRenderingInfo = new CustomRowRenderingInfo(columnCount);
                        Integer rowDeclarationIndex =
                                (Integer) tableRow.getAttributes().get(CUSTOM_ROW_INDEX_ATTRIBUTE);
                        if (rowDeclarationIndex == null)
                            throw new IllegalStateException("CUSTOM_ROW_INDEX_ATTRIBUTE can legally be null only for " +
                                    "the implicit grouping-related rows, which don't have any Ajax actions bound to " +
                                    "them, so they can't lead execution to this point in code");
                        applicableRowDeclarationIndexes.add(rowDeclarationIndex);
                    }
                }
                if (customRowRenderingInfo != null)
                    customRowRenderingInfo.setA4jEnabledRowDeclarationIndexes(applicableRowDeclarationIndexes);

                return prepareCustomCells(table, applicableCustomRows);
            }

            private String classNamesToClass(List<String> cellStyles) {
                StringBuilder combinedClassName = new StringBuilder();
                for (String className : cellStyles) {
                    if (className != null) {
                        int len = combinedClassName.length();
                        if (len > 0)
                            combinedClassName.append(' ');
                        combinedClassName.append(className.trim());
                    }
                }
                return combinedClassName.toString();
            }

            private List<String> getApplicableRowStyles(FacesContext context, List<Row> customRows, AbstractTable table) {
                List<String> result = new ArrayList<String>();
                for (Row customRow : customRows) {
                    String cls = customRow.getStyleClassForRow(context, table);
                    if (cls != null)
                        result.add(cls);
                }
                return result;
            }

            /**
             * Takes the list of custom row specifications applicable to a row of interest (this is a list of TableRow
             * instances because several TableRow instances can be applicable to the same row), and constructs a list of
             * custom cell specifications applicable to the cells of this row.
             *
             * @param table                a table whose row is being analyzed
             * @param applicableCustomRows a list of TableRow instances applicable to the current row
             * @return An array the size of number of rendered columns. Each array entry corresponds to each rendered
             *         cell. Each array entry contains one of the following: (1) null - means that no custom cells are
             *         applicable for this cell and it will be rendered as usual; (2) List of TableCell instances
             *         applicable to this cell; (3) List containing only one SpannedTableCell instance if an appropriate
             *         cell is part of a cell span - all cells referring to the same SpannedTableCell will be rendered
             *         as one cell spanning across several columns.
             */
            private List<Object>[] prepareCustomCells(AbstractTable table, List<Row> applicableCustomRows) {
                List<BaseColumn> allColumns = table.getAllColumns();
                List<BaseColumn> columnsForRendering = table.getAdaptedRenderedColumns();
                int allColCount = allColumns.size();
                for (int i = 0; i < allColCount; i++) {
                    UIComponent col = allColumns.get(i);
                    col.getAttributes().put(COLUMN_ATTR_ORIGINAL_INDEX, i);
                }

                int visibleColCount = columnsForRendering.size();
                List[] rowCellsByAbsoluteIndex = new List[allColCount];

                boolean thereAreCellSpans = false;
                List<Cell> rowCellsByColReference = new ArrayList<Cell>();
                for (Row row : applicableCustomRows) {
                    Integer customRowIndex = (Integer) row.getAttributes().get(CUSTOM_ROW_INDEX_ATTRIBUTE);
                    List<UIComponent> children = row.getChildren();
                    int freeCellIndex = 0;
                    int customCellIndex = 0;
                    for (UIComponent child : children) {
                        if (!(child instanceof Cell))
                            continue;
                        Cell cell = (Cell) child;

                        cell.getAttributes().put(CUSTOM_CELL_RENDERING_INFO_ATTRIBUTE,
                                new CustomContentCellRenderingInfo(
                                        customRowIndex != null ? customRowIndex : -1,
                                        customCellIndex++));
                        int span = cell.getSpan();
                        Object columnIds = cell.getColumnIds();
                        ValueExpression conditionExpression = cell.getConditionExpression();
                        if (span < 1)
                            throw new IllegalArgumentException("The value of 'span' attribute of <o:cell> tag can't " +
                                    "be less than 1, but encountered: " + span);

                        if (span != 1) {
                            thereAreCellSpans = true;
                        }

                        if (columnIds == null && conditionExpression == null) {
                            int thisColIndex = freeCellIndex;
                            freeCellIndex += span;
                            if (thisColIndex >= allColCount)
                                throw new FacesException("The number of free cells (cells without 'column' attribute)" +
                                        " inside of <o:row> tag should not be greater than the total number of " +
                                        "columns in a table (" + allColCount + ")");
                            List<Cell> applicableCells = rowCellsByAbsoluteIndex[thisColIndex];
                            if (applicableCells == null) {
                                applicableCells = new ArrayList<Cell>();
                                rowCellsByAbsoluteIndex[thisColIndex] = applicableCells;
                            }
                            applicableCells.add(cell);
                        } else {
                            rowCellsByColReference.add(cell);
                        }
                    }
                }
                if (rowCellsByColReference.size() > 0) {
                    for (int i = 0; i < allColCount; i++) {
                        BaseColumn col = allColumns.get(i);
                        for (Cell cell : rowCellsByColReference) {
                            if (isColumnApplicable(cell, table, col, i)) {
                                List<Cell> applicableCells = rowCellsByAbsoluteIndex[i];
                                if (applicableCells == null) {
                                    applicableCells = new ArrayList<Cell>();
                                    rowCellsByAbsoluteIndex[i] = applicableCells;
                                }
                                applicableCells.add(cell);
                            }
                        }
                    }
                }

                if (thereAreCellSpans) {
                    for (int i = 0; i < allColCount; i++) {
                        List customCellList = rowCellsByAbsoluteIndex[i];
                        if (customCellList == null)
                            continue;
                        int cellSpan = 1;
                        boolean groupHeaderCell = false;
                        for (Object cellObj : customCellList) {
                            Cell cell = (Cell) cellObj;
                            groupHeaderCell |= GroupHeaderOrFooterRow.isSyntheticGroupHeaderCell(cell);

                            int currentCellSpan = cell.getSpan();
                            if (currentCellSpan != 1)
                                cellSpan = currentCellSpan;
                        }
                        if (cellSpan == 1)
                            continue;
                        BaseColumn column = allColumns.get(i);
                        if (groupHeaderCell) {
                            // when table row grouping is turned on, the automatically generated implicit tree column is
                            // always generated to be the first rendered column, and spannedTableCell.getColumn() refers
                            // to the first column in the allColumns list, and so it always refers to the first column
                            // declared in the table's markup, which might actually be different than the first column
                            // in the renderedColumns list. Hence we're fixing this so that tree column was properly
                            // rendered on group header cells
                            TreeColumn treeColumn = null;
                            for (BaseColumn c : columnsForRendering) {
                                if (c instanceof TreeColumn) {
                                    treeColumn = (TreeColumn) c;
                                    break;
                                }
                            }
                            if (treeColumn == null)
                                throw new IllegalStateException("There must be an implicitly generated tree column " +
                                        "in the grouped table");
                            column = treeColumn;
                        }
                        SpannedTableCell spannedTableCell = new SpannedTableCell(column, customCellList);
                        for (int cellIndex = i; cellIndex < i + cellSpan; cellIndex++) {
                            rowCellsByAbsoluteIndex[cellIndex] = Collections.singletonList(spannedTableCell);
                        }
                        i += cellSpan - 1;
                    }
                }

                List[] applicableCells = new List[visibleColCount];
                for (int i = 0; i < visibleColCount; i++) {
                    BaseColumn column = columnsForRendering.get(i);
                    int originalColIndex = getOriginalColumnIndex(column);
                    applicableCells[i] = rowCellsByAbsoluteIndex[originalColIndex];
                }
                return (List<Object>[]) applicableCells;
            }

            private boolean isColumnApplicable(
                    Cell cell,
                    AbstractTable table,
                    BaseColumn column,
                    int originalColIndex) {
                Object columnIds = cell.getColumnIds();
                if (columnIds != null) {
                    Collection<Object> columnIdsCollection;
                    if (columnIds.getClass().isArray()) {
                        columnIdsCollection = Arrays.asList((Object[]) columnIds);
                    } else if (columnIds instanceof Collection)
                        columnIdsCollection = (Collection<Object>) columnIds;
                    else
                        throw new IllegalArgumentException("'columnIds' attribute of <o:cell> tag should contain " +
                                "either an array or a collection of column id strings, but a value of the following " +
                                "type encountered: " + columnIds.getClass().getName());
                    String colId = column.getId();
                    boolean result = columnIdsCollection.contains(colId);
                    return result;
                }

                ValueExpression conditionExpression = cell.getConditionExpression();

                String columnIndexVar = table.getColumnIndexVar();
                String columnIdVar = table.getColumnIdVar();
                Object prevColumnIndexVarValue = null;
                Object prevColumnIdVarValue = null;
                if (columnIndexVar != null)
                    prevColumnIndexVarValue = requestMap.put(columnIndexVar, originalColIndex);
                if (columnIdVar != null) {
                    String columnId = column.getId();
                    prevColumnIdVarValue = requestMap.put(columnIdVar, columnId);
                }

                Boolean result = (Boolean) conditionExpression.getValue(elContext);

                if (columnIndexVar != null)
                    requestMap.put(columnIndexVar, prevColumnIndexVarValue);
                if (columnIdVar != null)
                    requestMap.put(columnIdVar, prevColumnIdVarValue);

                return (result == null) || result;
            }

            private int getOriginalColumnIndex(BaseColumn column) {
                if (column instanceof GroupingStructureColumn) {
                    BaseColumn delegate = ((GroupingStructureColumn) column).getDelegate();
                    return getOriginalColumnIndex(delegate);
                }
                return (Integer) column.getAttributes().get(COLUMN_ATTR_ORIGINAL_INDEX);
            }

            private UIComponent applyCustomCellDefinitions(
                    BaseColumn column, int colIndex,
                    List<?> customCells,
                    boolean remainingPortionOfABrokenSpannedCell, int span) {

                UIComponent cellContentsContainer = column;
                if (customCells == null) return cellContentsContainer;

                String customCellStyle = null;
                String columnId = column.getId();
                for (Object customCell : customCells) {
                    Cell cell = (Cell) customCell;
                    Runnable exitContext = cell instanceof ContextDependentComponent
                            ? ((ContextDependentComponent) cell).enterComponentContext() : null;
                    boolean cellWithCustomContent = cell.getChildCount() > 0;
                    if (exitContext != null) exitContext.run();
                    if (cellWithCustomContent || Rendering.isComponentWithA4jSupport(cell)) {
                        if (customRowRenderingInfo == null)
                            customRowRenderingInfo = new CustomRowRenderingInfo(columnCount);
                        if (cellWithCustomContent)
                            cellContentsContainer = cell;
                        CustomContentCellRenderingInfo customCellRenderingInfo = (CustomContentCellRenderingInfo)
                                cell.getAttributes().get(CUSTOM_CELL_RENDERING_INFO_ATTRIBUTE);
                        customRowRenderingInfo.setCustomCellRenderingInfo(colIndex, customCellRenderingInfo);
                    }
                    customCellStyle = Styles.mergeClassNames(
                            customCellStyle,
                            cell.getStyleClassForCell(context, table, colIndex, columnId));
                }
                for (int i = colIndex + (remainingPortionOfABrokenSpannedCell ? 0 : 1), upperBound = colIndex + span;
                     i < upperBound; i++) {
                    if (customRowRenderingInfo == null)
                        customRowRenderingInfo = new CustomRowRenderingInfo(columnCount);
                    customRowRenderingInfo.setCustomCellRenderingInfo(i, new MergedCellRenderingInfo());
                }

                if (customCellStyle != null) {
                    customCellStyle = customCellStyle.trim();
                    if (customCellStyle.length() != 0) {
                        Map<Object, String> cellStylesMap = tableStructure.getCellStylesMap();
                        cellStylesMap.put(bodyRowIndex + "x" + colIndex, customCellStyle);
                    }
                }

                return cellContentsContainer;
            }

        }
    }
}
TOP

Related Classes of org.openfaces.renderkit.table.TableBody$TableBodyRenderer$RowRenderer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.