Package com.sun.sgs.app.util

Source Code of com.sun.sgs.app.util.ScalableList$SearchResult

/*
* Copyright 2007-2009 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the LICENSE file that accompanied
* this code.
*/

package com.sun.sgs.app.util;

import java.io.Serializable;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.DataManager;
import com.sun.sgs.app.ManagedObject;
import com.sun.sgs.app.ManagedObjectRemoval;
import com.sun.sgs.app.ManagedReference;
import com.sun.sgs.app.ObjectNotFoundException;
import com.sun.sgs.app.Task;

/**
* This class represents a {@code java.util.List} which supports a concurrent
* and scalable behavior. In particular, this List allows for arbitrary
* insertions and removals, arbitrary searches for values, and a number of
* other methods which are also defined in the {@code java.util.List}
* interface. This data structure builds upon the {@code AbstractList} class
* by implementing methods specific to concurrent and scalable operations. As
* an implementation decision, this data structure does not support
* {@code null} insertions but does implement all optional operations defined
* in the {@code java.util.List} interface.
* <p>
* Those who are interested in using a simple data structure that is not
* intended to grow very large, contains simple elements, but offers decent
* concurrency should use the
* {@code java.util.concurrent.CopyOnWriteArrayList<E>} type. However, if
* lists are intended to be very large and cloning the list is infeasible due
* to its size or the complexity of its contents, then the
* {@code ScalableList} is a better tool as it does not require cloning and it
* scales reasonably well.
* <p>
* This class actually stores a collection of {@code ManagedReference}s,
* which in turn point to {@code ManagedObject}s. Therefore, each element
* stored is converted into a {@code ManagedObject} in the form of an
* {@code Element} wrapper if it is not one already, and a
* {@code ManagedReference} is created to reference it. The wrapper is
* detected during an element removal: if the element is an instance of the
* wrapper {@code Element} class, then it is removed from the data manager.
* Conversely, if the element had no wrapper (but by definition is still a
* {@code ManagedObject}), then it remains within the data manager until the
* user explicitly removes it.
* <p>
* The class achieves scalability and concurrency by partitioning an ordinary
* list into a number of smaller lists contained in {@code ListNode} objects,
* and joining the nodes in a tree format. This implementation bears
* similarity to a skip-list in that access to arbitrary elements occurs
* through initially large jumps, and then through a finer iteration of the
* contained list. To allow for this behaviour, each {@code ListNode} holds a
* subset of the contents so that changes in the entries need not propagate to
* all elements at once. In fact, each {@code ListNode} only holds onto the
* size of its {@code SubList} (its children) and not a cumulative total of
* all its previous siblings. This enables intermediate changes to have no
* effect on neighbouring nodes, such as re-indexing.
* <p>
* The {@code branchingFactor} is a user-defined parameter which describes how
* the underlying tree is organized. A large {@code branchingFactor} means
* that each node in the tree contains a large number of children, providing
* for a shallower tree, but many more sibling traversals. Concurrency is
* somewhat compromised since parent nodes containing a large number of
* children are locked during modification. A smaller branching factor reduces
* the sibling traversals, but makes the tree deeper, somewhat affecting
* performance during split operations. Depending on the use of the list, it
* may be desirable to have a large {@code branchingFactor}, such as for
* improved scalability, or a smaller {@code branchingFactor}, such as for
* better concurrency.
* <p>
* Performing splits and removing unused nodes can be somewhat expensive,
* depending on the values set for the {@code branchingFactor} and
* {@code bucketSize}. This is because changes that occur at the leaf level
* need to propagate to the parents; for a deep tree, this can affect a number
* of different nodes. However, it is seen that the benefits provided by the
* partitioning of the list that enable concurrency outweigh the performance
* hit for these operations.
* <p>
* As mentioned, {@code ListNode}s contain a subset of the total number of
* elements in the {@code ScalableList}. When an iterator is referencing an
* element, the iterator is not affected by changes that occur in prior
* {@code ListNode}s. However, if modifications happen after the current
* {@code ListNode} being examined, they will be incorporated in the
* iteration, suggesting that the target element may not be the same one as
* when the call was initially made. However, if a modification in the form of
* an addition or removal happens on the same {@code ListNode}, then the
* iterator will throw a {@code ConcurrentModificationException} as a result
* of a compromise to the integrity of the node. This exception is not thrown
* if an element is replaced using the {@code set()} method because there
* would be no change in index to prompt the exception.
* <p>
* Since the {@code ScalableList} type is a {@code ManagedObject}, it is
* important to note that applications which instantiate it should be
* responsible for removing it from the data manager. This can be done by
* statically obtaining the {@code DataManager} through the {@code AppContext}
* via the call
* {@code AppContext.getDataManager().removeObject(ManagedObject)}.
* <p>
* Contrary to the {@code ScalableList}, iterators both within and provided
* by the {@code ScalableList} type are not {@code ManagedObject}s.
* Therefore, they are not stored within the data manager and do not need to
* be removed using the {@code DataManager}.
* <p>
* Since the list is capable of containing many elements, applications which
* use iterators to traverse elements should be aware that prolonged iterative
* tasks have the potential to lock out other concurrent tasks on the list.
* Therefore, it is highly recommended (and considered good practice) that
* potentially long iterations be broken up into smaller tasks. This strategy
* improves the concurrency of the {@code ScalableList} as it reduces the
* locked elements owned by any one task.
* <p>
*
* @param <E> the type of the elements stored in the {@code ScalableList}
*/
public class ScalableList<E> extends AbstractList<E> implements Serializable,
  ManagedObjectRemoval {

    private static final long serialVersionUID = 1L;

    /**
     * Default value for the {@code branchingFactor} if one is not explicitly
     * set.
     */
    public static final int DEFAULT_BRANCHING_FACTOR = 5;

    /**
     * Default value for the {@code bucketSize} if one is not explicitly set.
     */
    public static final int DEFAULT_BUCKET_SIZE = 10;

    /**
     * The top node in the tree
     */
    private ManagedReference<TreeNode<E>> root;

    /**
     * A reference to the head node of the list.
     */
    private ManagedReference<DummyConnector<E>> headRef;
    /**
     * A reference to the tail of the list. This makes appending to the list a
     * constant-time operation.
     */
    private ManagedReference<DummyConnector<E>> tailRef;

    /**
     * If non-null, a runnable to call when a task that asynchronously removes
     * nodes is done -- used for testing. Note that this method is called
     * during the transaction that completes the removal.
     */
    private static volatile Runnable noteDoneRemoving = null;

    /**
     * The maximum number of elements a {@code ListNode} can contain. This
     * number should be small enough to enable concurrency but large enough to
     * contain a reasonable number of nodes. If it is not explicitly set, it
     * defaults to a value of 10.
     */
    private int bucketSize = DEFAULT_BUCKET_SIZE;

    /**
     * The maximum number of children contained in a TreeNode<E>; this
     * parameter is passed to the TreeNode<E> during instantiation. If it is
     * not explicitly set, it defaults to a value of 5.
     */
    private int branchingFactor = DEFAULT_BRANCHING_FACTOR;

    /*
     * IMPLEMENTATION
     */

    /**
     * Constructor which creates a {@code ScalableList} object with default
     * values for the {@code bucketSize} and {@code branchingFactor}.
     */
    public ScalableList() {
  TreeNode<E> t = new TreeNode<E>(this, null, false);
  headRef =
    AppContext.getDataManager().createReference(
      new DummyConnector<E>(t.getChild()));
  tailRef =
    AppContext.getDataManager().createReference(
      new DummyConnector<E>(t.getChild()));
  setRoot(t);
    }

    /**
     * Constructor which creates a {@code ScalableList} object with the
     * {@code branchingFactor} and {@code bucketSize} supplied as a parameter.
     * The {@code bucketSize} can be any integer larger than 0, however the
     * {@code branchingFactor} must be larger than 1 so that the tree can be
     * meaningful. Otherwise, it would only be able to grow to a maximum size
     * of {@code bucketSize} since branching could not introduce any
     * additional children.
     *
     * @param branchingFactor the number of children each node should have. A
     * {@code branchingFactor} of 2 means that the tree structure is binary.
     * @param bucketSize the size of each partitioned list. This value must be
     * a positive integer (larger than 0).
     * @throws IllegalArgumentException when the arguments are too small
     */
    public ScalableList(int branchingFactor, int bucketSize) {
  isLegal(branchingFactor, bucketSize);
  this.bucketSize = bucketSize;
  this.branchingFactor = branchingFactor;
  TreeNode<E> t = new TreeNode<E>(this, null, false);
  headRef =
    AppContext.getDataManager().createReference(
      new DummyConnector<E>(t.getChild()));
  tailRef =
    AppContext.getDataManager().createReference(
      new DummyConnector<E>(t.getChild()));
  setRoot(t);
    }

    /**
     * Constructor which creates a {@code ScalableList} object with the
     * {@code branchingFactor}, {@code bucketSize}, and a {@code collection}
     * supplied as parameters. The {@code bucketSize} can be any integer
     * larger than 0, however the {@code branchingFactor} must be larger than
     * 1 so that the tree can be meaningful. Otherwise, it would only be able
     * to grow to a maximum size of {@code bucketSize} since branching could
     * not introduce any additional children. The {@code collection}
     * represents a {@code Collection} of elements which will be added to the
     * newly formed {@code ScalableList}.
     *
     * @param branchingFactor the number of children each node should have. A
     * {@code branchingFactor} of 2 means that the tree structure is binary.
     * @param bucketSize the size of each partitioned list. This value must be
     * a positive integer (larger than 0).
     * @param collection a collection of objects to initially populate the
     * {@code ScalableList}
     * @throws IllegalArgumentException if the collection is invalid (contains
     * {@code null} elements), or if the {@code branchingFactor} or
     * {@code bucketSize} are not within their respective ranges
     */
    public ScalableList(int branchingFactor, int bucketSize,
      Collection<E> collection) {

  this(branchingFactor, bucketSize);
  this.addAll(collection);
    }

    /**
     * Creates a reference to the supplied argument if it is not {@code null}.
     *
     * @param <T> the type of the object
     * @param t the object to reference
     * @return a {@code ManagedReference} of the object, or {@code null} if
     * the argument is null.
     */
    static <T> ManagedReference<T> createReferenceIfNecessary(T t) {
  if (t == null) {
      return null;
  }
  return AppContext.getDataManager().createReference(t);
    }

    /**
     * Returns the value stored within the {@code ManagedReference},
     * depending on whether it is an {@code Element} or {@code ManagedObject}
     *
     * @param <E> the type of element
     * @param ref the {@code ManagedReference} storing the value
     * @param delete {@code true} if the {@code Element} object should be
     * deleted, and {@code false} otherwise
     * @return the value stored in the reference
     */
    @SuppressWarnings("unchecked")
    static <E> E getValueFromReference(ManagedReference<ManagedObject> ref,
      boolean delete) {
  E obj = (E) ref.get();

  // In case we wrapped the item with an
  // Element object, fetch the value. In here,
  // check if the Element is to be deleted
  if (obj instanceof Element) {
      Element<E> tmp = uncheckedCast(obj);
      if (delete) {
    AppContext.getDataManager().removeObject(obj);
      }
      return tmp.getValue();
  }
  return obj;
    }

    /**
     * Casts the object to the desired type in order to avoid unchecked cast
     * warnings
     *
     * @param <T> the type to cast to
     * @param object the object to cast
     * @return the casted version of the object
     */
    @SuppressWarnings("unchecked")
    private static <T> T uncheckedCast(Object object) {
  return (T) object;
    }

    /**
     * Retrieves the {@code branchingFactor} for the list.
     *
     * @return the {@code branchingFactor}
     */
    int getBranchingFactor() {
  return branchingFactor;
    }

    /**
     * Retrieves the {@code bucketSize} for the {@code ListNode} elements.
     *
     * @return the {@code bucketSize}
     */
    int getBucketSize() {
  return bucketSize;
    }

    /**
     * Ensure that the parameters are valid.
     *
     * @param branchingFactor the branching factor
     * @param bucketSize the maximum number of elements in a {@code ListNode}
     * @throws IllegalArgumentException if one of the values is invalid
     */
    private void isLegal(int branchingFactor, int bucketSize) {
  if (bucketSize < 1) {
      throw new IllegalArgumentException("Cluster size must "
        + "be an integer larger than 0");
  }
  if (branchingFactor < 2) {
      throw new IllegalArgumentException("Max child size must "
        + "be an integer larger than 1");
  }
    }

    /**
     * Appends the specified element to the end of this list. This
     * implementation accepts all elements except for {@code null}.
     *
     * @param e element to add
     * @return this method will always return {@code true}
     */
    public boolean add(E e) {
  if (e == null) {
      throw new NullPointerException("Element cannot be null");
  }

  // add it at the end and propagate change to parents
  getTail().append(e);

  // update the tail in case it has changed.
  ListNode<E> next = getTail().next();
  if (next != null) {
      setTail(next);
  }

  return true;
    }

    /**
     * Inserts the specified element at the specified position in this list.
     * Shifts the element currently at that position (if any) and any
     * subsequent elements to the right (adds one to their indices).
     *
     * @param index the index
     * @param e the element to add
     * @throws IndexOutOfBoundsException if the index is out of bounds
     */
    public void add(int index, E e) {
  isNotNegative(index);

  // Check for the different boundary cases
  if (e == null) {
      throw new NullPointerException("Element cannot be null");

  } else if (getHead().equals(getTail()) && getHead().size() == 0) {
      getTail().append(e);
      return;

  } else if (getHead() == null) {
      throw new IndexOutOfBoundsException("Cannot add to index " +
        index + " on an empty list");
  }

  // otherwise, add it to the specified index.
  // This requires a search of the list nodes.
  SearchResult<E> sr;
  if (index != 0) {
      sr = getNode(--index);
      sr.node.insert(sr.offset + 1, e);
  } else {
      getHead().insert(0, e);
  }

  // update the tail in case it has changed.
  ListNode<E> next = getTail().next();
  if (next != null) {
      setTail(next);
  }
    }

    /**
     * Removes all of the elements referred to in the list, but retains a
     * basic structure that consists of a {@code SubList}, {@code ListNode},
     * and a {@code TreeNode}, along with {@code DummyConnector}s
     * representing the head and tail of the list. The call to
     * {@code removingObject()} is asynchronous, meaning the objects are
     * deleted from the data manager in a scheduled task.
     */
    public void clear() {

  // If the head is null, then the ScalableList is
  // not initialized; therefore, no work to do.
  if (getHead() == null) {
      return;
  }

  // Otherwise, remove the entire object..
  removingObject();

  // ..and then supply a new empty structure
  TreeNode<E> t = new TreeNode<E>(this, null, false);
  headRef =
    AppContext.getDataManager().createReference(
      new DummyConnector<E>(t.getChild()));
  tailRef =
    AppContext.getDataManager().createReference(
      new DummyConnector<E>(t.getChild()));
  setRoot(t);
    }

    /**
     * Returns {@code true} if this list contains the specified element. More
     * formally, returns {@code true} if and only if this list contains at
     * least one element e such that
     * <p>
     * {@code (o==null ? e==null : o.equals(e))}.
     *
     * @param o the object which may be in the list
     * @return whether the object is contained in the list; {@code true} if
     * so, {@code false} otherwise
     */
    public boolean contains(Object o) {
  if (o == null) {
      return false;
  }
  return (indexOf(o) != -1);
    }

    /**
     * Returns the index in this list of the first occurrence of the specified
     * element, or -1 if this list does not contain this element. More
     * formally, returns the lowest index i such that
     * <p>
     * {@code (o==null ? get(i)==null : o.equals(get(i)))},
     * <p>
     * or -1 if there is no such index.
     *
     * @param o the element to search for
     * @return the index in this list of the first occurrence of the specified
     * element, or -1 if this list does not contain this element.
     */
    public int indexOf(Object o) {
  int listIndex = 0;
  ScalableListNodeIterator<E> iter =
    new ScalableListNodeIterator<E>(getHead());

  while (iter.hasNext()) {
      ListNode<E> n = (ListNode<E>) iter.next();
      int index = n.getSubList().indexOf(o);

      if (index != -1) {
    return listIndex + index;
      }
      listIndex += n.size();
  }
  return -1;
    }

    /**
     * Returns the index in this list of the last occurrence of the specified
     * element, or -1 if this list does not contain this element. More
     * formally, returns the highest index i such that (o==null ? get(i)==null :
     * o.equals(get(i))), or -1 if there is no such index.
     *
     * @param obj element to search for
     * @return the index in this list of the last occurrence of the specified
     * element, or -1 if this list does not contain this element.
     */
    public int lastIndexOf(Object obj) {
  int listIndex = 0;
  int absIndex = -1;
  ScalableListNodeIterator<E> iter =
    new ScalableListNodeIterator<E>(getHead());

  // For every ListNode encountered, check for an
  // instance of the supplied object
  while (iter.hasNext()) {
      ListNode<E> n = iter.next();
      int index = n.getSubList().lastIndexOf(obj);

      // Save the most recent occurrence of a matching index
      // but keep searching in case we find another in another
      // node.
      if (index != -1) {
    absIndex = listIndex + index;
      }
      listIndex += n.size();
  }
  return absIndex;
    }

    /**
     * Determines if the provided index is positive (not negative). If the
     * index is negative, then an {@code IndexOutOfBoundsException} is thrown.
     *
     * @param index the index to check for validity
     * @throws IndexOutOfBoundsException if the index is negative
     */
    private void isNotNegative(int index) {
  if (index < 0) {
      throw new IndexOutOfBoundsException("Index " + index +
        " cannot be negative");
  }
    }

    /**
     * Removes the element at the specified position in this list. Shifts any
     * subsequent elements to the left (subtracts one from their indices).
     * Returns the element that was removed from the list.
     *
     * @param index the index of the element to be removed
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException if the index is out of bounds
     */
    public E remove(int index) {
  isNotNegative(index);
  SearchResult<E> sr = getNode(index);
  ListNode<E> n = sr.node;

  return n.remove(this, sr.offset);
    }

    /**
     * This operation preserves the order of the elements in the list and
     * keeps multiple copies of the same elements if they exist.
     *
     * @param c the collection of elements to keep
     * @return {@code true} if this list changed as a result of the call
     */
    public boolean retainAll(Collection<?> c) {
  Iterator<E> iter = new ScalableIterator<E>(this);
  ArrayList<E> newList = new ArrayList<E>();
  boolean changed = false;

  // For each element in the overall list,
  // if it is contained in the supplied
  // collection, then we add it to a
  // new list instance. Once finished, we
  // will add everything from this into
  // an emptied list (this).
  while (iter.hasNext()) {
      E e = iter.next();

      // Add the node to the new collection
      if (c.contains(e)) {
    newList.add(e);
      } else {
    changed = true;
      }
  }

  // Clear the current list and add all
  // elements from the temporary collection.
  clear();
  addAll(newList);
  return changed;
    }

    /**
     * Retrieves the head {@code ListNode}.
     *
     * @return the head {@code ListNode} if it exists
     * @throws ObjectNotFoundException if no reference exists
     */
    ListNode<E> getHead() {
  return headRef.get().getRefAsListNode();
    }

    /**
     * Retrieves the tail {@code ListNode}.
     *
     * @return the tail {@code ListNode} if it exists
     * @throws ObjectNotFoundException if there is no reference
     */
    ListNode<E> getTail() {
  return tailRef.get().getRefAsListNode();
    }

    /**
     * Sets the tail of the {@code ListNode} linked-list
     *
     * @param newTail the tail
     */
    void setTail(ListNode<E> newTail) {
  tailRef.getForUpdate().setRef(newTail);
    }

    /**
     * Sets the head of the {@code ListNode} linked-list
     *
     * @param newHead the head
     */
    void setHead(ListNode<E> newHead) {
  headRef.getForUpdate().setRef(newHead);
    }

    /**
     * Replaces the element at the specified position in this list with the
     * specified element.
     *
     * @param index the index to set
     * @param obj the object to replace the existing value
     * @return the old value which was replaced
     */
    @SuppressWarnings("unchecked")
    public E set(int index, Object obj) {
  if (obj == null) {
      throw new NullPointerException(
        "Value for set operation cannot be null");
  }
  SearchResult<E> sr = getNode(index);
  return sr.node.set(sr.offset, obj);
    }

    /**
     * Returns the element at the specified position in this list.
     *
     * @param index the index to retrieve
     * @return the element at the supplied index
     * @throws IndexOutOfBoundsException if the index is out of bounds
     */
    public E get(int index) {

  // Iterate through using the count. Use a get()
  // iterator because we are not modifying anything; hence, false.
  SearchResult<E> sr = getNode(index);
  SubList<E> sublist = sr.node.getSubList();
  if (sublist == null) {
      return null;
  }
  return sublist.get(sr.offset);
    }

    /**
     * Returns an iterator over the elements in this list in proper sequence.
     *
     * @return an {@code Iterator} over the elements in the list
     */
    public Iterator<E> iterator() {
  return new ScalableIterator<E>(this);
    }

    /**
     * Returns a {@code ListIterator} over the elements in this list in proper
     * sequence.
     *
     * @return a {@code ListIterator} over the elements in the list
     */
    public ListIterator<E> listIterator() {
  return new ScalableListIterator<E>(this);
    }

    /**
     * Returns a {@code ListIterator} over the elements in this list, starting
     * from the element at the given index.
     *
     * @return a {@code ListIterator} over the elements in the list
     * @param index the index
     * @throws IndexOutOfBoundsException if the index is out of bounds:
     * {@code index < 0 || index > size()}
     */
    public ListIterator<E> listIterator(int index) {
  // Special case: if the index provided equals the size,
  // then we must manually supply the starting node because
  // a search would throw an IndexOutOfBoundsException
  if (index == this.size()) {
      ListNode<E> tail = getTail();
      return new ScalableListIterator<E>(this, tail, tail.size());
  }
  return new ScalableListIterator<E>(this, getNode(index));
    }

    /**
     * Removes the first occurrence in this list of the specified element. If
     * this list does not contain the element, it is unchanged. More formally,
     * removes the element with the lowest index i such that
     * <p>
     * {@code (o==null ? get(i)==null : o.equals(get(i)))} (if such an element
     * exists).
     *
     * @param obj element to be removed from the list, if present
     * @return the element previously at the specified position
     */
    public boolean remove(Object obj) {
  ScalableListNodeIterator<E> iter =
    new ScalableListNodeIterator<E>(getHead());
  boolean removed = false;

  // Find and remove the object in the ListNode<E> that contains it
  while (iter.hasNext()) {
      ListNode<E> n = iter.next();
      removed = n.remove(this, obj);
      if (removed) {
    break;
      }
  }
  return removed;
    }

    /**
     * Retrieves the root {@code TreeNode} if it exists or null otherwise.
     *
     * @return the root node, or null if it does not exist
     */
    TreeNode<E> getRoot() {
  return this.root.get();
    }

    /**
     * Obtains the child of the root node, since the root node does not get
     * updated unless a split/removal occurs. For most operations, returning
     * the child is sufficient.
     *
     * @return
     */
    private Node<E> getRootChild() {
  return getRoot().getChild();
    }

    /**
     * Sets the root element of the underlying tree structure. This is
     * necessary during a split or remove.
     *
     * @param newRoot the {@code TreeNode} which is to be the new root
     */
    void setRoot(TreeNode<E> newRoot) {
  AppContext.getDataManager().markForUpdate(this);
  root = createReferenceIfNecessary(newRoot);
    }

    /**
     * Returns {@code true} if this list contains no elements.
     *
     * @return {@code true} if the list is empty, and {@code false} otherwise
     */
    public boolean isEmpty() {
  return (getHead().size() == 0);
    }

    /**
     * Performs the search for the desired {@code ListNode}. This method will
     * locate the {@code ListNode} using a recursive search process and return
     * a {@code SearchResult} which contains both the {@code ListNode} and the
     * numeric offset corresponding to the provided index.
     *
     * @param index the absolute index in the entire list to search for the
     * entry
     * @return the {@code SearchResult} which contains the element at the
     * specified {@code index}
     * @throws IndexOutOfBoundsException if the index is out of bounds
     */
    private SearchResult<E> getNode(int index) {
  if (index < 0) {
      throw new IndexOutOfBoundsException("Index cannot be negative");
  }
  // Recursive method to eventually return ListNode<E>
  // containing the desired index.
  return getRootChild().search(0, index + 1);
    }

    /**
     * Retrieves the size of the list. The size of the list is obtained by
     * performing a traversal of the root's children and adding each of their
     * sizes. For very large lists, this process can be somewhat expensive as
     * it does not occur in constant-time, but rather in a logarithmic time
     * proportional to the {@code branchingFactor}.
     *
     * @return the number of elements in the list
     */
    public int size() {
  // This gives us a reference to the one and only
  // root TreeNode<E>, which parents the entire tree.
  Node<E> current = getRootChild();
  int size = 0;

  // Iterate through the first level of the tree to get
  // the sizes of each node.
  while (current != null) {
      size += current.size();
      current = current.next();
  }
  return size;
    }

    /**
     * {@inheritDoc}
     */
    public void removingObject() {

  AsynchronousClearTask<E> clearTask =
    new AsynchronousClearTask<E>(this);

  // Schedule asynchronous task here
  // which will delete the list
  AppContext.getTaskManager().scheduleTask(clearTask);

  // Remove the dummy connectors
  DataManager dm = AppContext.getDataManager();
  dm.removeObject(headRef.get());
  dm.removeObject(tailRef.get());
    }

    /*
     * INLINE CLASSES
     */

    /**
     * The {@code DummyConnector} class is used as a junction point for two
     * {@code ManagedReference}s so that changes to the second
     * {@code ManagedReference} need not affect the top structure. The class
     * is fairly simple, only containing methods which enable the value to be
     * easily set and retrieved.
     *
     * @param <E> the type of element stored in the {@code ScalableList}
     */
    static class DummyConnector<E> implements ManagedObject, Serializable {

  private static final long serialVersionUID = 3L;

  /**
   * The pointer to the object.
   */
  private ManagedReference<Node<E>> ref = null;

  /**
   * Default no-argument constructor.
   */
  DummyConnector() {
      ref = null;
  }

  /**
   * Constructor which accepts the object
   *
   * @param reference the element which this object is to point to
   */
  DummyConnector(Node<E> reference) {
      if (reference != null) {
    ref = AppContext.getDataManager().createReference(reference);
      }
  }

  /**
   * Sets the reference for this object.
   *
   * @param newRef the intended new reference
   */
  void setRef(Node<E> newRef) {
      AppContext.getDataManager().markForUpdate(this);
      ref = createReferenceIfNecessary(newRef);
  }

  /**
   * Retrieves the reference as a {@code ListNode}.
   *
   * @return the reference as a {@code ListNode}, or null if it does
   * not exist
   */
  ListNode<E> getRefAsListNode() {
      if (ref == null) {
    return null;
      }
      return (ListNode<E>) ref.get();
  }
    }

    /**
     * An object which forms a tree above the {@code ListNode<E>} linked list.
     * Each {@code TreeNode<E>} has a {@code child} reference to either one
     * {@code TreeNode<E>} or a {@code ListNode<E>}. The {@code TreeNode<E>}
     * also has a reference to its next and previous sibling and its parent.
     * <p>
     * The {@code TreeNode<E>} is intended to only track the size of its
     * descendant children and the number of children that it owns.
     *
     * @param <E> the type of element stored in the {@code ScalableList}
     */
    static class TreeNode<E> implements ManagedObject, Serializable, Node<E> {

  // Public fields referenced during propagation
  public static final byte DECREMENT_SIZE = 0;
  public static final byte INCREMENT_SIZE = 1;
  public static final byte INCREMENT_CHILDREN_AND_SIZE = 2;
  public static final byte DECREMENT_CHILDREN_AND_SIZE = 3;

  private static final long serialVersionUID = 1L;

  // References to neighboring elements
  private ManagedReference<TreeNode<E>> nextRef;
  private ManagedReference<TreeNode<E>> prevRef;
  private ManagedReference<Node<E>> childRef;
  private ManagedReference<TreeNode<E>> parentRef;
  private final ManagedReference<ScalableList<E>> owner;

  /**
   * The maximum number of children this node can contain
   */
  private final int branchingFactor;

  /**
   * The maximum number of elements which the underlying
   * {@code ListNode} can store
   */
  private final int bucketSize;

  /**
   * The number of elements that exist as descendants of this node
   */
  private int size = 0;

  /**
   * The number of immediate children
   */
  private int childrenCount = 0;

  /**
   * Constructor which is called on a split. This is used to create a
   * new sibling and accepts a TreeNode argument which represents the
   * new child it is to possess.
   *
   * @param list a reference to the owning {@code ScalableList}
   * @param parent the intended parent
   * @param child the child to be added underneath the new
   * {@code TreeNode}
   * @param numberChildren the total number of children that will exist
   * once the {@code TreeNode} is instantiated. This is based on the
   * nature of the linked list of {@code ListNodes} attached to the
   * {@code child}.
   * @param size the total number of elements that will exist under the
   * {@code TreeNode}, based on the elements that already exist among
   * the {@code child} and its siblings.
   */
  private TreeNode(ScalableList<E> list, TreeNode<E> parent,
    Node<E> child, int numberChildren, int size) {
      this(list);

      // Set up links
      assert (child != null);
      childRef = AppContext.getDataManager().createReference(child);

      // this might be the root so parent could be null
      parentRef = createReferenceIfNecessary(parent);

      childrenCount = numberChildren;
      this.size = size;
  }

  /**
   * Constructor which is called by public constructors.
   *
   * @param list the {@code ScalableList} which is the owner of this
   * structure
   */
  private TreeNode(ScalableList<E> list) {
      owner = AppContext.getDataManager().createReference(list);
      this.branchingFactor = list.getBranchingFactor();
      nextRef = null;
      this.bucketSize = list.getBucketSize();
  }

  /**
   * Create a new TreeNode on account of a new leaf ({@code ListNode})
   * being created.
   *
   * @param list the {@code ScalableList} which is the owner of this
   * structure
   * @param parent the intended parent
   * @param e an element to add into the empty {@code ListNode}
   */
  TreeNode(ScalableList<E> list, TreeNode<E> parent, E e) {
      this(list);

      assert (e != null);

      ListNode<E> n = new ListNode<E>(this, bucketSize, e);
      size = n.size();
      childrenCount = 1;
      DataManager dm = AppContext.getDataManager();
      childRef = dm.createReference((Node<E>) n);
      parentRef = createReferenceIfNecessary(parent);
  }

  /**
   * Constructor which creates a {@code TreeNode} while specifying
   * parameters for the node characteristics
   *
   * @param list the {@code ScalableList} owner of this structure
   * @param parent the intended parent {@code ListNode}
   * @param isSplit {@code true} if the {@code TreeNode} is to be
   * created due to a {@code split} operation, and {@code false}
   * otherwise.
   */
  TreeNode(ScalableList<E> list, TreeNode<E> parent, boolean isSplit) {
      this(list);

      if (!isSplit) {
    ListNode<E> n = new ListNode<E>(this, bucketSize);
    DataManager dm = AppContext.getDataManager();
    size = n.size();
    childRef = dm.createReference((Node<E>) n);
      } else {
    size = 0;
    childRef = null;
      }
      parentRef = createReferenceIfNecessary(parent);
  }

  /**
   * Returns a {@code String} representation of this object.
   *
   * @return a {@code String} representation of this object.
   */
  public String toString() {
      StringBuilder s = new StringBuilder();
      int size = 0;
      int children = getChildCount();
      Node<E> node = getChild();

      // Add information for each child
      for (int i = 0; i < children; i++) {
    if (i > 0) {
        s.append(";");
    }
    s.append(node.toString());
    node = node.next();
    size += node.size();
      }

      // Add list metrics
      s.append("size: ");
      s.append(size);
      s.append(", children: ");
      s.append(children);
      return s.toString();
  }

  /**
   * {@inheritDoc}
   */
  public TreeNode<E> prev() {
      if (prevRef == null) {
    return null;
      }
      return prevRef.get();
  }

  /**
   * {@inheritDoc}
   */
  public void setPrev(Node<E> ref) {
      AppContext.getDataManager().markForUpdate(this);
      TreeNode<E> t = uncheckedCast(ref);
      prevRef = createReferenceIfNecessary(t);
  }

  /**
   * {@inheritDoc}
   */
  public TreeNode<E> next() {
      if (nextRef == null) {
    return null;
      }
      return nextRef.get();
  }

  /**
   * Obtains the child of the current node. The child represents the
   * head of the list of children. The {@code TreeNode} does not have
   * references to all the children, so it uses knowledge of the head to
   * iterate through them.
   *
   * @return the child, which can be either a {@code TreeNode} or
   * {@code ListNode}, depending on the position of the current node in
   * the tree.
   */
  Node<E> getChild() {
      if (childRef == null) {
    return null;
      }
      return childRef.get();
  }

  /**
   * Sets the child to be the supplied parameter as long as it is not
   * null. This method also updates the size of the parent because
   * changing the child suggests that a new size exists.
   *
   * @param child the new child
   */
  void setChild(Node<E> child, int size, int numberOfChildren) {
      AppContext.getDataManager().markForUpdate(this);
      this.size = size;
      this.childrenCount = numberOfChildren;
      childRef = createReferenceIfNecessary(child);
  }

  /**
   * Sets the child to null for the relinking process.
   */
  void setChildToNull() {
      AppContext.getDataManager().markForUpdate(this);
      childRef = null;
  }

  /**
   * {@inheritDoc}
   */
  public int size() {
      return size;
  }

  /**
   * Recursively increments the node's size until reaching the root. The
   * root is not updated to enable some degree of concurrency.
   */
  void increment() {
      if (getParent() == null) {
    return;
      }
      AppContext.getDataManager().markForUpdate(this);
      size++;
      getParent().increment();
  }

  /**
   * Recursively decrements the node's size until reaching the root. The
   * root is not updated to enable some degree of concurrency.
   */
  void decrement() {
      if (getParent() == null) {
    return;
      }
      AppContext.getDataManager().markForUpdate(this);
      if (--size == 0) {
    getParent().decrementChildrenAndSize();
      } else {
    getParent().decrement();
      }
  }

  /**
   * {@inheritDoc}
   */
  public void clear() {
      TreeNode<E> parent = getParent();
      AppContext.getDataManager().removeObject(this);
      if (parent != null) {
    parent.clear();
      }
  }

  /**
   * Retrieves the number of immediate children beneath this node.
   *
   * @return the number of immediate children
   */
  int getChildCount() {
      // If this is the root, then we have to iterate and
      // obtain the actual value.
      if (getParent() == null) {
    return getNumberOfChildrenUsingIteration();
      }
      return childrenCount;
  }

  /**
   * {@inheritDoc}
   */
  public TreeNode<E> getParent() {
      if (parentRef == null) {
    return null;
      }
      return parentRef.get();
  }

  /**
   * {@inheritDoc}
   */
  public void setNext(Node<E> ref) {
      AppContext.getDataManager().markForUpdate(this);
      TreeNode<E> t = uncheckedCast(ref);
      nextRef = createReferenceIfNecessary(t);
  }

  /**
   * Unlinks itself from the tree without performing a recursive
   * deletion. This method is guaranteed to delete itself. This method
   * re-links references that are dangling as a result of this node's
   * removal. There are four conditions:
   * <p>
   * The first condition is if the {@code TreeNode} is an intermediate
   * node. If so, it is necessary to link the left and right siblings
   * together before the node is pruned.
   * <p>
   * The second condition is if the {@code TreeNode} is the tail node.
   * If so, then the previous element's next reference is set to null
   * before the node is pruned.
   * <p>
   * The third condition is if the {@code TreeNode} is the head node. If
   * so, then the next element's previous reference is set to null
   * before the node is pruned.
   * <p>
   * The last condition is if the {@code TreeNode} is an only- child. If
   * so, then the parent's child reference is set to null before the
   * node is pruned.
   */
  void prune() {

      // Decide how to perform re-linking of the nodes,
      if (prevRef != null) {
    // interior TreeNode
    if (nextRef != null) {
        prevRef.getForUpdate().setNext(this.next());
        nextRef.getForUpdate().setPrev(this.prev());
    } else {
        // relink tail
        prevRef.getForUpdate().setNext(null);
    }
      } else if (nextRef != null) {
    // relink head and temporarily point to the next sibling.
    // If the next sibling didn't belong to the parent, then
    // the parent will be removed anyway since its child
    // count will have reached 0.
    nextRef.getForUpdate().setPrev(null);
    parentRef.getForUpdate().setChild(nextRef.get(),
      parentRef.get().size(),
      parentRef.get().getChildCount());

      } else {
    TreeNode<E> parent = parentRef.getForUpdate();
    parent.setChildToNull();
      }
      AppContext.getDataManager().removeObject(this);
  }

  /**
   * Determines where to split a list of children based on the list
   * size. By default, this is set to {@code numberOfChildren}/2.
   *
   * @param numberOfChildren the number of children to split
   * @return the index corresponding to the location where to split the
   * linked list
   */
  private static int calculateSplitSize(int numberOfChildren) {
      return numberOfChildren / 2;
  }

  /**
   * This method walks up the tree and performs any {@code TreeNode<E>}
   * splits if the children sizes have been exceeded.
   */
  private boolean performSplitIfNecessary() {
      // Check if we need to split
      if (childrenCount > branchingFactor) {
    split();
    return true;
      }
      return false;
  }

  /**
   * Splits the children into two roughly equal groups, and generates a
   * sibling to parent one of the groups.
   */
  private void split() {
      TreeNode<E> newNode = null;
      Node<E> tmp = (Node<E>) this.getChild();
      AppContext.getDataManager().markForUpdate(this);

      // Updates the reference of the new root, if created
      generateParentIfNecessary();

      // Perform split by creating and linking new sibling
      newNode = createAndLinkSibling(tmp, childrenCount);

      // Link the new sibling to the tree
      newNode.setPrev(this);
      newNode.setNext(this.next());
      this.setNext(newNode);
      if (newNode.next() != null) {
    newNode.next().setPrev(newNode);
      }
  }

  /**
   * Determines the nature of the sibling to be created. The Node
   * created will always be a TreeNode<E>, but the type of child
   * depends on whether it parents ListNodes, or other TreeNodes.
   *
   * @param child the {@code ManagedObject} which needs to be split and
   * placed under a new parent
   * @param numberOfChildren the number of children which the new
   * sibling will parent
   * @return the {@TreeNode<E>} that not represents a sibling which is
   * connected to the tree
   */
  private TreeNode<E> createAndLinkSibling(Node<E> child,
    int numberOfChildren) {
      int halfway = calculateSplitSize(numberOfChildren);
      int size = 0;

      // Create the new sibling
      TreeNode<E> newNode =
        new TreeNode<E>(owner.get(), getParent(), true);

      // Iterate through the children and update references
      Node<E> newChild = child;
      int i = 0;
      int children = 0;
      while (child != null && i < numberOfChildren) {

    // When we reach halfway, save the child as it
    // will be the child of the new TreeNode. When
    // we pass halfway, update the parents and
    // decrease the size.
    if (i == halfway) {
        newChild = child;
        if (child instanceof TreeNode) {
      child.prev().setNext(null);
      child.setPrev(null);
        }
    }
    if (i++ >= halfway) {
        size += child.size();
        children++;
        child.setParent(newNode);
    }
    child = child.next();
      }

      // Update the new parent's child, its size,
      // and our own size
      newNode.setChild(newChild, size, children);
      AppContext.getDataManager().markForUpdate(this);
      this.childrenCount -= children;
      this.size -= size;

      return newNode;
  }

  /**
   * This method creates a parent above the provided node so that any
   * new siblings resulting from a split can be joined to the new
   * parent. The decision to create a new parent depends on whether the
   * supplied node is the root; only these nodes are orphaned while
   * siblings have parents assigned to them.
   */
  private void generateParentIfNecessary() {
      // If this is the currently existing root, make a new one
      // and set the this node as the child
      if (getParent() == null) {
    TreeNode<E> grandparent =
      new TreeNode<E>(owner.get(), null, this, 1, size());

    // Link the node to its new parent
    setParent(grandparent);
    owner.getForUpdate().setRoot(grandparent);
      }
  }

  /**
   * Sets the parent for the node.
   *
   * @param parent the intended parent
   */
  public void setParent(TreeNode<E> parent) {
      AppContext.getDataManager().markForUpdate(this);
      parentRef = createReferenceIfNecessary(parent);
  }

  /**
   * Increments the number of children and size, and determines whether
   * the parent should do both, or just perform an increment of the
   * size.
   */
  void incrementChildrenAndSize() {
      AppContext.getDataManager().markForUpdate(this);
      TreeNode<E> parent = getParent();

      // If we are at the root, then check if we need to perform a
      // split. This will generate a new root.
      if (parent == null) {
    int numberOfRootChildren =
      getNumberOfChildrenUsingIteration();

    // Split if too many root children. Here we get
    // the real size since we don't update the root's
    // size to allow for concurrency. Since it will
    // no longer be the root, we need to find and set
    // its value.
    if (numberOfRootChildren > branchingFactor) {
        size = owner.get().size();
        childrenCount = numberOfRootChildren;
        split();
    }
    return;
      }

      size++;
      childrenCount++;

      // If it is not the root node, then
      // see if we need to split it normally.
      if (!performSplitIfNecessary()) {
    parent.increment();
    return;
      }
      parent.incrementChildrenAndSize();
  }

  /**
   * Decrements the number of children and size, and determines whether
   * the parent should do both again, or just decrement the size.
   */
  void decrementChildrenAndSize() {
      TreeNode<E> parent = getParent();

      // We are done when we reach the root
      if (parent == null) {
    return;
      }

      AppContext.getDataManager().markForUpdate(this);
      size--;
      childrenCount--;

      /*
       * If there are no children, then prune this node accordingly. If
       * the parent has only one child (this), then link the parent to
       * the children and prune this node. For both these cases, we
       * propagate the decrement of size and children to the parent.
       * Otherwise, we only propagate the decrement the size.
       */
      if (childrenCount == 0 && size() == 0) {
    prune();
    parent.decrementChildrenAndSize();
    return;
      } else if (parent.getChildCount() == 1) {
    pruneIntermediate();
      }
      parent.decrement();
  }

  /**
   * Removes a {@code TreeNode} that is the only child of a parent. This
   * requires that all children are updated of its new parent.
   */
  private void pruneIntermediate() {
      TreeNode<E> parent = getParent();
      Node<E> child = childRef.getForUpdate();
      parent.setChild(child, parent.size(), childrenCount);

      for (int i = 0; i < childrenCount; i++) {
    child.setParent(parent);
    child = child.next();
      }
      AppContext.getDataManager().removeObject(this);
  }

  /**
   * This method retrieves the number of children by iterating through
   * them one by one. This method is used when the current node is the
   * root, since the field {@code childrenCount} does not get updated.
   *
   * @return the number of children
   */
  private int getNumberOfChildrenUsingIteration() {
      int total = 0;
      Node<E> child = getChild();
      while (child != null) {
    total++;
    child = child.next();
      }
      return total;
  }

  /**
   * {@inheritDoc}
   */
  public SearchResult<E> search(int currentValue, int destIndex) {
      TreeNode<E> t = this;

      // Iterate through siblings
      while ((currentValue + t.size) < destIndex) {
    currentValue += t.size;
    t = t.next();

    // Check if the specified index was too large;
    if (t == null) {
        throw new IndexOutOfBoundsException("The index " +
          destIndex + " is out of range.");
    }
      }
      return t.getChild().search(currentValue, destIndex);
  }
    }

    /**
     * An asynchronous task which iterates through the tree and removes all
     * children. This task is instantiated when a {@code clear()} command is
     * issued from the {@code ScalableList}. The success of this operation is
     * dependent on implemented {@code clear()} methods on each of the
     * underlying components.
     *
     * @param <E> the type of element stored in the {@code ScalableList}
     */
    private static class AsynchronousClearTask<E> implements Serializable,
      Task, ManagedObject {

  private static final long serialVersionUID = 4L;

  /**
   * The maximum number of items to remove in a single iteration of the
   * task
   */
  private static final int MAX_OPERATIONS = 50;

  /**
   * A reference to the current {@code Node} whose children are being
   * deleted
   */
  private ManagedReference<ListNode<E>> current;

  /**
   * Constructor for the asynchronous task
   *
   * @param root the root node of the entire tree structure
   */
  AsynchronousClearTask(ScalableList<E> list) {
      assert (list != null);
      DataManager dm = AppContext.getDataManager();
      current = dm.createReference(list.getHead());
  }

  /**
   * The entry point of the task to perform the clear.
   */
  public void run() {
      // Perform some work and check if we need to reschedule
      DataManager dm = AppContext.getDataManager();
      dm.markForUpdate(this);

      if (doWork()) {
    AppContext.getTaskManager().scheduleTask(this);
      } else {
    dm.removeObject(this);

    Runnable r = noteDoneRemoving;
    if (r != null) {
        r.run();
    }
      }
  }

  /**
   * Removes MAX_OPERATION number of elements from the ScalableList and
   * returns {@code true} if there is more work to be done. If there are
   * no more elements to remove, then it will return {@code false},
   * signifying to the {@code AsynchronousClearTask} to start taking
   * values from the queue
   *
   * @return {@code true} if more work needs to be done, and
   * {@code false} if there are no more elements to remove.
   */
  private boolean doWork() {
      ListNode<E> currentListNode = current.get();
      AppContext.getDataManager().markForUpdate(currentListNode);
      ListNode<E> next;
      int count = 0;
      int size = currentListNode.size();
      E entry = null;

      // Perform some removals
      while (size > 0 && currentListNode != null &&
        count++ < MAX_OPERATIONS) {

    // Repeatedly remove the head element in the
    // ListNode as long as one exists.
    next = currentListNode.next();

    entry = currentListNode.remove(null, 0);
    if (--size == 0 && next != null) {
        currentListNode = next;
        size = currentListNode.size();
    }

    if (entry instanceof Element) {
        AppContext.getDataManager().removeObject(entry);
    }
      }

      // If we exit and the size is reported to be 0, then we
      // call the clear() method to delete the empty tree.
      // Otherwise, we will set up to re-execute the task
      if (size == 0) {
    currentListNode.clear();
    return false;

      } else {
    current =
      AppContext.getDataManager().createReference(
        currentListNode);
      }
      return true;
  }

    }

    /**
     * This class represents a stored entity of the list. It is a wrapper for
     * any object that is stored in the list so that the list can refer to it
     * by using a ManagedReference, rather than the actual object itself. This
     * makes managing sublists less intensive as each reference to an
     * underlying entity is a fixed size.
     *
     * @param <E> the type of element stored in the {@code ScalableList}
     */
    static class Element<E> implements Serializable, ManagedObject {

  private static final long serialVersionUID = 5L;

  /**
   * The stored value which is not a {@code ManagedObject}. If it were
   * a {@code ManagedObject}, then it would not need to be enveloped by
   * an {@code Element} wrapper.
   */
  private E value;

  /**
   * Constructor for creating an {@code Element}.
   *
   * @param e the element to store within the {@code Element}
   * {@code ManagedObject}
   */
  Element(E e) {
      value = e;
  }

  /**
   * Retrieves the element.
   *
   * @return the element stored by this object
   */
  E getValue() {
      return value;
  }

  /**
   * Sets the value of the element
   *
   * @param e the new value to set
   */
  void setValue(E e) {
      assert (e != null);
      AppContext.getDataManager().markForUpdate(this);
      value = e;
  }

  /**
   * Returns a {@code String} representation
   *
   * @return the value in {@code String} form
   */
  public String toString() {
      return value.toString();
  }
    }

    /**
     * This iterator walks through the {@code ListNode<E>}s which parent the
     * {@code SubList}s. An iterator for this type of element is necessary to
     * support the other element iterator as well as {@code indexOf()}
     * operations.
     *
     * @param <E> the type of element stored in the {@code ScalableList}
     */
    static class ScalableListNodeIterator<E> implements Serializable,
      Iterator<ListNode<E>> {

  /**
   * The current position of the iterator. This is initialized as
   * {@code null} and is set to null when there are no more elements.
   */
  private ListNode<E> current;

  /**
   * The next position of the iterator. This is initialized as the head
   * element provided in the constructor
   */
  private ListNode<E> next;

  /**
   * The previous position of the iterator. This is initialized as the
   * head element provided in the constructor
   */
  private ListNode<E> prev;

  private static final long serialVersionUID = 7L;

  /**
   * Constructor to create a new iterator.
   *
   * @param head the head {@code ListNode} from which to begin iterating
   */
  ScalableListNodeIterator(ListNode<E> head) {
      prev = null;
      current = null;
      next = head;
  }

  /**
   * Determines if there is a next element to iterate over; that is, if
   * the next {@code ListNode} is not null.
   *
   * @return {@code true} if the next element is not null, and
   * {@code false} otherwise
   */
  public boolean hasNext() {
      return (next != null);
  }

  /**
   * Returns the next {@code ListNode<E>} in order from the list
   *
   * @return the next {@code ListNode}
   * @throws NoSuchElementException if there exists no next sibling
   */
  public ListNode<E> next() {
      if (current == null && next == null) {
    throw new NoSuchElementException("There is no next element");
      }

      prev = current;
      current = next;
      if (next != null) {
    next = next.next();
      }
      return current;
  }

  /**
   * Determines if there is a previous element to iterate over; that is,
   * if the previous {@code ListNode} is not null.
   *
   * @return {@code true} if the previous element is not null, and
   * {@code false} otherwise
   */
  public boolean hasPrev() {
      return (prev != null);
  }

  /**
   * Returns the previous {@code ListNode<E>} in order from the list
   *
   * @return the previous {@code ListNode}
   * @throws NoSuchElementException if there exists no previous sibling
   */
  public ListNode<E> prev() {
      if (current == null && prev == null) {
    throw new NoSuchElementException("There is no prev element");
      }

      next = current;
      current = prev;
      if (prev != null) {
    prev = prev.prev();
      }
      return current;
  }

  /**
   * @deprecated this operation is not supported because the
   * {@code ScalableList} only removes elements explicitly, not
   * {@code ListNodes}.
   * @throws UnsupportedOperationException the operation is not
   * supported
   */
  public void remove() {
      throw new UnsupportedOperationException(
        "This method is not supported");
  }
    }

    /**
     * This class represents an iterator of the elements of the
     * {@code ScalableList}.
     *
     * @param <E> the type of element stored in the {@code ScalableList}
     */
    static class ScalableIterator<E> implements Serializable, Iterator<E> {

  /**
   * A reference to the {@code ScalableList} which this iterator is
   * referring to
   */
  final ManagedReference<ScalableList<E>> owner;

  /**
   * The current {@code ListNode} of the iterative process
   */
  protected ManagedReference<ListNode<E>> currentNode;

  /**
   * The iteration location of the list
   */
  protected int cursor;

  /**
   * Flag which only lets one removal happen per call to {@code next()}
   */
  protected boolean wasNextCalled;

  /**
   * The value for the current {@code ListNode} to determine if any
   * changes have taken place since the last time it was accessed
   */
  protected long listNodeReferenceValue = -1;

  private static final long serialVersionUID = 8L;

  /**
   * Performs a check to see that the index for {@code next()} is still
   * within range of the sub list.
   *
   * @param <E> the type of element stored
   * @param offset the offset
   * @param currentListNode the current {@code ListNode} being examined
   * @return {@code true} if the offset exists in the sub list and
   * {@code false} otherwise
   */
  static <E> boolean isNextWithinRange(int offset,
    ListNode<E> currentListNode) {
      return (offset < currentListNode.size());
  }

  /**
   * Constructor used to create a {@code ScalableListIterator} for the
   * underlying elements in the {@code ScalableList}.
   *
   * @param list the {@code ScalableList} over which to iterate
   */
  ScalableIterator(ScalableList<E> list) {
      this(list, list.getHead());
  }

  ScalableIterator(ScalableList<E> list, ListNode<E> startingNode) {
      owner = AppContext.getDataManager().createReference(list);
      currentNode =
        AppContext.getDataManager().createReference(startingNode);

      cursor = 0;
      listNodeReferenceValue =
        currentNode.get().getDataIntegrityValue();
      wasNextCalled = false;
  }

  /**
   * Retrieves the index of the current element by walking up the tree
   * to the root and aggregating the counts of each {@code ListNode} and
   * {@code TreeNode}. This operation is slightly expensive because of
   * the required percolation up the tree.
   *
   * @return the absolute index of the current element being examined
   */
  int getCurrentIndex() {
      return getAbsoluteIndex(currentNode.get(), 1) + cursor;
  }

  /**
   * Walks up the tree and collects the sizes to produce an absolute
   * index.
   *
   * @return the absolute index of the given node
   */
  int getAbsoluteIndex(Node<E> node, int size) {
      TreeNode<E> parent = node.getParent();

      // We reached the root; return the size we have.
      if (parent == null) {
    return size - 1;
      }
      Node<E> firstChild = parent.getChild();

      // Iterate through siblings. We don't
      // aggregate the first node's size because
      // we already found the offset from the
      // caller.
      while (!node.equals(firstChild)) {
    node = node.prev();
    size += node.size();
      }
      return getAbsoluteIndex(parent, size);
  }

  /**
   * Retrieves the next element.
   *
   * @throws ConcurrentModificationException if the {@code ListNode}
   * that the iterator is pointing to has been modified to (addition or
   * removal) by someone else
   * @throws NoSuchElementException if there is no next element
   * @return the next element
   */
  @SuppressWarnings("unchecked")
  public E next() {
      // Check the integrity of the ListNode to see if
      // any changes were made since we last operated.
      // Throw a ConcurrentModificationException if so.
      checkDataIntegrity();

      List<ManagedReference<ManagedObject>> elements =
        currentNode.get().getSubList().getElements();

      // Retrieve the next value from the list; we may have to load
      // up a new ListNode
      int index = getCursorBasedOnPreviousAction(true);
      if (!isNextWithinRange(index, currentNode.get())) {
    if (loadNextListNode()) {
        elements = currentNode.get().getSubList().getElements();
        index = 0;
    } else {
        throw new NoSuchElementException(
          "There is no next element");
    }
      }
      cursor = index;
      wasNextCalled = true;

      // Once we have located the next node,
      // update the reference values
      listNodeReferenceValue =
        currentNode.get().getDataIntegrityValue();

      // In case we wrapped the item with an
      // Element object, fetch the value.
      return (E) getValueFromReference(elements.get(cursor), false);
  }

  /**
   * Retrieve the index of interest, based on our previous direction and
   * intended direction
   *
   * @param next whether we intend on travelling in the {@code next}
   * direction; {@code true} if so, and {@code false} otherwise
   * @return the index corresponding to the cursor
   */
  protected int getCursorBasedOnPreviousAction(boolean next) {
      if (next) {
    return (wasNextCalled ? cursor + 1 : cursor);
      } else {
    return (wasNextCalled ? cursor : cursor - 1);
      }
  }

  /**
   * Get the next {@code ListNode} for the
   * {@code ScalableListNodeIterator}.
   *
   * @return {@code true} if there was a next {@code ListNode}, and
   * {@code false} otherwise
   */
  private boolean loadNextListNode() {
      ListNode<E> next = currentNode.get().next();
      if (next != null) {
    currentNode =
      AppContext.getDataManager().createReference(next);
    cursor = 0;
    return true;
      }
      return false;
  }

  /**
   * Checks whether the data integrity value has changed, and throws a
   * {@code ConcurrentModificationException} if so.
   *
   * @throws ConcurrentModificationException if the data integrity value
   * has changed or if it cannot be verified
   */
  void checkDataIntegrity() {
      try {
    if (currentNode.get().getDataIntegrityValue() ==
          listNodeReferenceValue) {
        return;
    }
      } catch (ObjectNotFoundException onfe) {
      }
      throw new ConcurrentModificationException(
        "The ListNode has been modified or removed");

  }

  /**
   * Returns whether there is a next element to iterate over.
   *
   * @return {@code true} if there is a next element, or {@code false}
   * otherwise
   * @throws ConcurrentModificationException if the {@code ListNode} has
   * been modified or removed
   */
  public boolean hasNext() {

      checkDataIntegrity();

      // If there is an element in the iterator still,
      // then simply return true since it will be the
      // next element to be returned.
      if (isNextWithinRange(getCursorBasedOnPreviousAction(true),
        currentNode.get())) {
    return true;
      }

      // Try seeing if there is a next ListNode
      return (currentNode.get().next() != null);
  }

  /**
   * Removes from the underlying collection the last element returned by
   * the iterator. This can only be called once per call to {@code next}.
   */
  public void remove() {
      if (!wasNextCalled) {
    throw new IllegalStateException(
      "remove() must follow next() or previous()");
      }
      doRemove();
      wasNextCalled = false;
  }

  /**
   * Performs the remove and updates the references if necessary
   */
  void doRemove() {
      ListNode<E> prev = currentNode.get().prev();
      ListNode<E> next = currentNode.get().next();
      int size = currentNode.get().size();
      currentNode.get().remove(owner.get(), cursor);

      // if the removal caused a ListNode to be removed,
      // then we need to replace the currentNode with a
      // valid one.
      if (size == 1 || cursor == 0) {
    if (prev != null) {
        currentNode =
          AppContext.getDataManager().createReference(prev);
        cursor = currentNode.get().size();
    } else if (next != null) {
        currentNode =
          AppContext.getDataManager().createReference(next);
        cursor = 0;
    } else {
        cursor = 0;
    }
      } else {
    cursor--;
      }

      // update the data integrity value
      listNodeReferenceValue =
        currentNode.get().getDataIntegrityValue();
  }
    }

    /**
     * A class which implements a {@code ListIterator} for the
     * {@code ScalableList} data structure. This iterator allows
     * bi-directional traversal and other operations native to the
     * {@code ListIterator} interface.
     *
     * @param <E> the type of element
     */
    static class ScalableListIterator<E> extends ScalableIterator<E>
      implements ListIterator<E> {

  private static final long serialVersionUID = 9L;

  /**
   * A flag which records whether a removal or set is an invalid
   * operation. {@code True} if invalid, and {@code false} otherwise.
   */
  private boolean cannotRemoveOrSet;

  /**
   * Constructor which starts the iterations at the specified
   * {@code ListNode}.
   *
   * @param list the {@code ScalableList} over which to iterate
   */
  ScalableListIterator(ScalableList<E> list) {
      super(list);
      cannotRemoveOrSet = true;
  }

  /**
   * Constructor which creates a {@code ScalableListIterator} given the
   * list, a {@code startingNode} and a {@code startingIndex} denoting
   * the starting point. This constructor is used primarily for when the
   * user specifies an index that is one larger than the highest index
   * value.
   *
   * @param list a reference to the {@code ScalableList}
   * @param startingIndex the starting index (relative)
   * @param startingNode the starting node
   */
  ScalableListIterator(ScalableList<E> list, ListNode<E> startingNode,
    int startingIndex) {
      super(list, startingNode);
      wasNextCalled = false;
      cursor = startingIndex;
  }

  /**
   * Constructor which creates a {@code ScalableListIterator} given the
   * list and a {@code searchResult} denoting the starting point.
   *
   * @param list
   * @param searchResult
   */
  ScalableListIterator(ScalableList<E> list,
    SearchResult<E> searchResult) {
      super(list, searchResult.node);
      cursor = searchResult.offset;
      cannotRemoveOrSet = true;
  }

  /**
   * Performs a check to see that the index for {@code prev()} is still
   * within range of the sub list.
   *
   * @param <E> the type of element stored
   * @param offset the offset
   * @param currentListNode the current {@code ListNode} being examined
   * @return {@code true} if the offset exists in the sub list and
   * {@code false} otherwise
   */
  static <E> boolean isPrevWithinRange(int offset,
    ListNode<E> currentListNode) {
      return (offset >= 0);
  }

  /**
   * Get the previous {@code ListNode} for the
   * {@code ScalableListNodeIterator}.
   *
   * @return {@code true} if there was a previous {@code ListNode}, and
   * {@code false} otherwise
   */
  private boolean loadPrevListNode() {
      ListNode<E> prev = currentNode.get().prev();
      if (prev != null) {
    currentNode =
      AppContext.getDataManager().createReference(prev);
    return true;
      }
      return false;
  }

  /**
   * Returns the index of the element that would be returned by a
   * subsequent call to <tt>next</tt>. (Returns list size if the list
   * iterator is at the end of the list.)
   *
   * @return the index of the element that would be returned by a
   * subsequent call to <tt>next</tt>, or list size if list iterator
   * is at end of list.
   */
  public int nextIndex() {
      int nextIndex = getCurrentIndex();
      if (wasNextCalled) {
    nextIndex++;
      }
      return nextIndex;
  }

  /**
   * Returns the index of the element that would be returned by a
   * subsequent call to <tt>previous</tt>. (Returns -1 if the list
   * iterator is at the beginning of the list.)
   *
   * @return the index of the element that would be returned by a
   * subsequent call to <tt>previous</tt>, or -1 if list iterator is
   * at beginning of list.
   */
  public int previousIndex() {
      int previousIndex = getCurrentIndex();
      if (!wasNextCalled) {
    previousIndex--;
      }
      return previousIndex;
  }

  /**
   * Returns <tt>true</tt> if this list iterator has more elements
   * when traversing the list in the reverse direction. (In other words,
   * returns <tt>true</tt> if <tt>previous</tt> would return an
   * element rather than throwing an exception.)
   *
   * @return <tt>true</tt> if the list iterator has more elements when
   * traversing the list in the reverse direction.
   * @throws ConcurrentModificationException if the {@code ListNode} has
   * been modified or removed
   */
  public boolean hasPrevious() {
      checkDataIntegrity();

      // If the future index will be equal to or larger than 0,
      // then another element exists
      if (isPrevWithinRange(getCursorBasedOnPreviousAction(false),
        currentNode.get())) {
    return true;
      }

      // Otherwise, try loading the next ListNode
      return (currentNode.get().prev() != null);
  }

  /**
   * Returns the previous element in the list. This method may be called
   * repeatedly to iterate through the list backwards, or intermixed
   * with calls to <tt>next</tt> to go back and forth. (Note that
   * alternating calls to <tt>next</tt> and <tt>previous</tt> will
   * return the same element repeatedly.)
   *
   * @return the previous element in the list
   * @throws NoSuchElementException if the iteration has no previous
   * element
   * @throws ConcurrentModificationException if the {@code ListNode} has
   * been modified or removed
   */
  @SuppressWarnings("unchecked")
  public E previous() {

      // Check the integrity of the ListNode to see if
      // any changes were made since we last operated.
      // Throw a ConcurrentModificationException if so.
      checkDataIntegrity();

      cannotRemoveOrSet = false;
      List<ManagedReference<ManagedObject>> elements =
        currentNode.get().getSubList().getElements();

      // Retrieve the value from the list; we may have to load
      // up a new ListNode
      int index = getCursorBasedOnPreviousAction(false);
      if (!isPrevWithinRange(index, currentNode.get())) {
    if (loadPrevListNode()) {
        elements = currentNode.get().getSubList().getElements();
        index = currentNode.get().size() - 1;
    } else {
        throw new NoSuchElementException(
          "The previous element does not exist");
    }
      }
      cursor = index;
      wasNextCalled = false;

      // Once we have found the previous node,
      // update the reference values
      listNodeReferenceValue =
        currentNode.get().getDataIntegrityValue();

      return (E) getValueFromReference(elements.get(cursor), false);

  }

  /**
   * Replaces the last element returned by <tt>next</tt> or
   * <tt>previous</tt> with the specified element (optional
   * operation). This process will automatically remove the old element
   * from the data manager if it was not a {@code ManagedObject}.
   *
   * @param o the element with which to replace the last element
   * returned by <tt>next</tt> or <tt>previous</tt>.
   * @throws IllegalStateException if the operation is called without
   * {@code next} or {@code previous} being called
   */
  public void set(E o) {
      if (cannotRemoveOrSet) {
    throw new IllegalStateException(
      "set() must follow next() or previous()");
      }
      currentNode.get().set(cursor, o);
      listNodeReferenceValue =
        currentNode.get().getDataIntegrityValue();
  }

  /**
   * Removes from the underlying collection the last element returned by
   * the iterator. This can only be called once per call to {@code next}.
   *
   * @throws IllegalStateException if the method is not preceded by
   * {@code next()} or {@code prev()}
   */
  public void remove() {
      if (cannotRemoveOrSet) {
    throw new IllegalStateException(
      "remove() must follow next() or previous()");
      }

      if (!wasNextCalled) {
    this.doRemove();
      } else {
    super.remove();
      }
      cannotRemoveOrSet = true;
  }

  /**
   * {@inheritDoc}
   */
  void doRemove() {
      ListNode<E> prev = currentNode.get().prev();
      ListNode<E> next = currentNode.get().next();
      int size = currentNode.get().size();
      currentNode.get().remove(owner.get(), cursor);

      // if the removal caused a ListNode to be removed,
      // then we need to replace the currentNode with a
      // valid one.
      if (size == 1 || cursor == 0) {
    if (next != null) {
        currentNode =
          AppContext.getDataManager().createReference(next);
        cursor = 0;
    } else if (prev != null) {
        currentNode =
          AppContext.getDataManager().createReference(prev);
        cursor = currentNode.get().size();
    } else {
        cursor = 0;
    }
      } else {
    cursor++;
      }

      // update the data integrity value
      listNodeReferenceValue =
        currentNode.get().getDataIntegrityValue();
  }

  /**
   * {@inheritDoc}
   */
  public E next() {
      cannotRemoveOrSet = false;
      return super.next();
  }

  /**
   * Inserts the specified element into the list. The element is
   * inserted immediately before the next element that would be returned
   * by <tt>next</tt>, if any, and after the next element that would
   * be returned by <tt>previous</tt>, if any. (If the list contains
   * no elements, the new element becomes the sole element on the list.)
   * The new element is inserted before the implicit cursor: a
   * subsequent call to <tt>next</tt> would be unaffected, and a
   * subsequent call to <tt>previous</tt> would return the new
   * element. (This call increases by one the value that would be
   * returned by a call to <tt>nextIndex</tt> or
   * <tt>previousIndex</tt>.)
   *
   * @param o the element to insert.
   * @throws IndexOutOfBoundsException if the index which the element is
   * to be added is out of bounds
   */
  public void add(E o) {
      // Add element to what will be the next index
      if (wasNextCalled) {
    currentNode.get().insert(++cursor, o);
      } else {
    currentNode.get().insert(cursor++, o);
      }

      // If the addition added a new list node,
      // then we need to point to it
      if (cursor > currentNode.get().size() - 1) {
    currentNode =
      AppContext.getDataManager().createReference(
        currentNode.get().next());
    cursor = 0;
      }

      cannotRemoveOrSet = true;
      listNodeReferenceValue =
        currentNode.get().getDataIntegrityValue();
  }
    }

    /**
     * Node which parents a {@code SubList<E>}. These nodes can be considered
     * as the leaf nodes of the tree and contain references to a portion of
     * the list. A {@code ListNode}'s parent is always a {@code TreeNode}
     * since they are the deepest organizational element of the
     * {@code ScalableList}. {@code ListNode}s are arranged in a
     * doubly-linked list, each having a reference to its parent.
     *
     * @param <E> the type of element stored in the {@code ScalableList}
     */
    static class ListNode<E> implements ManagedObject, Serializable, Node<E> {

  private static final int DATA_INTEGRITY_STARTING_VALUE =
    Integer.MIN_VALUE;

  // References to neighbors
  private ManagedReference<SubList<E>> subListRef;
  private ManagedReference<ListNode<E>> nextRef;
  private ManagedReference<ListNode<E>> prevRef;
  private ManagedReference<TreeNode<E>> parentRef;

  private static final long serialVersionUID = 9L;

  /**
   * A value which increments when changes are made to the node. This is
   * used by iterators to deal with concurrent modifications
   */
  private int dataIntegrityVal;

  /**
   * The number of elements contained in the SubList.
   */
  private int count;

  /**
   * Private constructor which the public constructors will call.
   *
   * @param parent the intended parent
   */
  private ListNode(TreeNode<E> parent) {
      nextRef = null;
      prevRef = null;
      dataIntegrityVal = DATA_INTEGRITY_STARTING_VALUE;
      parentRef = AppContext.getDataManager().createReference(parent);
  }

  /**
   * Constructor which uses knowledge of a parent and maximum list size.
   * A {@code ListNode} that exceeds {@code maxSize} will be subject to
   * splitting.
   *
   * @param parent the intended parent
   * @param maxSize the maximum number of elements that can be stored
   */
  ListNode(TreeNode<E> parent, int maxSize) {
      this(parent);

      SubList<E> sublist = new SubList<E>(maxSize);
      count = sublist.size();
      subListRef = AppContext.getDataManager().createReference(sublist);
  }

  /**
   * Constructor which uses knowledge of a parent and maximum list size.
   * A {@code ListNode} that exceeds {@code maxSize} will be subject to
   * splitting.
   *
   * @param parent the intended parent
   * @param maxSize the maximum number of elements that can be stored
   * @param e an element which is to be stored as the first item in the
   * list
   */
  ListNode(TreeNode<E> parent, int maxSize, E e) {
      this(parent);

      SubList<E> sublist = new SubList<E>(maxSize, e);
      count = sublist.size();
      subListRef = AppContext.getDataManager().createReference(sublist);
  }

  /**
   * Constructor which uses knowledge of a parent and maximum list size.
   * A {@code ListNode} that exceeds {@code maxSize} will be subject to
   * splitting.
   *
   * @param parent the intended parent
   * @param maxSize the maximum number of elements that can be stored
   * @param list a list of items which are to be added into the empty
   * list
   */
  ListNode(TreeNode<E> parent, int maxSize,
    List<ManagedReference<ManagedObject>> list) {
      this(parent);

      SubList<E> sublist = new SubList<E>(maxSize, list);
      count = sublist.size();
      subListRef = AppContext.getDataManager().createReference(sublist);
  }

  /**
   * Returns the data integrity value of the {@code ListNode}
   *
   * @return the current data integrity value
   */
  int getDataIntegrityValue() {
      return dataIntegrityVal;
  }

  /**
   * {@inheritDoc}
   */
  public void setNext(Node<E> ref) {
      AppContext.getDataManager().markForUpdate(this);
      ListNode<E> node = uncheckedCast(ref);
      nextRef = createReferenceIfNecessary(node);
  }

  /**
   * {@inheritDoc}
   */
  public void setParent(TreeNode<E> parent) {
      AppContext.getDataManager().markForUpdate(this);
      parentRef = createReferenceIfNecessary(parent);
  }

  /**
   * {@inheritDoc}
   */
  public ListNode<E> next() {
      if (nextRef == null) {
    return null;
      }
      return nextRef.get();
  }

  /**
   * {@inheritDoc}
   */
  public void setPrev(Node<E> ref) {
      AppContext.getDataManager().markForUpdate(this);
      ListNode<E> n = uncheckedCast(ref);
      prevRef = createReferenceIfNecessary(n);
  }

  /**
   * {@inheritDoc}
   */
  public ListNode<E> prev() {
      if (prevRef == null) {
    return null;
      }
      return prevRef.get();
  }

  /**
   * {@inheritDoc}
   */
  public int size() {
      return count;
  }

  /**
   * Returns the {@code SubList} object which contains a subset of the
   * elements in the collection.
   *
   * @return the {@code SubList} containing list elements, or null if
   * one has not yet been instantiated
   */
  SubList<E> getSubList() {
      if (subListRef == null) {
    return null;
      }
      return subListRef.get();
  }

  /**
   * Appends the supplied object to the list and performs a split if
   * necessary.
   *
   * @param e the element to append
   */
  void append(E e) {
      getSubList().append(e);
      AppContext.getDataManager().markForUpdate(this);
      count++;
      dataIntegrityVal++;

      // check if we need to split; i.e. if we have exceeded
      // the specified list size.
      if (count > getSubList().maxChildren) {
    split();
      } else {
    this.getParent().increment();
      }
  }

  /**
   * Inserts the supplied value at the given index. The index is
   * relative to the current list and not the global collection.
   *
   * @param index the index to insert the value, relative to the current
   * {@code SubList}
   * @param e the value to insert
   */
  void insert(int index, E e) {
      getSubList().insert(index, e);
      AppContext.getDataManager().markForUpdate(this);
      count++;
      dataIntegrityVal++;

      // check if we need to split; i.e. if we have exceeded
      // the specified list size.
      if (count > getSubList().maxChildren) {
    split();
      } else {
    this.getParent().increment();
      }
  }

  /**
   * Removes the object at the specified index of the sublist. The index
   * argument is not an absolute index; it is a relative index which
   * points to a valid index in the list.
   * <p>
   * For example, if there are five ListNodes with a cluster size of
   * five, the item with an absolute index of 16 corresponds to an
   * element in the fourth ListNode<E>, with a relative offset of 1.
   *
   * @param list a reference to the {@code ScalableList}; this argument
   * should only ever be null during the {@code AsynchronousClearTask}
   * operation
   * @param index the index corresponding to an element in the list (not
   * an absolute index with respect to the {@code ScalableList} object
   * @return the element that was removed
   */
  E remove(ScalableList<E> list, int index) {
      E old = getSubList().remove(index);
      if (old != null) {
    doRemoveWork(list);
      }
      return old;
  }

  /**
   * {@inheritDoc}
   */
  public void clear() {
      TreeNode<E> parent = getParent();
      DataManager dm = AppContext.getDataManager();
      dm.removeObject(getSubList());
      dm.removeObject(this);
      parent.clear();
  }

  /**
   * A {@code String} representation of the {@code ListNode}.
   *
   * @return a {@code String} representing the contents of the
   * {@code ListNode}
   */
  public String toString() {
      return subListRef.get().toString();
  }

  /**
   * Performs the work of calling the recursive update methods
   *
   * @param list a reference to the {@code ScalableList}
   */
  private void doRemoveWork(ScalableList<E> list) {
      AppContext.getDataManager().markForUpdate(this);
      count--;
      dataIntegrityVal++;

      TreeNode<E> parent = getParent();
      if (count == 0) {
    checkRemoveListNode(list);
    parent.decrementChildrenAndSize();
      } else {
    parent.decrement();
      }
  }

  /**
   * Removes the {@code Object} from the {@code SubList<E>}, if it
   * exists.
   *
   * @param list a reference to the {@code ScalableList}; since this
   * method is not called by the {@code AsynchronousClearTask}, this
   * parameter must not be null.
   * @param obj the {@code Object} to remove
   * @return whether the object was removed or not; {@code true} if so,
   * {@code false} otherwise
   */
  boolean remove(ScalableList<E> list, Object obj) {
      assert (list != null);
      boolean result = getSubList().remove(obj);

      // If a removal took place, then update
      // count information accordingly
      if (result) {
    doRemoveWork(list);
      }
      return result;
  }

  /**
   * Sets the element at the supplied index with the provided value.
   *
   * @param index the index to set the value
   * @param obj the value to replace the existing one
   * @return the old value
   */
  E set(int index, Object obj) {
      return subListRef.getForUpdate().set(index, obj);
  }

  /**
   * A method that determines how to remove an empty {@code ListNode<E>}
   * from a list of other {@code ListNode<E>}s. Update previous to
   * point to next (doubly) and remove this object from Data Store
   */
  private void checkRemoveListNode(ScalableList<E> list) {

      // If the size is not zero, then we
      // will not be removing any nodes,
      // so no relinking; just return.
      if (size() != 0) {
    return;
      }

      // If this is an only child and its size is 0,
      // we will keep it so that we can make future
      // appends rather than creating a new ListNode.
      if (next() == null && prev() == null) {
    return;
      }

      // Link the parent to the next child before
      // we attempt to remove the current node
      linkParentToNextIfNecessary();

      // Now we need to remove the list
      // node and relink accordingly.
      // First, we determine the type
      // of list node: there are three possibilities:
      // 1) interior node; connect prev to next
      // 2) head node. Update child pointer from parent
      // 3) tail node
      if (next() != null && prev() != null) {
    prev().setNext(next());
    next().setPrev(prev());
      } else if (next() != null) {
    next().setPrev(null);
    // The clear task will cause the list parameter
    // to be null because we don't need to be
    // updating the head; if we did, then we would
    // asynchronously be updating the empty list's
    // head.
    if (list != null) {
        list.setHead(next());
    }
      } else {
    prev().setNext(null);
    // The clear task will cause the list parameter
    // to be null because we don't need to be
    // updating the head; if we did, then we would
    // asynchronously be updating the empty list's
    // head.
    if (list != null) {
        list.setTail(prev());
    }
      }

      // This is an empty node, so remove it from Data Store.
      AppContext.getDataManager().removeObject(getSubList());
      AppContext.getDataManager().removeObject(this);
  }

  /**
   * Determines if the parent should be connected to the next sibling
   * after a removal has taken place. This will occur as long as there
   * is a next sibling, the next sibling has the same parent as
   * {@code this}, the parent has sufficient children, and if the
   * parent was not yet removed. Otherwise, if the parent has no
   * children, then there is no child to update the child reference to,
   * so we set it to null and prune the node during the propagation.
   */
  private void linkParentToNextIfNecessary() {
      TreeNode<E> parent = getParent();

      // We decrement the child count because this method is
      // called after a removal has taken place
      int children = parent.getChildCount() - 1;

      if (next() != null && children > 0 &&
        parent.getChild().equals(this) &&
        parent.equals(next().getParent())) {
    parent
      .setChild(next(), parent.size(), parent
        .getChildCount());
      } else if (children == 0) {
    parent.setChildToNull();
      }
  }

  /**
   * Retrieves the parent of the {@code ListNode}
   *
   * @return the parent
   */
  public TreeNode<E> getParent() {
      TreeNode<E> parent;
      if (parentRef == null) {
    return null;
      }

      // By definition, there should always be a parent
      // to a list node. Throw an ObjectNotFoundException
      // if this call goes bad.
      parent = parentRef.get();
      return parent;
  }

  /**
   * Splits the children into two roughly equal groups, and generates a
   * sibling to parent one of the groups.
   */
  private void split() {
      List<ManagedReference<ManagedObject>> contents =
        getSubList().getElements();
      List<ManagedReference<ManagedObject>> spawned =
        new ArrayList<ManagedReference<ManagedObject>>();

      // move last half of list into a new child
      int sublistSize = getSubList().size();
      int lower = sublistSize / 2;
      for (int index = lower; index < sublistSize; index++) {
    ManagedReference<ManagedObject> temp = contents.get(index);
    spawned.add(temp);
      }

      // remove the relocated nodes from the current list
      // and mark that the list has changed
      contents.removeAll(spawned);
      this.count = contents.size();

      // Create a new ListNode<E> for the moved contents
      ListNode<E> spawnedNode =
        new ListNode<E>(getParent(),
          this.getSubList().maxChildren, spawned);

      // Perform necessarily linking
      spawnedNode.setNext(this.next());
      spawnedNode.setPrev(this);
      if (next() != null) {
    this.next().setPrev(spawnedNode);
      }
      this.setNext(spawnedNode);

      // Walks up the tree to increment the new number of children
      this.getParent().incrementChildrenAndSize();
  }

  /**
   * {@inheritDoc}
   */
  public SearchResult<E> search(int currentValue, int destIndex) {
      ListNode<E> n = this;

      while ((currentValue + n.size()) < destIndex) {
    currentValue += n.size();
    n = n.next();

    if (n == null) {
        throw new IndexOutOfBoundsException(
          "The index is out of range.");
    }
      }

      return new SearchResult<E>(n, destIndex - currentValue - 1);
  }
    }

    /**
     * This object represents a partition in the list, otherwise denoted as a
     * {@code bucket} (as per {@code bucketSize}). Only one of these
     * {@code SubList} objects lives inside a ListNode<E> object; therefore,
     * there are as many {@code SubList} objects as there are {@code ListNode}s.
     * <p>
     * The separation of the elements from the {@code ListNode}s is to allow
     * iterations and other read operations while modifications to elements
     * occur. This is particularly important for replacements because parent
     * sizes do not need to change since no elements are being added or
     * removed.
     *
     * @param <E> the type of element stored in the {@code ScalableList}
     */
    static class SubList<E> implements ManagedObject, Serializable {

  private static final long serialVersionUID = 10L;

  /**
   * A reference to the list of elements
   */
  private ArrayList<ManagedReference<ManagedObject>> contents;

  /**
   * The maximum number of children which can be contained, previously
   * addressed as the {@code bucketSize}
   */
  private final int maxChildren;

  /**
   * Performs a quick check to see if the argument is a legal parameter;
   * that is, larger than 0.
   */
  public static boolean isLegal(int maxSize) {
      return (maxSize > 0);
  }

  /**
   * Constructor which creates a {@code SubList}.
   *
   * @param maxSize the maximum number of elements which can be stored
   * @param collection the elements to add to the empty list
   */
  SubList(int maxSize, List<ManagedReference<ManagedObject>> collection) {
      this(maxSize);
      ManagedReference<ManagedObject> tmp = null;

      for (int i = 0; i < collection.size(); i++) {
    tmp = collection.get(i);
    contents.add(tmp);
      }
  }

  /**
   * Constructor which creates a {@code SubList}
   *
   * @param maxSize the maximum number of elements which can be stored
   */
  SubList(int maxSize) {
      assert (isLegal(maxSize));
      maxChildren = maxSize;
      contents = new ArrayList<ManagedReference<ManagedObject>>();
  }

  /**
   * Constructor to create a {@code SubList}
   *
   * @param maxSize the maximum number of elements which can be stored
   * @param e an element to add to the empty list, at the first index
   */
  SubList(int maxSize, E e) {
      this(maxSize);
      append(e);
  }

  /**
   * Returns a {@code String} representation of this object.
   *
   * @return a {@code String} representation of this object
   */
  public String toString() {
      StringBuilder s = new StringBuilder("{");
      for (int i = 0; i < contents.size(); i++) {
    if (i > 0) {
        s.append(",");
    }
    s.append(contents.get(i).toString());
      }
      s.append("}");
      return s.toString();
  }

  /**
   * Returns the maximum number of children for this structure.
   *
   * @return the maximum number of children
   */
  int getMaxChildren() {
      return maxChildren;
  }

  /**
   * Returns the size of the collection.
   *
   * @return the size
   */
  int size() {
      return contents.size();
  }

  /**
   * Returns the elements contained in the {@code SubList} as an
   * {@code ArrayList}.
   *
   * @return the elements contained in the {@code SubList}
   */
  List<ManagedReference<ManagedObject>> getElements() {
      return contents;
  }

  /**
   * Since the list is a collection of ManagedReferences, we are
   * interested in retrieving the value it points to.
   *
   * @param index the index to retrieve
   * @return the element, if it exists, or null otherwise
   * @throws IndexOutOfBoundsException if the index is out of bounds
   * (less than 0 or larger than the {@code SubList} size)
   */
  @SuppressWarnings("unchecked")
  E get(int index) {
      return (E) getValueFromReference(contents.get(index), false);
  }

  /**
   * Sets the value at the index provided. The index is not an absolute
   * index; rather, it is relative to the current list. If the index
   * does not correspond to a valid index in the underlying list, an
   * {@code IndexOutOfBoundsException} will be thrown.
   *
   * @param index the index to add the element
   * @param obj the element to be added
   * @return the old element that was replaced
   * @throws IndexOutOfBoundsException if the index is outside the range
   * of the underlying list
   */
  @SuppressWarnings("unchecked")
  E set(int index, Object obj) {
      assert (obj != null);

      AppContext.getDataManager().markForUpdate(this);

      // Wrap the element as an Element if is not already
      // a ManagedObject
      ManagedReference<ManagedObject> ref = createRefForAdd((E) obj);

      // Get the stored element value, depending on
      // what kind of value it was (ManagedObject or Element)
      return (E) getValueFromReference(contents.set(index, ref), true);
  }

  /**
   * Appends the supplied argument to the list.
   *
   * @param e the element to add to append
   * @return whether the operation was successful; {@code true} if so,
   * {@code false} otherwise
   */
  boolean append(E e) {
      assert (e != null);

      // If it is not yet a ManagedObject, then
      // create a new Element to make it a ManagedObject
      ManagedReference<ManagedObject> ref = createRefForAdd(e);
      AppContext.getDataManager().markForUpdate(this);
      return contents.add(ref);
  }

  /**
   * Returns the index of the element inside the {@code SubList<E>}. If
   * the element does not exist, then -1 is returned.
   *
   * @param o the element whose last index is to be found
   * @return the index of the element, or -1 if it does not exist
   */
  int lastIndexOf(Object o) {
      assert (o != null);
      Iterator<ManagedReference<ManagedObject>> iter =
        contents.iterator();
      int index = 0;
      int lastIndex = -1;

      // Iterate through all contents,
      // checking for equality
      while (iter.hasNext()) {
    ManagedReference<ManagedObject> ref = iter.next();
    Object obj = getValueFromReference(ref, false);
    if (o.equals(obj)) {
        lastIndex = index;
    }
    index++;
      }
      return lastIndex;
  }

  /**
   * Inserts the element into the list at a specified location. If the
   * index is not valid, an {@code IndexOutOfBoundsException} is thrown.
   *
   * @param index the index to add the new element.
   * @param e the object which is to be inserted at the specified
   * {@code index}
   * @throws IndexOutOfBoundsException if the supplied index is outside
   * of the range of the underlying list
   */
  void insert(int index, E e) {
      AppContext.getDataManager().markForUpdate(this);
      assert (e != null);
      if (index < 0) {
    throw new IndexOutOfBoundsException(
      "Supplied index cannot be less than 0");
      }
      ManagedReference<ManagedObject> ref = createRefForAdd(e);

      contents.add(index, ref);
  }

  /**
   * Sets up the ref {@code ManagedReference} so that it contains a
   * serialized {@code ManagedObject}.
   *
   * @param e the element to potentially wrap in an {@code Element}
   * @return a reference to the wrapped element, ready to be stored into
   * a {@code SubList}
   * @throws IllegalArgumentException if the element does not implement
   * the {@code Serializable} interface
   */
  private ManagedReference<ManagedObject> createRefForAdd(E e) {
      assert (e != null);
      ManagedReference<ManagedObject> ref = null;

      if (!(e instanceof Serializable)) {
    throw new IllegalArgumentException(
      "The element does not implement "
        + "the Serializable interface");
      }

      // Determine if we need to wrap the parameter or not.
      if (e instanceof ManagedObject) {
    ref =
      AppContext.getDataManager().createReference(
        (ManagedObject) e);
      } else {
    Element<E> element = new Element<E>(e);
    ref =
      AppContext.getDataManager().createReference(
        (ManagedObject) element);
      }
      return ref;
  }

  /**
   * Determines the index of the first occurrence of the supplied
   * argument. If the element does not exist, then -1 is returned.
   *
   * @param o the element whose index is to be searched
   * @return the first index of the supplied element, or -1 if it does
   * not exist in the list
   */
  int indexOf(Object o) {
      assert (o != null);
      Iterator<ManagedReference<ManagedObject>> iter =
        contents.iterator();
      int index = 0;

      // Iterate through all the list
      // contents until we find a match
      while (iter.hasNext()) {
    ManagedReference<ManagedObject> ref = iter.next();
    Object obj = getValueFromReference(ref, false);
    if (o.equals(obj)) {
        return index;
    }
    index++;
      }
      return -1;
  }

  /**
   * Removes the element at the supplied index. This method throws an
   * {@code IndexOutOfBoundsException} if the index does not exist in
   * the underlying list.
   *
   * @param index the index to remove
   * @return the object removed from the index
   * @throws IndexOutOfBoundsException if the index is outside of the
   * range of the underlying list
   */
  @SuppressWarnings("unchecked")
  E remove(int index) {
      if (index > contents.size() - 1) {
    throw new IndexOutOfBoundsException(
      "The index is out of bounds");
      }
      AppContext.getDataManager().markForUpdate(this);
      return (E) getValueFromReference(contents.remove(index), true);
  }

  /**
   * Removes the supplied object from the underlying list, if it exists.
   *
   * @param obj the element to remove from the list
   * @return whether the operation was successful; {@code true} if so,
   * {@code false} otherwise
   */
  boolean remove(Object obj) {

      Iterator<ManagedReference<ManagedObject>> iter =
        contents.iterator();

      // go through all the elements in this collection (a sublist)
      while (iter.hasNext()) {
    ManagedReference<ManagedObject> current = iter.next();
    Object object = getValueFromReference(current, false);

    if (obj.equals(object)) {
        // remove the ManagedReference from the sub list.
        // If the object that the ManagedReference was
        // pointing to is an Element object, remove it
        // from the data manager.
        AppContext.getDataManager().markForUpdate(this);
        iter.remove();
        Object stored = current.get();
        if (stored instanceof Element) {
      AppContext.getDataManager().removeObject(stored);
        }
        return true;
    }
      }
      return false;
  }
    }

    /**
     * An interface which unifies the concept of a {@code ListNode} and
     * {@code TreeNode} as elements within a {@code ScalableList}. This
     * interface avoids cast warnings when the identity of a given element is
     * not immediately known.
     *
     * @param <E> the type of element stored in the {@code ScalableList}
     */
    static interface Node<E> {

  /**
   * Retrieves the node's parent.
   *
   * @return the parent, or null if none exists.
   */
  TreeNode<E> getParent();

  /**
   * Sets the {@code Node}'s parent to the supplied argument.
   */
  void setParent(TreeNode<E> t);

  /**
   * The size of the node; that is, the sum of the sizes of its
   * immediate children.
   *
   * @return the size of this node.
   */
  int size();

  /**
   * Walks up the tree and removes the object and any of its parents.
   * This method is intended to be called during the
   * {@code AsynchronousClearTask} operation.
   */
  void clear();

  /**
   * Traverses the tree (recursively) in search of the ListNode<E>
   * which contains the index provided. If no ListNode<E> can be found,
   * then null is returned.
   *
   * @param currentValue the current index value at the beginning of
   * this current search
   * @param destIndex the absolute index of the desired element
   * @return the {@code ListNode} containing the absolute
   * {@code destIndex}
   * @throws IndexOutOfBoundsException if the index is out of bounds
   */
  SearchResult<E> search(int currentValue, int destIndex);

  /**
   * Returns the next {@code Node} in sequence, or null if none exists.
   *
   * @return the next node
   */
  Node<E> next();

  /**
   * Sets the next element to be the supplied argument. The argument
   * should be the same type as the variable.
   *
   * @param next the next {@code Node}
   */
  void setNext(Node<E> next);

  /**
   * Returns the previous {@code Node} in sequence, or null if none
   * exists.
   *
   * @return the previous node
   */
  Node<E> prev();

  /**
   * Sets the previous element to be the supplied argument. The argument
   * should be the same type as the variable.
   *
   * @param prev the previous {@code Node}
   */
  void setPrev(Node<E> prev);
    }

    /**
     * This class is responsible for returning the results of a search,
     * including the target {@code ListNode} and the offset. Since this object
     * is not Serializable, its intention is to be a temporary object whose
     * scope is simply within the tree search process. Therefore, it is
     * unnecessary to include mutator methods to adjust the entries; only
     * accessor methods are needed to retreive the stored values.
     */
    private static class SearchResult<E> {

  /**
   * The target node where the element lives
   */
  final ListNode<E> node;

  /**
   * The offset of the element within the {@code ListNode}'s
   * {@code SubList}
   */
  final int offset;

  /**
   * Constructor which creates a new SearchResult based on the target
   * node and the calculated offset. The offset is not an absolute
   * index, but rather the index of the element with respect to the
   * {@code SubList} of the {@code ListNode}.
   *
   * @param node the target node
   * @param offset the index of the element in this {@code ListNode}'s
   * {@code SubList}.
   */
  SearchResult(ListNode<E> node, int offset) {
      this.node = node;
      this.offset = offset;
  }
    }
}
TOP

Related Classes of com.sun.sgs.app.util.ScalableList$SearchResult

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.