/* NamedStack.java A Stack object in which items on the stack are given names, to allow items on the stack at or after the occurrence of a given name to be popped in one operation. Created: 2 October 2000 Module By: Jonathan Abbey, jonabbey@arlut.utexas.edu ----------------------------------------------------------------------- Ganymede Directory Management System Copyright (C) 1996-2013 The University of Texas at Austin Ganymede is a registered trademark of The University of Texas at Austin Contact information Web site: http://www.arlut.utexas.edu/gash2 Author Email: ganymede_author@arlut.utexas.edu Email mailing list: ganymede@arlut.utexas.edu US Mail: Computer Science Division Applied Research Laboratories The University of Texas at Austin PO Box 8029, Austin TX 78713-8029 Telephone: (512) 835-3200 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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/>. */ package arlut.csd.Util; import java.util.Stack; /*------------------------------------------------------------------------------ class NamedStack ------------------------------------------------------------------------------*/ /** * <p>NamedStack is a data structure that allows named items to be placed on a * stack. When it comes time to pop data off the stack, the pop operation takes * the name of the item to be popped, and all items pushed on the stack after * the named item will also be popped.</p> * * <p>That is, NamedStack is basically a Stack that supports rollback to a named * state. It is used in the Ganymede server to support checkpoint operations.</p> * * <p>NamedStack does not require that names of pushed items be unique. A pop * operation will simply pop up to the last item matching the given name off * the stack.</p> */ final public class NamedStack<E> { private Stack<NamedStackHandle<E>> stack; /* -- */ public NamedStack() { stack = new Stack<NamedStackHandle<E>>(); } /** * <p>push pushes a named item onto the stack.</p> */ public synchronized E push(String name, E item) { stack.push(new NamedStackHandle<E>(name, item)); return item; } /** * <p>pop rolls the state of the stack back to the time just before * the top-most named object matching name was pushed.</p> * * @return The top-most named object matching name. */ public synchronized E pop(String name) { // okay, i'm a bad person. i micro-optimized this to avoid having // to do a few name equality tests twice. i figure everything's // nice, private, and synchronized, so this is a safe enough thing // to do. but you can spank me if you want to. int i = findName(name); if (i == -1) { return null; } for (int j = stack.size()-1; j > i; j--) { stack.pop(); } return stack.pop().getData(); } /** * <p>This is just plain old pop, it will return the top element * without regard to its name.</p> */ public synchronized E pop() { if (stack.size() == 0) { return null; } return stack.pop().getData(); } public E elementAt(int index) { NamedStackHandle<E> handle = stack.elementAt(index); if (handle == null) { return null; } return handle.getData(); } public String nameAt(int index) { NamedStackHandle handle = stack.elementAt(index); if (handle == null) { return null; } return handle.getName(); } public int size() { return stack.size(); } public void clear() { this.removeAllElements(); } public synchronized void removeAllElements() { stack.removeAllElements(); } /** * <p>This method returns the name associated with the last item pushed * onto this name stack, without altering the stack.</p> */ public synchronized String getTopName() { if (stack.size() == 0) { return null; } return stack.elementAt(stack.size()-1).getName(); } /** * <p>This method returns the object associated with the last item pushed * onto this name stack, without altering the stack.</p> */ public synchronized E getTopObject() { if (stack.size() == 0) { return null; } return stack.elementAt(stack.size()-1).getData(); } /** * <p>findName() returns the top-most index at which the name is * present in the named object stack, or -1 if the name was not found.</p> */ public synchronized int findName(String name) { for (int i = stack.size()-1; i >= 0; i--) { if (stack.elementAt(i).getName().equals(name)) { return i; } } return -1; } public boolean empty() { return stack.empty(); } public synchronized String toString() { StringBuilder result = new StringBuilder(); /* -- */ for (int i = stack.size()-1; i >= 0; i--) { NamedStackHandle handle = stack.elementAt(i); result.append(i); result.append(" : "); result.append(handle.getName()); result.append(" -- "); result.append(handle.getData().toString()); result.append("\n"); } return result.toString(); } } /** * <p>This class is used to associate a name String with an object for use with the * {@link arlut.csd.Util.NamedStack NamedStack} data structure.</p> */ final class NamedStackHandle<E> { private String name; private E data; /* -- */ public NamedStackHandle(String name, E data) { if (name == null) { throw new IllegalArgumentException("NamedStackHandle: null name"); } if (data == null) { throw new IllegalArgumentException("NamedStackHandle: null data"); } this.name = name; this.data = data; } public String getName() { return name; } public E getData() { return data; } }