org.nuiton.jaxx.runtime.swing.tree.DefaultIterableTreeNode Maven / Gradle / Ivy
package org.nuiton.jaxx.runtime.swing.tree;
/*-
* #%L
* JAXX :: Runtime
* %%
* Copyright (C) 2008 - 2024 Code Lutin, Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import javax.swing.tree.TreeNode;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
/**
* Default implementation of a {@link IterableTreeNode}.
*
* Created by tchemit on 05/01/2018.
*
* @author Tony Chemit - [email protected]
*/
public class DefaultIterableTreeNode implements IterableTreeNode {
private static final Enumeration> EMPTY_ENUMERATION = Collections.emptyEnumeration();
private IterableTreeNode parent;
private List> children;
private O userObject;
private boolean allowsChildren;
public DefaultIterableTreeNode(O userObject, boolean allowsChildren) {
this.userObject = userObject;
this.allowsChildren = allowsChildren;
}
@Override
public IterableTreeNode> getChildAt(int childIndex) {
return isEmpty() ? null : children.get(childIndex);
}
@Override
public final int getChildCount() {
return isEmpty() ? 0 : children.size();
}
@Override
public final boolean isRoot() {
return parent == null;
}
@Override
public IterableTreeNode> getRoot() {
return isRoot() ? this : getParent().getRoot();
}
@Override
public IterableTreeNode> getParent() {
return parent;
}
@Override
public final void setParent(IterableTreeNode> parent) {
this.parent = parent;
}
@Override
public final int getIndex(TreeNode node) {
Objects.requireNonNull(node);
if (!(node instanceof DefaultIterableTreeNode)) {
throw new IllegalArgumentException("node should be a DefaultIterableTreeNode, but was: " + node.getClass().getName());
}
return isEmpty() ? -1 : children.indexOf(node);
}
@Override
public final boolean getAllowsChildren() {
return allowsChildren;
}
@Override
public final void setAllowsChildren(boolean allowsChildren) {
this.allowsChildren = allowsChildren;
}
@Override
public boolean isLeaf() {
return isEmpty();
}
@Override
public final Enumeration children() {
return isEmpty() ? EMPTY_ENUMERATION : Collections.enumeration(children);
}
@Override
public final Iterator> iterator() {
return isEmpty() ? EMPTY_ITERATOR : children.iterator();
}
@Override
public final boolean isEmpty() {
return children == null || children.isEmpty();
}
@Override
public final O getUserObject() {
return userObject;
}
@Override
public final void setUserObject(O userObject) {
this.userObject = userObject;
}
@Override
public final void addChild(IterableTreeNode> newChild) {
if (newChild != null && newChild.getParent() == this) {
insertChild(newChild, getChildCount() - 1);
} else {
insertChild(newChild, getChildCount());
}
}
@Override
public final void insertChild(IterableTreeNode> newChild, int position) {
Objects.requireNonNull(newChild);
if (!allowsChildren) {
throw new IllegalStateException("node does not allow children");
} else if (isNodeAncestor(newChild)) {
throw new IllegalArgumentException("new child is an ancestor");
}
IterableTreeNode> oldParent = newChild.getParent();
if (oldParent != null) {
oldParent.removeChild(newChild);
}
newChild.setParent(this);
getNotNullChildren().add(position, newChild);
}
@Override
public final void removeChild(IterableTreeNode> child) {
boolean remove = getNotNullChildren().remove(Objects.requireNonNull(child));
if (!remove) {
throw new IllegalArgumentException("argument is not a child");
}
child.setParent(null);
}
@Override
public final void removeChild(int index) {
IterableTreeNode> childAt = getChildAt(index);
removeChild(childAt);
}
@Override
public void removeChildren() {
if (isEmpty()) {
return;
}
children.forEach(c -> c.setParent(null));
children.clear();
}
@Override
public void removeChildren(List> children) {
children.forEach(this::removeChild);
}
/**
* Returns true if anotherNode
is an ancestor of this node
* -- if it is this node, this node's parent, or an ancestor of this
* node's parent. (Note that a node is considered an ancestor of itself.)
* If anotherNode
is null, this method returns false. This
* operation is at worst O(h) where h is the distance from the root to
* this node.
*
* @param anotherNode node to test as an ancestor of this node
* @return true if this node is a descendant of anotherNode
*/
public final boolean isNodeAncestor(IterableTreeNode anotherNode) {
if (anotherNode == null) {
return false;
}
TreeNode ancestor = this;
do {
if (ancestor == anotherNode) {
return true;
}
} while ((ancestor = ancestor.getParent()) != null);
return false;
}
private List> getNotNullChildren() {
if (children == null) {
children = new LinkedList<>();
}
return children;
}
}