Package org.openfaces.component.table

Source Code of org.openfaces.component.table.TreeTable

/*
* 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.component.table;

import org.openfaces.component.filter.Filter;
import org.openfaces.component.table.impl.NodeComparator;
import org.openfaces.component.table.impl.NodeInfo;
import org.openfaces.component.table.impl.NodeInfoForRow;
import org.openfaces.component.table.impl.RowComparator;
import org.openfaces.component.table.impl.TableDataModel;
import org.openfaces.component.table.impl.TempNodeParams;
import org.openfaces.renderkit.table.TreeTableRenderer;
import org.openfaces.util.AjaxUtil;
import org.openfaces.util.ValueBindings;

import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* The TreeTable component is used to display hierarchical data in a tabular format.
* It provides flexible configuration of the tree structure and content and supports
* such advanced features as sorting, interactive filtering, node selection (both multiple
* and single), keyboard navigation, and dynamic data loading (using Ajax). You can also
* specify a node preloading mode for expanding TreeTable nodes on the client or server side.
*
* @author Dmitry Pikhulya
*/
public class TreeTable extends AbstractTable {
    public static final String COMPONENT_TYPE = "org.openfaces.TreeTable";
    public static final String COMPONENT_FAMILY = "org.openfaces.TreeTable";

    private String nodeLevelVar;
    private String nodePathVar;
    private String nodeHasChildrenVar;

    private List<NodeInfoForRow> nodeInfoForRows;
    private Map<Object, NodeInfoForRow> rowIndexToExpansionData;

    private ExpansionState expansionState = new AllNodesCollapsed();
    private PreloadingState preloadingState = new PreloadingState();

    private Integer sortLevel;
    private Boolean foldingEnabled;
    private PreloadedNodes preloadedNodes;

    private String filterAcceptedRowStyle;
    private String filterAcceptedRowClass;
    private String filterSubsidiaryRowStyle;
    private String filterSubsidiaryRowClass;

    private String textStyle;
    private String textClass;


    private List<Filter> collectedFilters;
    private List<NodeInfoForRow> nodeInfoForRows_unfiltered;
    private int deepestLevel;
    private Boolean rowValuesForFilteringNeeded;

    public TreeTable() {
        setRendererType("org.openfaces.TreeTableRenderer");
    }

    @Override
    public String getFamily() {
        return COMPONENT_TYPE;
    }

    @Override
    public Object saveState(FacesContext context) {
        Object superState = super.saveState(context);
        return new Object[]{superState, nodeLevelVar, nodePathVar, nodeHasChildrenVar, expansionState,
                sortLevel, foldingEnabled, preloadedNodes,
                filterAcceptedRowStyle, filterAcceptedRowClass, filterSubsidiaryRowStyle, filterSubsidiaryRowClass,
                textStyle, textClass, preloadingState};
    }

    @Override
    public void restoreState(FacesContext context, Object stateObj) {
        Object[] state = (Object[]) stateObj;
        int i = 0;
        super.restoreState(context, state[i++]);
        nodeLevelVar = (String) state[i++];
        nodePathVar = (String) state[i++];
        nodeHasChildrenVar = (String) state[i++];
        expansionState = (ExpansionState) state[i++];
        sortLevel = (Integer) state[i++];
        foldingEnabled = (Boolean) state[i++];
        preloadedNodes = (PreloadedNodes) state[i++];
        filterAcceptedRowStyle = (String) state[i++];
        filterAcceptedRowClass = (String) state[i++];
        filterSubsidiaryRowStyle = (String) state[i++];
        filterSubsidiaryRowClass = (String) state[i++];
        textStyle = (String) state[i++];
        textClass = (String) state[i++];
        preloadingState = (PreloadingState) state[i++];
    }

    @Override
    protected void beforeProcessDecodes(FacesContext context) {
        super.beforeProcessDecodes(context);
        TableDataModel model = getModel();
        model.prepareForRestoringRowIndexes();
        restoreRows(false);
    }

    @Override
    protected void restoreRows(boolean forceDecoding) {
        FacesContext context = getFacesContext();
        TableDataModel model = getModel();
        boolean readFreshData =
                TreeTableRenderer.isAjaxFoldingInProgress(context) ||
                        TreeTableRenderer.isAjaxRowLoadingInProgress(context) ||
                        isRowsDecodingRequired();
        prepareModelFromTreeStructure(readFreshData);

        TableDataModel.RestoredRowIndexes rri = model.restoreRowIndexes();
        Set<Integer> unavailableRowIndexes = rri.getUnavailableRowIndexes();
        setUnavailableRowIndexes(unavailableRowIndexes);

        int[] oldIndexes = rri.getOldIndexes();
        List<NodeInfoForRow> newNodeInfoForRows = new ArrayList<NodeInfoForRow>(oldIndexes.length);
        for (int oldIndex : oldIndexes) {
            NodeInfoForRow nodeInfo = oldIndex != -1 ? nodeInfoForRows.get(oldIndex) : null;
            newNodeInfoForRows.add(nodeInfo);
        }
        nodeInfoForRows = newNodeInfoForRows;
    }

