View Javadoc

1   package org.apache.turbine.util;
2   
3   /*
4    * Copyright 2001-2005 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License")
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.util.Collection;
20  import java.util.Hashtable;
21  import java.util.Iterator;
22  import java.util.LinkedList;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  /***
28   * A {@link java.util.Hashtable} whose keys are sequenced.  The
29   * sequencing of the keys allow easy access to the values in the order
30   * which they were added in.  This class is thread safe.
31   * <p>
32   * Implementing the List interface is not possible due to a instance
33   * method name clash between the Collection and the List interface:
34   *
35   * <table>
36   * <tr><td>Collections</td><td>boolean remove(Object o)</td></tr>
37   * <tr><td>Lists</td><td>Object remove(Object o)</td></tr>
38   * </table>
39   *
40   * So one cannot implement both interfaces at the same, which is unfortunate
41   * because the List interface would be very nice in conjuction with Velocity.
42   * <p>
43   * A slightly more complex implementation and interface could involve
44   * the use of a list of <code>Map.Entry</code> objects.
45   *
46   * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
47   * @version $Id: SequencedHashtable.java 264152 2005-08-29 14:50:22Z henning $
48   * @deprecated Use SequencedHashMap from the commons collections.
49   */
50  public class SequencedHashtable extends Hashtable
51  {
52      /***
53       * Indicator for an unknown index.
54       */
55      private static final int UNKNOWN_INDEX = -1;
56  
57      /***
58       * The sequence used to keep track of the hash keys.  Younger objects are
59       * kept towards the end of the list.  Does not allow duplicates.
60       */
61      private LinkedList keySequence;
62  
63      /***
64       * Creates a new instance with default storage.
65       */
66      public SequencedHashtable()
67      {
68          keySequence = new LinkedList();
69      }
70  
71      /***
72       * Creates a new instance with the specified storage.
73       *
74       * @param size The storage to allocate up front.
75       */
76      public SequencedHashtable(int size)
77      {
78          super(size);
79          keySequence = new LinkedList();
80      }
81  
82      /***
83       * Clears all elements.
84       */
85      public synchronized void clear()
86      {
87          super.clear();
88          keySequence.clear();
89      }
90  
91      /***
92       * Creates a shallow copy of this object, preserving the internal
93       * structure by copying only references.  The keys, values, and
94       * sequence are not <code>clone()</code>'d.
95       *
96       * @return A clone of this instance.
97       */
98      public synchronized Object clone()
99      {
100         SequencedHashtable seqHash = (SequencedHashtable) super.clone();
101         seqHash.keySequence = (LinkedList) keySequence.clone();
102         return seqHash;
103     }
104 
105     /***
106      * Returns the key at the specified index.
107      */
108     public Object get(int index)
109     {
110         return keySequence.get(index);
111     }
112 
113     /***
114      * Returns the value at the specified index.
115      */
116     public Object getValue(int index)
117     {
118         return get(get(index));
119     }
120 
121     /***
122      * Returns the index of the specified key.
123      */
124     public int indexOf(Object key)
125     {
126         return keySequence.indexOf(key);
127     }
128 
129     /***
130      * Returns a key iterator.
131      */
132     public Iterator iterator()
133     {
134         return keySequence.iterator();
135     }
136 
137     /***
138      * Returns the last index of the specified key.
139      */
140     public int lastIndexOf(Object key)
141     {
142         return keySequence.lastIndexOf(key);
143     }
144 
145     /***
146      * Returns the ordered sequence of keys.
147      *
148      * This method is meant to be used for retrieval of Key / Value pairs
149      * in e.g. Velocity:
150      * <PRE>
151      * ## $table contains a sequenced hashtable
152      * #foreach ($key in $table.sequence())
153      * &lt;TR&gt;
154      * &lt;TD&gt;Key: $key&lt;/TD&gt;
155      * &lt;/TD&gt;Value: $table.get($key)&lt;/TD&gt;
156      * &lt;/TR&gt;
157      * #end
158      * </PRE>
159      *
160      * @return The ordered list of keys.
161      */
162     public List sequence()
163     {
164         return keySequence;
165     }
166 
167     /***
168      * Stores the provided key/value pair.  Freshens the sequence of existing
169      * elements.
170      *
171      * @param key   The key to the provided value.
172      * @param value The value to store.
173      * @return      The previous value for the specified key, or
174      *              <code>null</code> if none.
175      */
176     public synchronized Object put(Object key, Object value)
177     {
178         Object prevValue = super.put(key, value);
179         freshenSequence(key, prevValue);
180         return prevValue;
181     }
182 
183     /***
184      * Freshens the sequence of the element <code>value</code> if
185      * <code>value</code> is not <code>null</code>.
186      *
187      * @param key   The key whose sequence to freshen.
188      * @param value The value whose existance to check before removing the old
189      *              key sequence.
190      */
191     protected void freshenSequence(Object key, Object value)
192     {
193         if (value != null)
194         {
195             // Freshening existing element's sequence.
196             keySequence.remove(key);
197         }
198         keySequence.add(key);
199     }
200 
201     /***
202      * Stores the provided key/value pairs.
203      *
204      * @param t The key/value pairs to store.
205      */
206     public synchronized void putAll(Map t)
207     {
208         Set set = t.entrySet();
209         for (Iterator iter = set.iterator(); iter.hasNext();)
210         {
211             Map.Entry e = (Map.Entry) iter.next();
212             put(e.getKey(), e.getValue());
213         }
214     }
215 
216     /***
217      * Removes the element at the specified index.
218      *
219      * @param index The index of the object to remove.
220      * @return      The previous value coressponding the <code>key</code>, or
221      *              <code>null</code> if none existed.
222      */
223     public Object remove(int index)
224     {
225         return remove(index, null);
226     }
227 
228     /***
229      * Removes the element with the specified key.
230      *
231      * @param key   The <code>Map</code> key of the object to remove.
232      * @return      The previous value coressponding the <code>key</code>, or
233      *              <code>null</code> if none existed.
234      */
235     public Object remove(Object key)
236     {
237         return remove(UNKNOWN_INDEX, key);
238     }
239 
240     /***
241      * Removes the element with the specified key or index.
242      *
243      * @param index The index of the object to remove, or
244      *              <code>UNKNOWN_INDEX</code> if not known.
245      * @param key   The <code>Map</code> key of the object to remove.
246      * @return      The previous value coressponding the <code>key</code>, or
247      *              <code>null</code> if none existed.
248      */
249     private synchronized Object remove(int index, Object key)
250     {
251         if (index == UNKNOWN_INDEX)
252         {
253             index = indexOf(key);
254         }
255         if (key == null)
256         {
257             key = get(index);
258         }
259         if (index != UNKNOWN_INDEX)
260         {
261             keySequence.remove(index);
262         }
263         return super.remove(key);
264     }
265 
266     /***
267      * Slightly cheaper implementation of <code>values()</code> method.
268      */
269     public Collection values()
270     {
271         return keySequence;
272     }
273 }