blob: bedd267a64922183fa1524ff1e0e1e0eb8a0c515 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2020 Original authors and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Original authors and others - initial API and implementation
* Dirk Fauth <dirk.fauth@googlemail.com> - Bug 453707
******************************************************************************/
package org.eclipse.nebula.widgets.nattable.extension.glazedlists.tree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.nebula.widgets.nattable.tree.AbstractTreeRowModel;
import ca.odell.glazedlists.TreeList;
public class GlazedListTreeRowModel<T> extends AbstractTreeRowModel<T> {
public GlazedListTreeRowModel(GlazedListTreeData<T> treeData) {
super(treeData);
}
/**
* Note: The check only works for visible elements, therefore a check for an
* element that is below an collapsed element does not work.
*/
@Override
public boolean isCollapsed(int index) {
if (!this.getTreeData().isValidIndex(index)) {
return false;
}
return !this.getTreeData().getTreeList().isExpanded(index);
}
/**
* Collapse the tree node at the given visible index.
*
* @param index
* The index of the node in the collection that should be
* collapsed.
*
* @return Returns an empty list because the list transformation for
* expand/collapse is handled by GlazedLists {@link TreeList}.
*/
@Override
public List<Integer> collapse(int index) {
if (this.getTreeData().isValidIndex(index)) {
this.getTreeData().getTreeList().setExpanded(index, false);
notifyListeners();
}
return new ArrayList<>();
}
/**
* Will check the whole TreeList for its nodes and collapses them if they
* are expanded. After executing this method, all nodes in the TreeList will
* be collapsed.
*
* @return Returns an empty list because the list transformation for
* expand/collapse is handled by GlazedLists {@link TreeList}.
*/
@Override
public List<Integer> collapseAll() {
TreeList<T> treeList = this.getTreeData().getTreeList();
treeList.getReadWriteLock().writeLock().lock();
try {
// iterating directly over the TreeList is a lot faster than
// checking the nodes
// which is related that on collapsing we only need to iterate once
// from bottom to top
for (int i = (treeList.size() - 1); i >= 0; i--) {
/*
* Checks if the node at the given visible index has children
* and is collapsible. If it is it will be collapsed otherwise
* skipped. This backwards searching and collapsing mechanism is
* necessary to ensure to really get every collapsible node in
* the whole tree structure.
*/
if (hasChildren(i) && !isCollapsed(i)) {
treeList.setExpanded(i, false);
}
}
} finally {
treeList.getReadWriteLock().writeLock().unlock();
}
notifyListeners();
return new ArrayList<>();
}
/**
* Expand the tree node at the given visible index.
*
* @param index
* The index of the node in the collection that should be
* expanded.
*
* @return Returns an empty list because the list transformation for
* expand/collapse is handled by GlazedLists {@link TreeList}.
*/
@Override
public List<Integer> expand(int index) {
if (this.getTreeData().isValidIndex(index)) {
this.getTreeData().getTreeList().setExpanded(index, true);
notifyListeners();
}
return new ArrayList<>();
}
@Override
public List<Integer> expandToLevel(int parentIndex, int level) {
if (this.getTreeData().isValidIndex(parentIndex)) {
internalExpandToLevel(parentIndex, level);
notifyListeners();
}
return new ArrayList<>();
}
/**
* Performs the expand operations iteratively without notifying the
* listeners while processing.
*
* @param parentIndex
* The index of the node in the collection that should be
* expanded.
* @param level
* The level to which the tree nodes should be expanded.
*/
protected void internalExpandToLevel(int parentIndex, int level) {
if (this.getTreeData().isValidIndex(parentIndex)
&& depth(parentIndex) <= (level - 1)) {
this.getTreeData().getTreeList().setExpanded(parentIndex, true);
List<Integer> directChildren = getDirectChildIndexes(parentIndex);
// iterate backwards because the indexes are changing on expand
Collections.sort(directChildren, Collections.reverseOrder());
for (Integer child : directChildren) {
if (hasChildren(child) && depth(child) <= (level - 1)) {
internalExpandToLevel(child, level);
}
}
}
}
/**
* {@inheritDoc}
*
* @return Returns an empty list because the list transformation for
* expand/collapse is handled by GlazedLists {@link TreeList}.
*/
@Override
public List<Integer> expandAll() {
internalExpandAll();
notifyListeners();
return new ArrayList<>();
}
/**
* Performs the expand operations iteratively without notifying the
* listeners while processing.
*/
protected void internalExpandAll() {
TreeList<T> treeList = this.getTreeData().getTreeList();
boolean expandPerformed = false;
treeList.getReadWriteLock().writeLock().lock();
try {
// iterating directly over the TreeList is a lot faster than
// checking the nodes
for (int i = (treeList.size() - 1); i >= 0; i--) {
/*
* Checks if the node at the given visible index has children
* and is expandable. If it is it will be expanded otherwise
* skipped. This backwards searching and expanding mechanism is
* necessary to ensure to really get every expandable node in
* the whole tree structure.
*/
if (hasChildren(i) && isCollapsed(i)) {
treeList.setExpanded(i, true);
expandPerformed = true;
}
}
} finally {
treeList.getReadWriteLock().writeLock().unlock();
}
// if at least one element was expanded we need to perform the step
// again as we are only able to retrieve the visible nodes
if (expandPerformed) {
expandAll();
}
}
/**
* {@inheritDoc}
*
* @return Returns an empty list because the list transformation for
* expand/collapse is handled by GlazedLists {@link TreeList}.
*/
@Override
public List<Integer> expandToLevel(int level) {
internalExpandToLevel(level);
notifyListeners();
return new ArrayList<>();
}
/**
* Performs the expand operations iteratively without notifying the
* listeners while processing.
*
* @param level
* The level to which the tree nodes should be expanded.
*/
protected void internalExpandToLevel(int level) {
TreeList<T> treeList = this.getTreeData().getTreeList();
boolean expandPerformed = false;
treeList.getReadWriteLock().writeLock().lock();
try {
// iterating directly over the TreeList is a lot faster than
// checking the nodes
for (int i = (treeList.size() - 1); i >= 0; i--) {
/*
* Checks if the node at the given visible index has children,
* is expandable and is on a level below the given level. If it
* is it will be expanded otherwise skipped. This backwards
* searching and expanding mechanism is necessary to ensure to
* really get every expandable node in the whole tree structure.
*/
if (hasChildren(i) && isCollapsed(i) && treeList.getTreeNode(i).path().size() <= level) {
treeList.setExpanded(i, true);
expandPerformed = true;
}
}
} finally {
treeList.getReadWriteLock().writeLock().unlock();
}
// if at least one element was expanded we need to perform the step
// again as we are only able to retrieve the visible nodes
if (expandPerformed) {
expandToLevel(level);
}
}
/**
* {@inheritDoc}
*
* @since 1.6
*/
@Override
public GlazedListTreeData<T> getTreeData() {
return (GlazedListTreeData<T>) super.getTreeData();
}
}