    @Override
    protected String getRowClientSuffixByIndex(int index) {
        if (nodeInfoForRows == null || index >= nodeInfoForRows.size()) {
            // can be the case when pre-rendering TreeTable inspections are made by the filters
            return null;
        }

        NodeInfoForRow nodeInfoForRow = nodeInfoForRows.get(index);
        if (nodeInfoForRow == null)
            return null;
        TreePath indexPath = nodeInfoForRow.getNodeIndexPath();
        return indexPathToStr(indexPath);
    }


    @Override
    public int getRowIndexByClientSuffix(String id) {
        for (int i = 0, count = nodeInfoForRows.size(); i < count; i++) {
            NodeInfoForRow nodeInfoForRow = nodeInfoForRows.get(i);
            TreePath nodeIndexPath = nodeInfoForRow.getNodeIndexPath();
            String indexPathStr = indexPathToStr(nodeIndexPath);
            if (id.equals(indexPathStr))
                return i;
        }
        return -1;
    }

    private String indexPathToStr(TreePath indexPath) {
        StringBuilder sb = new StringBuilder();
        for (TreePath path = indexPath; path != null; path = path.getParentPath()) {
            if (sb.length() > 0) sb.insert(0, "_");
            sb.insert(0, path.getValue());
        }
        return sb.toString();
    }


    public ExpansionState getExpansionState() {
        return expansionState;
    }

    public void setExpansionState(ExpansionState expansionState) {
        this.expansionState = expansionState;
    }

    public boolean isFoldingEnabled() {
        return ValueBindings.get(this, "foldingEnabled", foldingEnabled, true);
    }

    public void setFoldingEnabled(boolean foldingEnabled) {
        this.foldingEnabled = foldingEnabled;
    }

    public PreloadedNodes getPreloadedNodes() {
        return ValueBindings.get(this, "preloadedNodes", preloadedNodes, PreloadedNodes.class);
    }

    public void setPreloadedNodes(PreloadedNodes preloadedNodes) {
        this.preloadedNodes = preloadedNodes;
    }

    public int getSortLevel() {
        return ValueBindings.get(this, "sortLevel", sortLevel, -1);
    }

    public void setSortLevel(int sortLevel) {
        this.sortLevel = sortLevel;
    }

    public String getNodeLevelVar() {
        return nodeLevelVar;
    }

    public void setNodeLevelVar(String nodeLevelVar) {
        this.nodeLevelVar = nodeLevelVar;
    }

    public String getNodeHasChildrenVar() {
        return nodeHasChildrenVar;
    }

    public void setNodeHasChildrenVar(String nodeHasChildrenVar) {
        this.nodeHasChildrenVar = nodeHasChildrenVar;
    }

    public String getNodePathVar() {
        return nodePathVar;
    }

    public void setNodePathVar(String nodePathVar) {
        this.nodePathVar = nodePathVar;
    }

    public String getFilterAcceptedRowStyle() {
        return ValueBindings.get(this, "filterAcceptedRowStyle", filterAcceptedRowStyle);
    }

    public void setFilterAcceptedRowStyle(String filterAcceptedRowStyle) {
        this.filterAcceptedRowStyle = filterAcceptedRowStyle;
    }

    public String getFilterAcceptedRowClass() {
        return ValueBindings.get(this, "filterAcceptedRowClass", filterAcceptedRowClass);
    }

    public void setFilterAcceptedRowClass(String filterAcceptedRowClass) {
        this.filterAcceptedRowClass = filterAcceptedRowClass;
    }

    public String getFilterSubsidiaryRowStyle() {
        return ValueBindings.get(this, "filterSubsidiaryRowStyle", filterSubsidiaryRowStyle);
    }

    public void setFilterSubsidiaryRowStyle(String filterSubsidiaryRowStyle) {
        this.filterSubsidiaryRowStyle = filterSubsidiaryRowStyle;
    }

    public String getFilterSubsidiaryRowClass() {
        return ValueBindings.get(this, "filterSubsidiaryRowClass", filterSubsidiaryRowClass);
    }

    public String getTextStyle() {
        return ValueBindings.get(this, "textStyle", textStyle);
    }

    public void setTextStyle(String textStyle) {
        this.textStyle = textStyle;
    }

    public String getTextClass() {
        return ValueBindings.get(this, "textClass", textClass);
    }

    public void setTextClass(String textClass) {
        this.textClass = textClass;
    }

    public void setFilterSubsidiaryRowClass(String filterSubsidiaryRowClass) {
        this.filterSubsidiaryRowClass = filterSubsidiaryRowClass;
    }

