001 /*
002 * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing;
026
027 import java.io.IOException;
028 import java.io.ObjectOutputStream;
029 import java.io.Serializable;
030 import java.util.Enumeration;
031 import java.util.Hashtable;
032
033 /*
034 * Private storage mechanism for Action key-value pairs.
035 * In most cases this will be an array of alternating
036 * key-value pairs. As it grows larger it is scaled
037 * up to a Hashtable.
038 * <p>
039 * This does no synchronization, if you need thread safety synchronize on
040 * another object before calling this.
041 *
042 * @version 1.12 05/05/07
043 * @author Georges Saab
044 * @author Scott Violet
045 */
046 class ArrayTable implements Cloneable {
047 // Our field for storage
048 private Object table = null;
049 private static final int ARRAY_BOUNDARY = 8;
050
051 /**
052 * Writes the passed in ArrayTable to the passed in ObjectOutputStream.
053 * The data is saved as an integer indicating how many key/value
054 * pairs are being archived, followed by the the key/value pairs. If
055 * <code>table</code> is null, 0 will be written to <code>s</code>.
056 * <p>
057 * This is a convenience method that ActionMap/InputMap and
058 * AbstractAction use to avoid having the same code in each class.
059 */
060 static void writeArrayTable(ObjectOutputStream s, ArrayTable table)
061 throws IOException {
062 Object keys[];
063
064 if (table == null || (keys = table.getKeys(null)) == null) {
065 s.writeInt(0);
066 } else {
067 // Determine how many keys have Serializable values, when
068 // done all non-null values in keys identify the Serializable
069 // values.
070 int validCount = 0;
071
072 for (int counter = 0; counter < keys.length; counter++) {
073 Object key = keys[counter];
074
075 /* include in Serialization when both keys and values are Serializable */
076 if ((key instanceof Serializable && table.get(key) instanceof Serializable)
077 ||
078 /* include these only so that we get the appropriate exception below */
079 (key instanceof ClientPropertyKey && ((ClientPropertyKey) key)
080 .getReportValueNotSerializable())) {
081
082 validCount++;
083 } else {
084 keys[counter] = null;
085 }
086 }
087 // Write ou the Serializable key/value pairs.
088 s.writeInt(validCount);
089 if (validCount > 0) {
090 for (int counter = 0; counter < keys.length; counter++) {
091 if (keys[counter] != null) {
092 s.writeObject(keys[counter]);
093 s.writeObject(table.get(keys[counter]));
094 if (--validCount == 0) {
095 break;
096 }
097 }
098 }
099 }
100 }
101 }
102
103 /*
104 * Put the key-value pair into storage
105 */
106 public void put(Object key, Object value) {
107 if (table == null) {
108 table = new Object[] { key, value };
109 } else {
110 int size = size();
111 if (size < ARRAY_BOUNDARY) { // We are an array
112 if (containsKey(key)) {
113 Object[] tmp = (Object[]) table;
114 for (int i = 0; i < tmp.length - 1; i += 2) {
115 if (tmp[i].equals(key)) {
116 tmp[i + 1] = value;
117 break;
118 }
119 }
120 } else {
121 Object[] array = (Object[]) table;
122 int i = array.length;
123 Object[] tmp = new Object[i + 2];
124 System.arraycopy(array, 0, tmp, 0, i);
125
126 tmp[i] = key;
127 tmp[i + 1] = value;
128 table = tmp;
129 }
130 } else { // We are a hashtable
131 if ((size == ARRAY_BOUNDARY) && isArray()) {
132 grow();
133 }
134 ((Hashtable) table).put(key, value);
135 }
136 }
137 }
138
139 /*
140 * Gets the value for key
141 */
142 public Object get(Object key) {
143 Object value = null;
144 if (table != null) {
145 if (isArray()) {
146 Object[] array = (Object[]) table;
147 for (int i = 0; i < array.length - 1; i += 2) {
148 if (array[i].equals(key)) {
149 value = array[i + 1];
150 break;
151 }
152 }
153 } else {
154 value = ((Hashtable) table).get(key);
155 }
156 }
157 return value;
158 }
159
160 /*
161 * Returns the number of pairs in storage
162 */
163 public int size() {
164 int size;
165 if (table == null)
166 return 0;
167 if (isArray()) {
168 size = ((Object[]) table).length / 2;
169 } else {
170 size = ((Hashtable) table).size();
171 }
172 return size;
173 }
174
175 /*
176 * Returns true if we have a value for the key
177 */
178 public boolean containsKey(Object key) {
179 boolean contains = false;
180 if (table != null) {
181 if (isArray()) {
182 Object[] array = (Object[]) table;
183 for (int i = 0; i < array.length - 1; i += 2) {
184 if (array[i].equals(key)) {
185 contains = true;
186 break;
187 }
188 }
189 } else {
190 contains = ((Hashtable) table).containsKey(key);
191 }
192 }
193 return contains;
194 }
195
196 /*
197 * Removes the key and its value
198 * Returns the value for the pair removed
199 */
200 public Object remove(Object key) {
201 Object value = null;
202 if (key == null) {
203 return null;
204 }
205 if (table != null) {
206 if (isArray()) {
207 // Is key on the list?
208 int index = -1;
209 Object[] array = (Object[]) table;
210 for (int i = array.length - 2; i >= 0; i -= 2) {
211 if (array[i].equals(key)) {
212 index = i;
213 value = array[i + 1];
214 break;
215 }
216 }
217
218 // If so, remove it
219 if (index != -1) {
220 Object[] tmp = new Object[array.length - 2];
221 // Copy the list up to index
222 System.arraycopy(array, 0, tmp, 0, index);
223 // Copy from two past the index, up to
224 // the end of tmp (which is two elements
225 // shorter than the old list)
226 if (index < tmp.length)
227 System.arraycopy(array, index + 2, tmp, index,
228 tmp.length - index);
229 // set the listener array to the new array or null
230 table = (tmp.length == 0) ? null : tmp;
231 }
232 } else {
233 value = ((Hashtable) table).remove(key);
234 }
235 if (size() == ARRAY_BOUNDARY - 1 && !isArray()) {
236 shrink();
237 }
238 }
239 return value;
240 }
241
242 /**
243 * Removes all the mappings.
244 */
245 public void clear() {
246 table = null;
247 }
248
249 /*
250 * Returns a clone of the <code>ArrayTable</code>.
251 */
252 public Object clone() {
253 ArrayTable newArrayTable = new ArrayTable();
254 if (isArray()) {
255 Object[] array = (Object[]) table;
256 for (int i = 0; i < array.length - 1; i += 2) {
257 newArrayTable.put(array[i], array[i + 1]);
258 }
259 } else {
260 Hashtable tmp = (Hashtable) table;
261 Enumeration keys = tmp.keys();
262 while (keys.hasMoreElements()) {
263 Object o = keys.nextElement();
264 newArrayTable.put(o, tmp.get(o));
265 }
266 }
267 return newArrayTable;
268 }
269
270 /**
271 * Returns the keys of the table, or <code>null</code> if there
272 * are currently no bindings.
273 * @param keys array of keys
274 * @return an array of bindings
275 */
276 public Object[] getKeys(Object[] keys) {
277 if (table == null) {
278 return null;
279 }
280 if (isArray()) {
281 Object[] array = (Object[]) table;
282 if (keys == null) {
283 keys = new Object[array.length / 2];
284 }
285 for (int i = 0, index = 0; i < array.length - 1; i += 2, index++) {
286 keys[index] = array[i];
287 }
288 } else {
289 Hashtable tmp = (Hashtable) table;
290 Enumeration enum_ = tmp.keys();
291 int counter = tmp.size();
292 if (keys == null) {
293 keys = new Object[counter];
294 }
295 while (counter > 0) {
296 keys[--counter] = enum_.nextElement();
297 }
298 }
299 return keys;
300 }
301
302 /*
303 * Returns true if the current storage mechanism is
304 * an array of alternating key-value pairs.
305 */
306 private boolean isArray() {
307 return (table instanceof Object[]);
308 }
309
310 /*
311 * Grows the storage from an array to a hashtable.
312 */
313 private void grow() {
314 Object[] array = (Object[]) table;
315 Hashtable tmp = new Hashtable(array.length / 2);
316 for (int i = 0; i < array.length; i += 2) {
317 tmp.put(array[i], array[i + 1]);
318 }
319 table = tmp;
320 }
321
322 /*
323 * Shrinks the storage from a hashtable to an array.
324 */
325 private void shrink() {
326 Hashtable tmp = (Hashtable) table;
327 Object[] array = new Object[tmp.size() * 2];
328 Enumeration keys = tmp.keys();
329 int j = 0;
330
331 while (keys.hasMoreElements()) {
332 Object o = keys.nextElement();
333 array[j] = o;
334 array[j + 1] = tmp.get(o);
335 j += 2;
336 }
337 table = array;
338 }
339 }
|