/*
* Copyright (C) 2010 eXo Platform SAS.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.portal.tree.list;
import java.util.ListIterator;
import java.util.NoSuchElementException;
/**
* <p>
* A tree structure where the children in which the children are organized as a linked list. The children of a tree is a linked
* list and can be iterated with an iterator or thanks to the {@link #getFirst()}, {@link #getLast()}, {@link #getNext()} and
* {@link #getPrevious()} methods.
* </p>
*
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
* @version $Revision$
* @param <T> the self bounding tree type
*/
public class ListTree<T extends ListTree<T>> {
/** . */
private T parent;
/** . */
private T next;
/** . */
private T previous;
/** . */
private T head;
/** . */
private T tail;
/** . */
private int size;
public ListTree() {
this.next = null;
this.previous = null;
this.head = null;
this.tail = null;
this.size = 0;
}
/**
* Returns the depth.
*
* @return the depth
*/
public final int getDepth() {
int depth = 0;
for (T current = parent; current != null; current = ((ListTree<T>) current).parent) {
depth++;
}
return depth;
}
/**
* Returns the tree.
*
* @return the tree
*/
public final T getNext() {
return next;
}
/**
* Returns the previous.
*
* @return the previous
*/
public final T getPrevious() {
return previous;
}
/**
* Returns the parent.
*
* @return the parent
*/
public final T getParent() {
return parent;
}
/**
* Returns the size.
*
* @return the size
*/
public final int getSize() {
return size;
}
/**
* Returns the first tree.
*
* @return the first tree
*/
public final T getFirst() {
return head;
}
/**
* Returns the last tree.
*
* @return the last tree
*/
public final T getLast() {
return tail;
}
/**
* Returns a tree specified by its index.
*
* @param index the index
* @return the corresponding tree
* @throws IndexOutOfBoundsException if the index is incorrect
*/
public final T get(int index) throws IndexOutOfBoundsException {
if (index < 0) {
throw new IndexOutOfBoundsException("No negative index allowed");
}
//
T current = head;
while (true) {
if (current == null) {
throw new IndexOutOfBoundsException("index " + index + " is greater than the children size");
}
if (index == 0) {
break;
} else {
current = ((ListTree<T>) current).next;
index--;
}
}
return current;
}
/**
* Insert the specified tree.
*
* @param index the index
* @param tree the tree
* @throws NullPointerException if the context is null
* @throws IllegalArgumentException if an existing child with the same name already exist
* @throws IndexOutOfBoundsException if the index is negative or is greater than the children size
*/
public final void insertAt(Integer index, T tree) throws NullPointerException, IllegalArgumentException,
IndexOutOfBoundsException {
if (tree == null) {
throw new NullPointerException("No null tree accepted");
}
if (index != null && index < 0) {
throw new IndexOutOfBoundsException("No negative index permitted");
}
//
if (index != null) {
T a = head;
if (index == 0) {
insertFirst(tree);
} else {
while (index > 0) {
if (a == null) {
throw new IndexOutOfBoundsException();
}
index--;
a = ((ListTree<T>) a).next;
}
//
if (a == null) {
insertLast(tree);
} else if (a != tree) {
a.insertBefore(tree);
}
}
} else {
T a = tail;
if (a == null) {
insertFirst(tree);
} else if (a != tree) {
a.insertAfter(tree);
}
}
}
/**
* Insert the specified context at the last position among the children of this context.
*
* @param tree the content to insert
* @throws NullPointerException if the tree argument is null
*/
public final void insertLast(T tree) {
if (tail == null) {
insertFirst(tree);
} else {
tail.insertAfter(tree);
}
}
/**
* Insert the specified context at the first position among the children of this context.
*
* @param tree the content to insert
* @throws NullPointerException if the tree argument is null
*/
public void insertFirst(T tree) throws NullPointerException {
if (tree == null) {
throw new NullPointerException();
}
if (head == null) {
beforeInsert(tree);
if (((ListTree<T>) tree).parent != null) {
tree.remove();
}
head = tail = tree;
((ListTree<T>) tree).parent = (T) this;
size++;
afterInsert(tree);
} else {
head.insertBefore(tree);
}
}
/**
* Insert the specified tree after this tree
*
* @param tree the tree to insert after
* @throws NullPointerException if the specified tree argument is null
* @throws IllegalStateException if this tree does not have a parent
*/
public final void insertAfter(T tree) {
if (tree == null) {
throw new NullPointerException("No null tree argument accepted");
}
if (parent == null) {
throw new IllegalStateException();
}
if (this != tree) {
parent.beforeInsert(tree);
if (((ListTree<T>) tree).parent != null) {
tree.remove();
}
((ListTree<T>) tree).previous = (T) this;
((ListTree<T>) tree).next = next;
if (next == null) {
((ListTree<T>) parent).tail = tree;
} else {
((ListTree<T>) next).previous = tree;
}
next = tree;
((ListTree<T>) tree).parent = parent;
((ListTree<T>) parent).size++;
parent.afterInsert(tree);
}
}
/**
* Insert the specified tree before this tree
*
* @param tree the tree to insert before
* @throws NullPointerException if the specified tree argument is null
* @throws IllegalStateException if this tree does not have a parent
*/
public final void insertBefore(T tree) throws NullPointerException, IllegalStateException {
if (tree == null) {
throw new NullPointerException("No null tree argument accepted");
}
if (parent == null) {
throw new IllegalStateException();
}
if (this != tree) {
parent.beforeInsert(tree);
if (((ListTree<T>) tree).parent != null) {
tree.remove();
}
((ListTree<T>) tree).previous = previous;
((ListTree<T>) tree).next = (T) this;
if (previous == null) {
((ListTree<T>) parent).head = tree;
} else {
((ListTree<T>) previous).next = tree;
}
previous = tree;
((ListTree<T>) tree).parent = parent;
((ListTree<T>) parent).size++;
parent.afterInsert(tree);
}
}
/**
* Removes this tree from its parent
*
* @throws IllegalStateException if this tree does not have a parent
*/
public final void remove() throws IllegalStateException {
if (parent == null) {
throw new IllegalStateException();
}
parent.beforeRemove((T) this);
if (previous == null) {
((ListTree<T>) parent).head = next;
} else {
((ListTree<T>) previous).next = next;
}
if (next == null) {
((ListTree<T>) parent).tail = previous;
} else {
((ListTree<T>) next).previous = previous;
}
T _parent = parent;
parent = null;
previous = null;
next = null;
((ListTree<T>) _parent).size--;
_parent.afterRemove((T) this);
}
public final ListIterator<T> listIterator() {
/*
* if (map == null) { return null; }
*/
return new ListIterator<T>() {
T next = head;
T current = null;
T previous = null;
int index = 0;
public boolean hasNext() {
return next != null;
}
public T next() {
if (next != null) {
current = next;
//
previous = next;
next = ((ListTree<T>) next).next;
index++;
return current;
} else {
throw new NoSuchElementException();
}
}
public boolean hasPrevious() {
return previous != null;
}
public T previous() {
if (previous != null) {
current = previous;
//
next = previous;
previous = ((ListTree<T>) previous).previous;
index--;
return current;
} else {
throw new NoSuchElementException();
}
}
public int nextIndex() {
return index;
}
public int previousIndex() {
return index - 1;
}
public void remove() {
if (current == null) {
throw new IllegalStateException("no element to remove");
}
if (current == previous) {
index--;
}
next = ((ListTree<T>) current).next;
previous = ((ListTree<T>) current).previous;
current.remove();
current = null;
}
public void set(T tree) {
throw new UnsupportedOperationException();
}
public void add(T tree) {
if (previous == null) {
insertFirst(tree);
} else {
previous.insertAfter(tree);
}
index++;
previous = tree;
next = ((ListTree<T>) tree).next;
}
};
}
/**
* Callback to signal insertion occured.
*
* @param tree the child inserted
*/
protected void beforeInsert(T tree) {
}
/**
* Callback to signal insertion occured.
*
* @param tree the child inserted
*/
protected void afterInsert(T tree) {
}
/**
* Callback to signal insertion occured.
*
* @param tree the child inserted
*/
protected void beforeRemove(T tree) {
}
/**
* Callback to signal insertion occured.
*
* @param tree the child inserted
*/
protected void afterRemove(T tree) {
}
}