    public TreeStructure getTreeStructure() {
        return findTreeStructureChild();
    }

    private TreeStructure findTreeStructureChild() {
        List<UIComponent> children = getChildren();
        TreeStructure treeStructure = null;
        for (UIComponent child : children) {
            if (child instanceof TreeStructure) {
                if (treeStructure != null)
                    throw new RuntimeException("There should be only one tree structure child under the TreeTable component");
                treeStructure = (TreeStructure) child;
            }
        }
        return treeStructure;
    }

    @Override
    public void processUpdates(FacesContext context) {
        super.processUpdates(context);
        ValueExpression expansionStateExpression = getValueExpression("expansionState");
        if (expansionStateExpression != null)
            expansionStateExpression.setValue(context.getELContext(), expansionState);
    }

    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        if (AjaxUtil.getSkipExtraRenderingOnPortletsAjax(context))
            return;

        beforeRenderResponse(context);

        ValueExpression expansionStateExpression = getValueExpression("expansionState");
        if (expansionStateExpression != null) {
            ExpansionState newExpansionState = (ExpansionState) expansionStateExpression.getValue(context.getELContext());
            if (newExpansionState != null)
                expansionState = newExpansionState;
        }
        updateSortingFromBindings();
        prepareModelFromTreeStructure(true);

