/*
* $Id$
* This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc
*
* Copyright (c) 2000-2012 Stephane GALLAND.
* Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
* Universite de Technologie de Belfort-Montbeliard.
* Copyright (c) 2013-2016 The original authors, and other authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.arakhne.afc.math.tree.node;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Collection;
import java.util.List;
import org.eclipse.xtext.xbase.lib.Pure;
import org.arakhne.afc.math.MathUtil;
import org.arakhne.afc.math.tree.TreeNode;
/**
* This is the generic implementation of a ternary
* tree.
*
* <p><h3>moveTo</h3>
* According to its definition in
* {@link TreeNode#moveTo(TreeNode, int)}, the binary
* tree node implementation of <code>moveTo</code>
* replaces any existing node at the position given as
* parameter of <code>moveTo</code>..
*
* @param <D> is the type of the data inside the tree
* @param <N> is the type of the tree nodes.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
public abstract class TernaryTreeNode<D, N extends TernaryTreeNode<D, N>> extends AbstractTreeNode<D, N> {
private static final long serialVersionUID = -5699134081962229144L;
private N left;
private N middle;
private N right;
/**
* Empty node.
*/
public TernaryTreeNode() {
this(DEFAULT_LINK_LIST_USE);
}
/** Construct a node.
*
* @param data is the initial user data
*/
public TernaryTreeNode(Collection<D> data) {
super(DEFAULT_LINK_LIST_USE, data);
this.left = null;
this.middle = null;
this.right = null;
}
/** Construct a node.
*
* @param data is the initial user data
*/
public TernaryTreeNode(D data) {
this(DEFAULT_LINK_LIST_USE, data);
}
/**
* @param useLinkedList indicates if a linked list must be used to store the data.
* If <code>false</code>, an ArrayList will be used.
*/
public TernaryTreeNode(boolean useLinkedList) {
super(useLinkedList);
this.left = null;
this.middle = null;
this.right = null;
}
/**
* @param useLinkedList indicates if a linked list must be used to store the data.
* If <code>false</code>, an ArrayList will be used.
* @param copyDataCollection indicates if the given data collection is copied
* if <code>true</code> or the inner data collection will be the given
* collection itself if <code>false</code>.
* @param data is the initial user data
*/
public TernaryTreeNode(boolean useLinkedList, boolean copyDataCollection, List<D> data) {
super(useLinkedList, copyDataCollection, data);
this.left = null;
this.middle = null;
this.right = null;
}
/**
* @param useLinkedList indicates if a linked list must be used to store the data.
* If <code>false</code>, an ArrayList will be used.
* @param data is the initial user data
*/
public TernaryTreeNode(boolean useLinkedList, D data) {
super(useLinkedList, data);
this.left = null;
this.middle = null;
this.right = null;
}
@Pure
@Override
public Class<? extends Enum<?>> getPartitionEnumeration() {
return null;
}
/** Invoked when this object must be deserialized.
*
* @param in is the input stream.
* @throws IOException in case of input stream access error.
* @throws ClassNotFoundException if some class was not found.
*/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
final N me = toN();
if (this.left != null) {
this.left.setParentNodeReference(me, false);
}
if (this.middle != null) {
this.middle.setParentNodeReference(me, false);
}
if (this.right != null) {
this.right.setParentNodeReference(me, false);
}
}
/** Clear the tree.
*
* <p>Caution: this method also destroyes the
* links between the child nodes inside the tree.
* If you want to unlink the first-level
* child node with
* this node but leave the rest of the tree
* unchanged, please call <code>setChildAt(i,null)</code>.
*/
@Override
public void clear() {
if (this.left != null) {
final N child = this.left;
setLeftChild(null);
child.clear();
}
if (this.middle != null) {
final N child = this.middle;
setMiddleChild(null);
child.clear();
}
if (this.right != null) {
final N child = this.right;
setRightChild(null);
child.clear();
}
removeAllUserData();
}
@Pure
@Override
public final int getChildCount() {
return 3;
}
@Pure
@Override
public int getNotNullChildCount() {
return this.notNullChildCount;
}
@Pure
@Override
public final N getChildAt(int index) throws IndexOutOfBoundsException {
switch (index) {
case 0:
return this.left;
case 1:
return this.middle;
case 2:
return this.right;
default:
throw new IndexOutOfBoundsException();
}
}
/** Set the left child of this node.
*
* @param newChild the new child.
* @return <code>true</code> on success, otherwise <code>false</code>
*/
public boolean setLeftChild(N newChild) {
final N oldChild = this.left;
if (oldChild == newChild) {
return false;
}
if (oldChild != null) {
oldChild.setParentNodeReference(null, true);
--this.notNullChildCount;
firePropertyChildRemoved(0, oldChild);
}
if (newChild != null) {
final N oldParent = newChild.getParentNode();
if (oldParent != this) {
newChild.removeFromParent();
}
}
this.left = newChild;
if (newChild != null) {
newChild.setParentNodeReference(toN(), true);
++this.notNullChildCount;
firePropertyChildAdded(0, newChild);
}
return true;
}
/** Set the left child of this node.
*
* @return the left child.
*/
@Pure
public final N getLeftChild() {
return this.left;
}
/** Set the middle child of this node.
*
* @param newChild the new child.
* @return <code>true</code> on success, otherwise <code>false</code>
*/
public boolean setMiddleChild(N newChild) {
final N oldChild = this.middle;
if (oldChild == newChild) {
return false;
}
if (oldChild != null) {
oldChild.setParentNodeReference(null, true);
--this.notNullChildCount;
firePropertyChildRemoved(1, oldChild);
}
if (newChild != null) {
final N oldParent = newChild.getParentNode();
if (oldParent != this) {
newChild.removeFromParent();
}
}
this.middle = newChild;
if (newChild != null) {
newChild.setParentNodeReference(toN(), true);
++this.notNullChildCount;
firePropertyChildAdded(1, newChild);
}
return true;
}
/** Set the middle child of this node.
*
* @return the middle child.
*/
@Pure
public final N getMiddleChild() {
return this.middle;
}
/** Set the right child of this node.
*
* @param newChild the new child.
* @return <code>true</code> on success, otherwise <code>false</code>
*/
public boolean setRightChild(N newChild) {
final N oldChild = this.right;
if (oldChild == newChild) {
return false;
}
if (oldChild != null) {
oldChild.setParentNodeReference(null, true);
--this.notNullChildCount;
firePropertyChildRemoved(2, oldChild);
}
if (newChild != null) {
final N oldParent = newChild.getParentNode();
if (oldParent != this) {
newChild.removeFromParent();
}
}
this.right = newChild;
if (newChild != null) {
newChild.setParentNodeReference(toN(), true);
++this.notNullChildCount;
firePropertyChildAdded(2, newChild);
}
return true;
}
/** Set the right child of this node.
*
* @return the right child.
*/
@Pure
public final N getRightChild() {
return this.right;
}
@Pure
@Override
public final boolean isLeaf() {
return this.left == null && this.right == null && this.middle == null;
}
@Override
public boolean moveTo(N newParent, int index) {
return moveTo(newParent, index, false);
}
@Override
public final boolean setChildAt(int index, N newChild) throws IndexOutOfBoundsException {
switch (index) {
case 0:
return setLeftChild(newChild);
case 1:
return setMiddleChild(newChild);
case 2:
return setRightChild(newChild);
default:
throw new IndexOutOfBoundsException();
}
}
@Override
protected void setChildAtWithoutEventFiring(int index, N newChild) throws IndexOutOfBoundsException {
switch (index) {
case 0:
if (this.left != null) {
--this.notNullChildCount;
}
this.left = newChild;
if (this.left != null) {
++this.notNullChildCount;
}
break;
case 1:
if (this.middle != null) {
--this.notNullChildCount;
}
this.middle = newChild;
if (this.middle != null) {
++this.notNullChildCount;
}
break;
case 2:
if (this.right != null) {
--this.notNullChildCount;
}
this.right = newChild;
if (this.right != null) {
++this.notNullChildCount;
}
break;
default:
throw new IndexOutOfBoundsException();
}
}
@Override
public boolean removeChild(N child) {
if (child != null) {
if (child == this.left) {
return setLeftChild(null);
} else if (child == this.right) {
return setRightChild(null);
} else if (child == this.middle) {
return setMiddleChild(null);
}
}
return false;
}
@Pure
@Override
public final int indexOf(N child) {
if (child == this.left) {
return 0;
}
if (child == this.middle) {
return 1;
}
if (child == this.right) {
return 2;
}
return -1;
}
/** Returns true if the specified node is effectively a child of this node, false otherwise.
*
* @param potentialChild - the node to test
* @return a boolean, true if the specified node is effectively a child of this node, false otherwise
*/
@Pure
public boolean hasChild(N potentialChild) {
if ((this.left == potentialChild) || (this.middle == potentialChild) || (this.right == potentialChild)) {
return true;
}
return false;
}
@Override
public void getChildren(Object[] array) {
if (array != null) {
if (array.length > 0) {
array[0] = this.left;
}
if (array.length > 1) {
array[1] = this.middle;
}
if (array.length > 2) {
array[2] = this.right;
}
}
}
@Pure
@Override
public int getMinHeight() {
return 1 + MathUtil.min(
this.left != null ? this.left.getMinHeight() : 0,
this.middle != null ? this.middle.getMinHeight() : 0,
this.right != null ? this.right.getMinHeight() : 0);
}
@Pure
@Override
public int getMaxHeight() {
return 1 + MathUtil.max(
this.left != null ? this.left.getMaxHeight() : 0,
this.middle != null ? this.middle.getMaxHeight() : 0,
this.right != null ? this.right.getMaxHeight() : 0);
}
/** Replies the heights of all the leaf nodes.
* The order of the heights is given by a depth-first iteration.
*
* @param currentHeight is the current height of this node.
* @param heights is the list of heights to fill
*/
@Override
protected void getHeights(int currentHeight, List<Integer> heights) {
if (isLeaf()) {
heights.add(new Integer(currentHeight));
} else {
if (this.left != null) {
this.left.getHeights(currentHeight + 1, heights);
}
if (this.middle != null) {
this.middle.getHeights(currentHeight + 1, heights);
}
if (this.right != null) {
this.right.getHeights(currentHeight + 1, heights);
}
}
}
/**
* This is the generic implementation of a ternary
* tree.
*
* @param <D> is the type of the data inside the tree
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
public static class DefaultTernaryTreeNode<D> extends TernaryTreeNode<D, DefaultTernaryTreeNode<D>> {
private static final long serialVersionUID = 8673470473666658484L;
/**
* Empty node.
*/
public DefaultTernaryTreeNode() {
super();
}
/**
* @param data are the initial user data.
*/
public DefaultTernaryTreeNode(Collection<D> data) {
super(data);
}
/**
* @param data are the initial user data.
*/
public DefaultTernaryTreeNode(D data) {
super(data);
}
}
}