1 package org.apache.turbine.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 * <TR>
154 * <TD>Key: $key</TD>
155 * </TD>Value: $table.get($key)</TD>
156 * </TR>
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
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 }