        super.encodeBegin(context);
    }

    @Override
    public void encodeChildren(FacesContext context) throws IOException {
        if (AjaxUtil.getSkipExtraRenderingOnPortletsAjax(context))
            return;
        super.encodeChildren(context);
    }


    @Override
    public void encodeEnd(FacesContext context) throws IOException {
        if (AjaxUtil.getSkipExtraRenderingOnPortletsAjax(context))
            return;
        super.encodeEnd(context);
    }

    public int loadSubNodes(int rowIndex) {
        if (!isRowAvailableAfterRestoring(rowIndex))
            return 0;
        NodeInfoForRow nodeInfo = nodeInfoForRows.get(rowIndex);

        if (!nodeInfo.getNodeHasChildren())
            throw new IllegalArgumentException("The node at this rowIndex doesn't have children: " + rowIndex);
        if (nodeInfo.getChildrenPreloaded())
            throw new IllegalArgumentException("The node at this rowIndex already has its children preloaded: " + rowIndex);
        TreeStructure treeStructure = findTreeStructureChild();
        TreePath nodeKeyPath = nodeInfo.getNodeKeyPath();
        TreePath nodePath = nodeInfo.getNodePath();
        TreePath nodeIndexPath = nodeInfo.getNodeIndexPath();
        if (!locateNodeChildrenByKeyPath(treeStructure, nodeKeyPath))
            return 0; // the node has probably been removed (can be the case here in server-side state saving)

        List<NodeInfoForRow> nodeInfoForRows = new ArrayList<NodeInfoForRow>();
        List<NodeInfoForRow> nodeInfoForRows_unfiltered = new ArrayList<NodeInfoForRow>();
        int sortLevel = getSortLevel();
        List<Filter> filters = getActiveFilters();
        deepestLevel = 0;
        int childNodeCount = collectTreeNodeDatas(treeStructure, filters, nodeInfoForRows, null, nodeInfoForRows_unfiltered,
                nodeKeyPath.getLevel() + 1, sortLevel, nodePath, nodeKeyPath, nodeIndexPath, true);
        nodeInfo.setChildNodeCount(childNodeCount);
        nodeInfo.setChildrenPreloaded(true);
        this.nodeInfoForRows.addAll(rowIndex + 1, nodeInfoForRows);
        this.nodeInfoForRows_unfiltered.addAll(nodeInfoForRows_unfiltered);


        if (isRendered()) {
            Map<Object, NodeInfoForRow> newRowIndexToExpansionData = new HashMap<Object, NodeInfoForRow>();

            int addedRowCount = nodeInfoForRows.size();
            List<Object> addedRowDatas = new ArrayList<Object>(addedRowCount);
            List<TreePath> addedRowKeys = new ArrayList<TreePath>(addedRowCount);
            for (int i = 0; i < addedRowCount; i++) {
                NodeInfoForRow ni = nodeInfoForRows.get(i);
                addedRowDatas.add(ni.getNodeData());
                addedRowKeys.add(ni.getNodeKeyPath());
                newRowIndexToExpansionData.put(rowIndex + 1 + i, ni);
            }

            getModel().addRows(rowIndex + 1, addedRowDatas, addedRowKeys);

            for (Map.Entry<Object, NodeInfoForRow> entry : rowIndexToExpansionData.entrySet()) {
                Object keyObj = entry.getKey();
                if (keyObj instanceof String) {
                    newRowIndexToExpansionData.put(keyObj, entry.getValue());
                    continue;
                }
                Integer i = (Integer) keyObj;
                if (i > rowIndex)
                    i = i + addedRowCount;
                newRowIndexToExpansionData.put(i, entry.getValue());
            }

            rowIndexToExpansionData = newRowIndexToExpansionData;
        }
        return nodeInfoForRows.size();
    }

    private boolean locateNodeChildrenByKeyPath(TreeStructure treeStructure, TreePath nodeKeyPath) {
        List<Object> nodesFromTopToBottom = new LinkedList<Object>();
        do {
            nodesFromTopToBottom.add(0, nodeKeyPath.getValue());
            nodeKeyPath = nodeKeyPath.getParentPath();
        } while (nodeKeyPath != null);

        treeStructure.goToTopLevel();
        while (nodesFromTopToBottom.size() > 0) {
            Object nodeKey = nodesFromTopToBottom.remove(0);
            int nodeCount = treeStructure.getNodeCount();
            int nodeIndex = 0;
            for (; nodeIndex < nodeCount; nodeIndex++) {
                treeStructure.setNodeIndex(nodeIndex);
                Object currentNodeKey = treeStructure.getNodeKey();
                if (nodeKey.equals(currentNodeKey))
                    break;
            }
            if (nodeIndex == nodeCount)
                return false;
            treeStructure.goToChildLevel();
        }
        return true;
    }

    private void prepareModelFromTreeStructure(boolean readActualData) {
        TreeStructure treeStructure = findTreeStructureChild();
        boolean proceedWithReadingData = treeStructure != null && isRendered() && readActualData;
        if (proceedWithReadingData)
            treeStructure.goToTopLevel();

        List<NodeInfoForRow> nodeInfoForRows = new ArrayList<NodeInfoForRow>();
        List<NodeInfoForRow> nodeInfoForRows_unfiltered = new ArrayList<NodeInfoForRow>();
        int sortLevel = getSortLevel();
        List<Filter> filters = getActiveFilters();
        deepestLevel = 0;
        rowValuesForFilteringNeeded = null;
        FacesContext context = FacesContext.getCurrentInstance();
        if (!AjaxUtil.isAjaxPortionRequest(context, this)) {
            if (isTableRerenderingNeeded(context)) {
                preloadingState = new PreloadingState();
            }
        }
        int rootNodeCount = proceedWithReadingData
                ? collectTreeNodeDatas(treeStructure, filters, nodeInfoForRows, null, nodeInfoForRows_unfiltered, 0, sortLevel, null, null, null, true)
                : 0;

        this.nodeInfoForRows = nodeInfoForRows;
        this.nodeInfoForRows_unfiltered = nodeInfoForRows_unfiltered;
        collectedFilters = filters;

        updateModelRowDatasAndExpansionData(nodeInfoForRows, rootNodeCount);
    }

    private void updateModelRowDatasAndExpansionData(List<NodeInfoForRow> nodeInfoForRows, int rootNodeCount) {
        setModelFromNodeInfos(nodeInfoForRows);
        if (!isRendered())
            return;

        Map<Object, NodeInfoForRow> rowIndexToExpansionData = new HashMap<Object, NodeInfoForRow>();
        if (rootNodeCount != -1) {
            NodeInfoForRow nodeInfoForRow = new NodeInfoForRow(null, null, null, -1, true, false);
            nodeInfoForRow.setNodeHasChildren(rootNodeCount > 0);
            nodeInfoForRow.setChildrenPreloaded(true);
            nodeInfoForRow.setChildNodeCount(rootNodeCount);
            rowIndexToExpansionData.put("root", nodeInfoForRow);
        }
        for (int rowIndex = 0; rowIndex < nodeInfoForRows.size(); rowIndex++) {
            NodeInfoForRow nodeInfoForRow = nodeInfoForRows.get(rowIndex);
            if (nodeInfoForRow.getNodeHasChildren())
                rowIndexToExpansionData.put(rowIndex, nodeInfoForRow);
        }
        this.rowIndexToExpansionData = rowIndexToExpansionData;
    }

    private void setModelFromNodeInfos(List<NodeInfoForRow> nodeInfoForRows) {
        List<Object> rowDatas = new ArrayList<Object>(nodeInfoForRows.size());
        List<TreePath> rowKeys = new ArrayList<TreePath>(nodeInfoForRows.size());
        for (NodeInfoForRow nodeInfo : nodeInfoForRows) {
            rowDatas.add(nodeInfo != null ? nodeInfo.getNodeData() : null);
            rowKeys.add(nodeInfo != null ? nodeInfo.getNodeKeyPath() : null);
        }

        getModel().setWrappedData(rowDatas, rowKeys);
    }

    @Override
    public Map<Object, ? extends NodeInfo> getTreeStructureMap(FacesContext context) {
        return rowIndexToExpansionData;
    }

    private int collectTreeNodeDatas(TreeStructure treeStructure, List<Filter> activeFilters, // todo: too much parameters. review this method
                                     List<NodeInfoForRow> nodeInfosForRendering,
                                     List<NodeInfoForRow> nodeInfosAllFiltered,
                                     List<NodeInfoForRow> nodeInfos_unfiltered,
                                     int level, int sortLevel,
                                     TreePath parentNodePath,
                                     TreePath parentNodeKeyPath,
                                     TreePath parentNodeIndexPath,
                                     boolean thisLevelInitiallyVisible) {
        if (level > deepestLevel)
            deepestLevel = level;

        List<TempNodeParams> thisLevelNodeParams = new ArrayList<TempNodeParams>();
        PreloadedNodes preloadedNodes = getPreloadedNodes();
        for (int nodeIndex = 0, nodeCount = treeStructure.getNodeCount(); nodeIndex < nodeCount; nodeIndex++) {
            treeStructure.setNodeIndex(nodeIndex);
            if (!treeStructure.isNodeAvailable())
                break;
            Object nodeData = treeStructure.getNodeData();
            Object nodeKey = treeStructure.getNodeKey();
            boolean nodeHasChildren = treeStructure.getNodeHasChildren();
            TreePath nodePath = new TreePath(nodeData, parentNodePath);
            TreePath nodeKeyPath = new TreePath(nodeKey, parentNodeKeyPath);
            TreePath nodeIndexPath = new TreePath(nodeIndex, parentNodeIndexPath);
            boolean nodeExpanded = isNodeExpanded(nodeKeyPath);
            if (nodeExpanded && !preloadingState.isPreloaded(nodePath)) {
                preloadingState.addPreloadedTreePath(nodePath);
            }
            NodeInfoForRow nodeInfo = new NodeInfoForRow(nodePath, nodeKeyPath, nodeIndexPath, level, nodeExpanded, thisLevelInitiallyVisible);
            boolean preloadChildren = nodeHasChildren && (
                    preloadingState.isPreloaded(nodePath) || (preloadedNodes != null && preloadedNodes.getPreloadChildren(nodeKeyPath))
            );
            TempNodeParams thisNodeParams = new TempNodeParams(nodeData, nodeInfo, nodeExpanded, preloadChildren);
            boolean[] flagsArray = new boolean[activeFilters.size()];
            boolean rowAccepted = TableDataModel.filterRow(activeFilters, thisNodeParams, flagsArray);
            thisNodeParams.setRowAccepted(rowAccepted);
            thisNodeParams.setFilteringFlags(flagsArray);

            boolean lookForFilteredSubrows =
                    activeFilters.size() > 0 &&
                    !rowAccepted && /* when some filters are active we shouldn't extract invisible nodes if parent node satisfies filtering criteria */
                    !treeStructure.isEnteringInfiniteRecursion() /* avoid searching the endlessly-recurring hierarchies */;

            if (isRowValuesForFilteringNeeded() || lookForFilteredSubrows || preloadChildren) {
                treeStructure.goToChildLevel();
                List<NodeInfoForRow> subtreeNodeInfosForRendering = new ArrayList<NodeInfoForRow>();
                List<NodeInfoForRow> subtreeNodeInfosAllFiltered = new ArrayList<NodeInfoForRow>();
                List<NodeInfoForRow> subtreeNodeInfos_unfiltered = new ArrayList<NodeInfoForRow>();
                int filteredChildCount = collectTreeNodeDatas(
                        treeStructure, activeFilters, subtreeNodeInfosForRendering, subtreeNodeInfosAllFiltered, subtreeNodeInfos_unfiltered,
                        level + 1, sortLevel, nodePath, nodeKeyPath, nodeIndexPath, thisLevelInitiallyVisible && nodeExpanded);
                nodeInfo.setNodeHasChildren(filteredChildCount > 0);
                nodeInfo.setChildrenPreloaded(preloadChildren);
                nodeInfo.setChildNodeCount(filteredChildCount);
                thisNodeParams.setSubtreeNodeInfosForRendering(subtreeNodeInfosForRendering);
                thisNodeParams.setSubtreeNodeInfosAllFiltered(subtreeNodeInfosAllFiltered);
                thisNodeParams.setSubtreeNodeInfos_unfiltered(subtreeNodeInfos_unfiltered);
                treeStructure.goToParentLevel();
            } else {
                nodeInfo.setNodeHasChildren(nodeHasChildren);
                nodeInfo.setChildrenPreloaded(false);
                nodeInfo.setChildNodeCount(0);
            }

            thisLevelNodeParams.add(thisNodeParams);
        }
        if (sortLevel == -1 || level == sortLevel)
            sortNodes(thisLevelNodeParams, level);

        List<boolean[]> thisLevelNodeFilteringFlags = new ArrayList<boolean[]>();
        List<TempNodeParams> filteredNodeParams = new ArrayList<TempNodeParams>();
        for (TempNodeParams rowObj : thisLevelNodeParams) {
            thisLevelNodeFilteringFlags.add(rowObj.getFilteringFlags());
            if (rowObj.isRowAccepted())
                filteredNodeParams.add(rowObj);
        }

        for (TempNodeParams nodeParams : filteredNodeParams) {
            NodeInfoForRow nodeInfoForRow = nodeParams.getNodeInfoForRow();
            nodeInfoForRow.setAcceptedByFilters(true);
        }

        int passedChildCount = 0;
        for (int i = 0; i < thisLevelNodeParams.size(); i++) {
            TempNodeParams nodeParams = thisLevelNodeParams.get(i);
            boolean[] nodeFilteringFlags = thisLevelNodeFilteringFlags.get(i);
            NodeInfoForRow nodeInfoForRow = nodeParams.getNodeInfoForRow();
            nodeInfoForRow.setNodeFilteringFlags(nodeFilteringFlags);
            List<NodeInfoForRow> subtreeNodeInfos_unfiltered = nodeParams.getSubtreeNodeInfos_unfiltered();
            nodeInfos_unfiltered.add(nodeInfoForRow);
            if (subtreeNodeInfos_unfiltered != null)
                nodeInfos_unfiltered.addAll(subtreeNodeInfos_unfiltered);

            List<NodeInfoForRow> subtreeNodeInfosForRendering = nodeParams.getSubtreeNodeInfosForRendering();
            List<NodeInfoForRow> subtreeNodeInfosAllFiltered = nodeParams.getSubtreeNodeInfosAllFiltered();
            if (nodeInfoForRow.isAcceptedByFilters() || (subtreeNodeInfosAllFiltered != null && subtreeNodeInfosAllFiltered.size() > 0)) {
                nodeInfosForRendering.add(nodeInfoForRow);
                if (nodeInfosAllFiltered != null)
                    nodeInfosAllFiltered.add(nodeInfoForRow);
                passedChildCount++;
                boolean preloadChildrenToClient = nodeParams.getPreloadChildrenToClient();
                if (preloadChildrenToClient)
                    nodeInfosForRendering.addAll(subtreeNodeInfosForRendering);
                if (nodeInfosAllFiltered != null && subtreeNodeInfosAllFiltered != null)
                    nodeInfosAllFiltered.addAll(subtreeNodeInfosAllFiltered);
            }

        }

        return passedChildCount;
    }

    private boolean isRowValuesForFilteringNeeded() {
        if (rowValuesForFilteringNeeded != null)
            return rowValuesForFilteringNeeded;

        List<Filter> filters = getFilters();
        boolean rowListNeeded = false;
        for (Filter filter : filters) {
            if (filter.getWantsRowList()) {
                rowListNeeded = true;
                break;
            }
        }
        rowValuesForFilteringNeeded = rowListNeeded;
        return rowListNeeded;
    }

    private void sortNodes(List<TempNodeParams> thisLevelArrays, int level) {
        List<SortingRule> sortingRules = getSortingRules();
        Comparator<Object> sortingComparator = createRowDataComparator(null, sortingRules);
        if (sortingComparator == null)
            return;
        String nodeLevelVar = getNodeLevelVar();
        if (nodeLevelVar != null) {
            Map<String, Object> requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
            requestMap.put(nodeLevelVar, level);
        }

        Collections.sort(thisLevelArrays, sortingComparator);
    }

    @Override
    protected RowComparator createRowComparator(
            FacesContext facesContext,
            ValueExpression sortingExpression,
            Comparator<Object> sortingComparator,
            Map<String, Object> requestMap,
            boolean sortAscending) {
        return new NodeComparator(this, facesContext, sortingExpression, sortingComparator, requestMap, sortAscending);
    }

    /**
     * This method is only for internal usage from within the OpenFaces library. It shouldn't be used explicitly
     * by any application code.
     */
    public Runnable populateSortingExpressionParams(final String var, final Map<String, Object> requestMap, Object collectionObject) {
//        protected Runnable populateSortingExpressionParams(final Map<String, Object> requestMap, Object collectionObject) {
            TempNodeParams nodeParams = (TempNodeParams) collectionObject;
            Object nodeData = nodeParams.getNodeData();
            NodeInfoForRow nodeInfo = nodeParams.getNodeInfoForRow();

            requestMap.put(var, nodeData);
            final Object prevNodePathVarValue;
            if (nodePathVar != null)
                prevNodePathVarValue = requestMap.put(nodePathVar, nodeInfo.getNodePath());
            else
                prevNodePathVarValue = null;
            final Object prevNodeHasChildrenVarValue;
            if (nodeHasChildrenVar != null)
                prevNodeHasChildrenVarValue = requestMap.put(nodeHasChildrenVar, nodeInfo.getNodeHasChildren());
            else
                prevNodeHasChildrenVarValue = null;
            String nodeParamsKey = TempNodeParams.class.getName();
            final Object prevNodeParamsValue = requestMap.put(nodeParamsKey, nodeParams);
            return new Runnable() {
                public void run() {
                    if (nodePathVar != null)
                        requestMap.put(nodePathVar, prevNodePathVarValue);
                    if (nodeHasChildrenVar != null)
                        requestMap.put(nodeHasChildrenVar, prevNodeHasChildrenVarValue);
                    String nodeParamsKey = TempNodeParams.class.getName();
                    requestMap.put(nodeParamsKey, prevNodeParamsValue);
                }
            };
        }

    @Override
    public void setRowIndex(int rowIndex) {
        super.setRowIndex(rowIndex);
        boolean rowAvailable = isRowAvailable();
        Map<String, Object> requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
        if (nodePathVar != null)
            requestMap.put(nodePathVar, rowAvailable ? getNodePath() : null);
        if (nodeLevelVar != null)
            requestMap.put(nodeLevelVar, rowAvailable ? getNodeLevel() : null);
        if (nodeHasChildrenVar != null)
            requestMap.put(nodeHasChildrenVar, rowAvailable ? getNodeHasChildren() : null);
    }


    public Object getNodeKey() {
        if (nodeInfoForRows == null)
            return null;
        int rowIndex = getRowIndex();
        if (rowIndex == -1)
            return null;
        if (!isRowAvailableAfterRestoring(rowIndex))
            return null;
        return getNodeKey(rowIndex);
    }

    public TreePath getNodeKeyPath() {
        if (nodeInfoForRows == null)
            return null;
        int rowIndex = getRowIndex();
        if (rowIndex == -1)
            return null;
        if (!isRowAvailableAfterRestoring(rowIndex))
            return null;
        return getNodeKeyPath(rowIndex);
    }

    public Object getNodeKey(int rowIndex) {
        if (!isRowAvailableAfterRestoring(rowIndex))
            return null;
        NodeInfoForRow nodeInfo = nodeInfoForRows.get(rowIndex);
        return nodeInfo.getNodeKey();
    }

    public Object getNodeData(int rowIndex) {
        if (!isRowAvailableAfterRestoring(rowIndex))
            return null;
        NodeInfoForRow nodeInfo = nodeInfoForRows.get(rowIndex);
        return nodeInfo.getNodeData();
    }

    public int getNodeLevel() {
        if (nodeInfoForRows == null)
            return 0;
        int rowIndex = getRowIndex();
        if (rowIndex == -1)
            return 0;
        NodeInfoForRow nodeInfo = nodeInfoForRows.get(rowIndex);
        return nodeInfo.getNodeLevel();
    }

    public TreePath getNodePath() {
        if (nodeInfoForRows == null)
            return null;
        int rowIndex = getRowIndex();
        if (rowIndex == -1)
            return null;
        return getNodePath(rowIndex);
    }

    public TreePath getNodePath(int rowIndex) {
        if (!isRowAvailableAfterRestoring(rowIndex))
            return null;
        NodeInfoForRow nodeInfo = nodeInfoForRows.get(rowIndex);
        return nodeInfo.getNodePath();
    }

    public TreePath getNodeKeyPath(int rowIndex) {
        if (!isRowAvailableAfterRestoring(rowIndex))
            return null;
        NodeInfoForRow nodeInfo = nodeInfoForRows.get(rowIndex);
        return nodeInfo.getNodeKeyPath();
    }

    public boolean getNodeHasChildren() {
        if (nodeInfoForRows == null)
            return false;
        int rowIndex = getRowIndex();
        return getNodeHasChildren(rowIndex);
    }

    @Override
    protected boolean getNodeHasChildren(int rowIndex) {
        if (rowIndex == -1)
            return false;
        if (!isRowAvailableAfterRestoring(rowIndex))
            return false;
        NodeInfoForRow nodeInfo = nodeInfoForRows.get(rowIndex);
        return nodeInfo.getNodeHasChildren();
    }

    @Override
    public String getClientRowKey() {
        TreePath treePath = (TreePath) super.getRowKey();
        Object nodeKey = treePath.getValue();
        if (nodeKey == null) return null;
        return nodeKey.toString();
    }

    public boolean isNodeExpanded() {
        if (nodeInfoForRows == null)
            return false;
        int rowIndex = getRowIndex();

        if (rowIndex == -1)
            return false;
        if (!isRowAvailableAfterRestoring(rowIndex))
            return false;

        NodeInfoForRow nodeInfo = nodeInfoForRows.get(rowIndex);
        return nodeInfo.isExpanded();
    }

    public boolean isNodeInitiallyVisible() {
        if (nodeInfoForRows == null)
            return false;
        int rowIndex = getRowIndex();
        if (rowIndex == -1)
            return false;
        if (!isRowAvailableAfterRestoring(rowIndex))
            return false;

        NodeInfoForRow nodeInfo = nodeInfoForRows.get(rowIndex);
        return nodeInfo.isNodeInitiallyVisible();
    }

    public boolean isNodeAcceptedByFilters() {
        if (nodeInfoForRows == null)
            return false;
        int rowIndex = getRowIndex();
        if (rowIndex == -1)
            return false;
        if (!isRowAvailableAfterRestoring(rowIndex))
            return false;

        NodeInfoForRow nodeInfo = nodeInfoForRows.get(rowIndex);
        return nodeInfo.isAcceptedByFilters();
    }

    public boolean isFilteringPerformed() {
        if (collectedFilters == null || collectedFilters.size() == 0)
            return false;
        for (Filter filter : collectedFilters) {
            if (!filter.isAcceptingAllRecords())
                return true;
        }
        return false;
    }

    @Override
    public boolean isNodeExpanded(TreePath keyPath) {
        if (keyPath == null)
            return false;
        return expansionState.isNodeExpanded(keyPath);
    }

    @Override
    public void setNodeExpanded(TreePath keyPath, boolean expanded) {
        boolean oldExpansion = isNodeExpanded(keyPath);
        if (expanded == oldExpansion)
            return;
        expansionState = expansionState.getMutableExpansionState();
        expansionState.setNodeExpanded(keyPath, expanded);
    }

    /**
     * Expands a node at the specified path, and all of its parent nodes.
     * @param nodeKeyPath a tree path consisting of node key objects. Note that if the <o:dynamicTreeStructure> tag
     * doesn't have a nodeKey attribute then  node key objects are considered to be the same as the appropriate node
     * data objects.
     */
    public void expandNodePath(TreePath nodeKeyPath) {
        expansionState = expansionState.getMutableExpansionState();
        expansionState.expandNodePath(nodeKeyPath);
    }

    public List getRowListForFiltering(Filter filter) {
        List<boolean[]> allRowFilteringFlags = new ArrayList<boolean[]>();
        for (NodeInfoForRow info : nodeInfoForRows_unfiltered) {
            allRowFilteringFlags.add(info.getNodeFilteringFlags());
        }
        List<Object> result = TableDataModel.getRowListForFiltering(filter, collectedFilters, nodeInfoForRows_unfiltered, allRowFilteringFlags);
        return result;
    }

    protected Object setupDataRetrievalContext(Object data, Map<String, Object> requestMap, String var) {
        NodeInfoForRow nodeInfo = (data instanceof NodeInfoForRow) ? (NodeInfoForRow) data : ((TempNodeParams) data).getNodeInfoForRow();
        requestMap.get(var);
        Object nodeData = nodeInfo.getNodeData();
        requestMap.put(var, nodeData);

        String nodeLevelVar = getNodeLevelVar();
        if (nodeLevelVar != null)
            requestMap.put(nodeLevelVar, nodeInfo.getNodeLevel());

        String nodePathVar = getNodePathVar();
        if (nodePathVar != null)
            requestMap.put(nodePathVar, nodeInfo.getNodePath());

        String nodeHasChildrenVar = getNodeHasChildrenVar();
        if (nodeHasChildrenVar != null)
            requestMap.put(nodeHasChildrenVar, nodeInfo.getNodeHasChildren());
        return nodeData;
    }

    public boolean isDataSourceEmpty() {
        if (nodeInfoForRows_unfiltered == null)
            return true;
        int originalRowCount = nodeInfoForRows_unfiltered.size();
        return originalRowCount == 0;
    }


    protected boolean isRowInColumnSelected(BaseColumn column, Map<String, Object> requestMap, String var) {
        String nodeParamsKey = TempNodeParams.class.getName();
        TempNodeParams params = (TempNodeParams) requestMap.get(nodeParamsKey);
        TreePath nodeKeyPath = params.getNodeInfoForRow().getNodeKeyPath();
        List selectedNodePaths = getSelectedNodePaths(column);
        return selectedNodePaths.contains(nodeKeyPath);
    }

    private List getSelectedNodePaths(BaseColumn column) {
        if (column instanceof SelectionColumn) {
            TreeTableSelection selection = (TreeTableSelection) getSelection();
            return selection.getSelectedNodeKeyPaths();
        } else if (column instanceof CheckboxColumn) {
            return ((CheckboxColumn) column).getSelectedRowKeys();
        } else
            throw new IllegalArgumentException("Unkown column type: " + (column != null ? column.getClass().getName() : "null"));
    }

    @Override
    protected boolean isRowsDecodingRequired() {
        // That's because we need to decode TreeTable every request to save expansion state.
        return true;
    }

    private boolean isTableRerenderingNeeded(FacesContext context) {
        String render = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().
                get(AjaxUtil.PARAM_RENDER);
        return render != null ? render.contains(getClientId(context)) : false;
    }

}
TOP

Related Classes of org.openfaces.component.table.TreeTable

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.