1   /*
2    *  Copyright (c) 1998-2001, The University of Sheffield.
3    *
4    *  This file is part of GATE (see http://gate.ac.uk/), and is free
5    *  software, licenced under the GNU Library General Public License,
6    *  Version 2, June 1991 (in the distribution as file licence.html,
7    *  and also available at http://gate.ac.uk/gate/licence.html).
8    *
9    *  Valentin Tablan, 10/Oct/2001
10   *
11   *  $Id: WeakBumpyStack.java,v 1.4 2001/11/24 18:23:24 hamish Exp $
12   */
13  
14  ////////////////////////////////////////////////////////////////////
15  //////////// DEVELOPERS: SEE WARNING IN JAVADOC COMMENT FOR
16  //////////// THIS CLASS!!!!
17  ////////////////////////////////////////////////////////////////////
18  
19  package gate.util;
20  
21  import java.util.*;
22  import java.lang.ref.*;
23  
24  import gate.*;
25  
26  /**
27   * Weak stack that allow you to bump an element to the front.
28   * Objects that are only referenced by this stack will be candidates for
29   * garbage collection and wil be removed from the stack as soon as the garbage
30   * collector marks them for collection.
31   * <P>
32   * <B>*** WARNING: ***</B> the test for this class,
33   * <TT>TestBumpyStack.testSelfCleaning</TT> is not a proper test; it doesn't
34   * fail even when it should, and only prints a warning when DEBUG is true.
35   * This is because to test it properly you need to force garbage collection,
36   * and that isn't possible. So, if you work on this class <B>you must
37   * turn DEBUG on on TestBumpyStack</B> in order to run the tests in a
38   * meaningfull way.
39   */
40  public class WeakBumpyStack extends AbstractList
41  {
42  
43    /**
44     * Creates a new empty stack.
45     */
46    public WeakBumpyStack(){
47      supportStack = new Stack();
48      refQueue = new ReferenceQueue();
49    }
50  
51    /**
52     * Pushes an item onto the top of this stack. This has exactly
53     * the same effect as:
54     * <blockquote><pre>
55     * addElement(item)</pre></blockquote>
56     *
57     * @param   item   the item to be pushed onto this stack.
58     * @return  the <code>item</code> argument.
59     */
60    public Object push(Object item){
61      supportStack.push(new WeakReference(item,refQueue));
62      return item;
63    }
64  
65    /**
66     * Removes the object at the top of this stack and returns that
67     * object as the value of this function.
68     *
69     * @return     The object at the top of this stack.
70     * @exception  EmptyStackException  if this stack is empty.
71     */
72    public synchronized Object pop(){
73      processQueue();
74      //we need to check for null in case the top reference has just been cleared
75      Object res = null;
76      while(res == null){
77        res = ((WeakReference)supportStack.pop()).get();
78      }
79      return res;
80    }
81  
82  
83    /**
84     * Looks at the object at the top of this stack without removing it
85     * from the stack.
86     *
87     * @return     the object at the top of this stack.
88     * @exception  EmptyStackException  if this stack is empty.
89     */
90    public synchronized Object peek(){
91      processQueue();
92      //we need to check for null in case the top reference has just been cleared
93      Object res = null;
94      while(res == null){
95        res = ((WeakReference)supportStack.peek()).get();
96      }
97      return res;
98    }
99  
100   /**
101    * Tests if this stack is empty.
102    *
103    * @return  <code>true</code> if and only if this stack contains
104    *          no items; <code>false</code> otherwise.
105    */
106   public boolean empty() {
107     processQueue();
108     return supportStack.empty();
109   }
110 
111 
112   /** Bump an item to the front of the stack.
113     * @param item the item to bump
114     * @return true when the item was found, else false
115     */
116   public boolean bump(Object item) {
117     processQueue();
118 
119     int itemIndex = search(item);
120 
121     if(itemIndex == -1) // not a member of the stack
122       return false;
123     else if(itemIndex == 1) // at the front already
124       return true;
125 
126     WeakReference wr = (WeakReference)supportStack.remove(itemIndex - 1);
127     supportStack.push(wr);
128     return true;
129   } // bump
130 
131   /**
132    * Returns the 1-based position where an object is on this stack.
133    * If the object <tt>o</tt> occurs as an item in this stack, this
134    * method returns the distance from the top of the stack of the
135    * occurrence nearest the top of the stack; the topmost item on the
136    * stack is considered to be at distance <tt>1</tt>. The <tt>equals</tt>
137    * method is used to compare <tt>o</tt> to the
138    * items in this stack.
139    *
140    * @param   o   the desired object.
141    * @return  the 1-based position from the top of the stack where
142    *          the object is located; the return value <code>-1</code>
143    *          indicates that the object is not on the stack.
144    */
145   public synchronized int search(Object o) {
146     processQueue();
147     int i = supportStack.size() - 1;
148     while(i >= 0 &&
149           !((WeakReference)supportStack.get(i)).get().equals(o)) i--;
150     if (i >= 0) {
151       return supportStack.size() - i;
152     }
153     return -1;
154   }
155 
156 
157   /**
158    * Checks the queue for any new weak references that have been cleared and
159    * queued and removes them from the underlying stack.
160    *
161    * This method should be called by every public method before realising its
162    * internal logic.
163    */
164   protected void processQueue(){
165     WeakReference wr;
166     while ((wr = (WeakReference)refQueue.poll()) != null) {
167       supportStack.remove(wr);
168     }
169   }
170 
171   /**
172    * Returns the element at the specified position in this list.
173    *
174    * @param  index index of element to return.
175    * @return the element at the specified position in this list.
176    * @throws    IndexOutOfBoundsException if index is out of range <tt>(index
177    *      &lt; 0 || index &gt;= size())</tt>.
178    */
179   public Object get(int index) {
180     processQueue();
181     //we need to check for null in case the top reference has just been cleared
182     Object res = null;
183     while(res == null){
184       res = ((WeakReference)supportStack.get(index)).get();
185     }
186     return res;
187   }
188 
189   /**
190    * Returns the number of elements in this list.
191    *
192    * @return  the number of elements in this list.
193    */
194   public int size() {
195     processQueue();
196     return supportStack.size();
197   }
198 
199   /**
200    * Replaces the element at the specified position in this list with
201    * the specified element.
202    *
203    * @param index index of element to replace.
204    * @param element element to be stored at the specified position.
205    * @return the element previously at the specified position.
206    * @throws    IndexOutOfBoundsException if index out of range
207    *      <tt>(index &lt; 0 || index &gt;= size())</tt>.
208    */
209   public Object set(int index, Object element) {
210     processQueue();
211     WeakReference ref = (WeakReference)
212                         supportStack.set(index,
213                                          new WeakReference(element, refQueue));
214     return ref.get();
215   }
216 
217   /**
218    * Inserts the specified element at the specified position in this
219    * list. Shifts the element currently at that position (if any) and
220    * any subsequent elements to the right (adds one to their indices).
221    *
222    * @param index index at which the specified element is to be inserted.
223    * @param element element to be inserted.
224    * @throws    IndexOutOfBoundsException if index is out of range
225    *      <tt>(index &lt; 0 || index &gt; size())</tt>.
226    */
227   public void add(int index, Object element) {
228     processQueue();
229     supportStack.add(index, new WeakReference(element, refQueue));
230   }
231 
232   /**
233    * Removes the element at the specified position in this list.
234    * Shifts any subsequent elements to the left (subtracts one from their
235    * indices).
236    *
237    * @param index the index of the element to removed.
238    * @return the element that was removed from the list.
239    * @throws    IndexOutOfBoundsException if index out of range <tt>(index
240    *      &lt; 0 || index &gt;= size())</tt>.
241    */
242   public Object remove(int index) {
243     processQueue();
244     //we need to check for null in case the top reference has just been cleared
245     Object res = null;
246     while(res == null){
247       res = ((WeakReference)supportStack.remove(index)).get();
248     }
249     return res;
250   }
251 
252   ReferenceQueue refQueue;
253 
254   /**
255    * This is the underlying stack object for this weak stack. It holds weak
256    * references to the objects that are the actual contents of this stack.
257    */
258   Stack supportStack;
259 } // class BumpyStack
260