org.nuiton.jaxx.runtime.swing.tree.IterableTreeNode 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.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.Stack;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* A {@link TreeNode} implementation that is mutable, but does not implements {@link javax.swing.tree.MutableTreeNode},
* because this API has some lacks:
*
* - Implementation use {@link java.util.Vector}
* - Methods around userObject are not typed
*
* Moreover this class implements {@link Iterable} to get nice access to children nodes using modern API. In that way
* we can perform nice and efficient traversal algorithms using exclusively {@link Stream} API.
*
* Created by tchemit on 07/01/2018.
*
* @author Tony Chemit - [email protected]
*/
public interface IterableTreeNode extends TreeNode, Iterable> {
Iterator> EMPTY_ITERATOR = Collections.emptyIterator();
int STREAM_CHARACTERISTICS = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL | Spliterator.IMMUTABLE;
static Stream> stream(Iterator> iterator) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, STREAM_CHARACTERISTICS), false);
}
default Stream> stream() {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator(), STREAM_CHARACTERISTICS), false);
}
boolean isRoot();
IterableTreeNode> getRoot();
IterableTreeNode> getParent();
void setParent(IterableTreeNode> parent);
void setAllowsChildren(boolean allowsChildren);
boolean isEmpty();
O getUserObject();
void setUserObject(O userObject);
void addChild(IterableTreeNode> newChild);
void insertChild(IterableTreeNode> newChild, int position);
/**
* Remove child at given {@code index}.
*
* Will throw exception if no child at index.
*
* @param index position of child to remove
*/
void removeChild(int index);
/**
* Remove given given {@code child}.
*
* Will throw exception if given child is not a child of this node.
*
* @param child child to remove
*/
void removeChild(IterableTreeNode> child);
/**
* Removes all of this node's children, setting their parents to null.
* If this node has no children, this method does nothing.
*/
void removeChildren();
/**
* Remove given children. Acts as method {@link #removeChild(IterableTreeNode)} for each element.
*
* @param children children to remove
*/
void removeChildren(List> children);
default Iterator> preorderIterator() {
return isEmpty() ? EMPTY_ITERATOR : new PreorderIterator(this);
}
default Iterator> postorderIterator() {
return isEmpty() ? EMPTY_ITERATOR : new PostorderIterator(this);
}
default Iterator> breadthFirstIterator() {
return isEmpty() ? EMPTY_ITERATOR : new BreadthFirstIterator(this);
}
final class PreorderIterator implements Iterator> {
private final Stack stack = new Stack<>();
public PreorderIterator(IterableTreeNode> rootNode) {
stack.push(Collections.singleton(rootNode).iterator());
}
@Override
public boolean hasNext() {
return !stack.empty() && stack.peek().hasNext();
}
@Override
public IterableTreeNode next() {
Iterator currentIterator = stack.peek();
IterableTreeNode node = (IterableTreeNode) currentIterator.next();
if (!currentIterator.hasNext()) {
stack.pop();
}
Iterator children = node.iterator();
if (children.hasNext()) {
stack.push(children);
}
return node;
}
}
final class PostorderIterator implements Iterator> {
private IterableTreeNode> root;
private Iterator> children;
private Iterator> subtree;
public PostorderIterator(IterableTreeNode> rootNode) {
Objects.requireNonNull(rootNode);
root = rootNode;
children = root.iterator();
subtree = EMPTY_ITERATOR;
}
@Override
public boolean hasNext() {
return root != null;
}
@Override
public IterableTreeNode> next() {
IterableTreeNode> returnValue;
if (subtree.hasNext()) {
returnValue = subtree.next();
} else if (children.hasNext()) {
subtree = new PostorderIterator(children.next());
returnValue = subtree.next();
} else {
returnValue = root;
root = null;
}
return returnValue;
}
}
final class BreadthFirstIterator implements Iterator> {
private final BreadthFirstIterator.Queue queue;
public BreadthFirstIterator(IterableTreeNode> rootNode) {
Objects.requireNonNull(rootNode);
queue = new BreadthFirstIterator.Queue();
Set> singleton = Collections.singleton(rootNode);
queue.enqueue(singleton.iterator());
}
@Override
public boolean hasNext() {
return (!queue.isEmpty() && queue.peek().hasNext());
}
@Override
public IterableTreeNode> next() {
Iterator> currentIterator = queue.peek();
IterableTreeNode> node = currentIterator.next();
if (!currentIterator.hasNext()) {
queue.pop();
}
if (!node.isEmpty()) {
queue.enqueue(node.iterator());
}
return node;
}
// A simple queue with a linked list data structure.
public final class Queue {
QNode head; // null if empty
QNode tail;
public void enqueue(Iterator> anObject) {
if (head == null) {
head = tail = new QNode(anObject, null);
} else {
tail.next = new QNode(anObject, null);
tail = tail.next;
}
}
public void pop() {
if (head == null) {
throw new NoSuchElementException("No more elements");
}
QNode oldHead = head;
head = head.next;
if (head == null) {
tail = null;
} else {
oldHead.next = null;
}
}
public Iterator> peek() {
if (head == null) {
throw new NoSuchElementException("No more elements");
}
return head.object;
}
public boolean isEmpty() {
return head == null;
}
final class QNode {
public Iterator> object;
public QNode next; // null if end
public QNode(Iterator> object, QNode next) {
this.object = object;
this.next = next;
}
}
}
}
}