View Javadoc
1   /*
2    * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package javax.swing;
26  
27  import java.io.IOException;
28  import java.io.ObjectOutputStream;
29  import java.io.Serializable;
30  import java.util.Enumeration;
31  import java.util.Hashtable;
32  
33  /*
34   * Private storage mechanism for Action key-value pairs.
35   * In most cases this will be an array of alternating
36   * key-value pairs.  As it grows larger it is scaled
37   * up to a Hashtable.
38   * <p>
39   * This does no synchronization, if you need thread safety synchronize on
40   * another object before calling this.
41   *
42   * @author Georges Saab
43   * @author Scott Violet
44   */
45  class ArrayTable implements Cloneable {
46      // Our field for storage
47      private Object table = null;
48      private static final int ARRAY_BOUNDARY = 8;
49  
50  
51      /**
52       * Writes the passed in ArrayTable to the passed in ObjectOutputStream.
53       * The data is saved as an integer indicating how many key/value
54       * pairs are being archived, followed by the the key/value pairs. If
55       * <code>table</code> is null, 0 will be written to <code>s</code>.
56       * <p>
57       * This is a convenience method that ActionMap/InputMap and
58       * AbstractAction use to avoid having the same code in each class.
59       */
60      static void writeArrayTable(ObjectOutputStream s, ArrayTable table) throws IOException {
61          Object keys[];
62  
63          if (table == null || (keys = table.getKeys(null)) == null) {
64              s.writeInt(0);
65          }
66          else {
67              // Determine how many keys have Serializable values, when
68              // done all non-null values in keys identify the Serializable
69              // values.
70              int validCount = 0;
71  
72              for (int counter = 0; counter < keys.length; counter++) {
73                  Object key = keys[counter];
74  
75                  /* include in Serialization when both keys and values are Serializable */
76                  if (    (key instanceof Serializable
77                           && table.get(key) instanceof Serializable)
78                               ||
79                           /* include these only so that we get the appropriate exception below */
80                          (key instanceof ClientPropertyKey
81                           && ((ClientPropertyKey)key).getReportValueNotSerializable())) {
82  
83                      validCount++;
84                  } else {
85                      keys[counter] = null;
86                  }
87              }
88              // Write ou the Serializable key/value pairs.
89              s.writeInt(validCount);
90              if (validCount > 0) {
91                  for (Object key : keys) {
92                      if (key != null) {
93                          s.writeObject(key);
94                          s.writeObject(table.get(key));
95                          if (--validCount == 0) {
96                              break;
97                          }
98                      }
99                  }
100             }
101         }
102     }
103 
104 
105     /*
106      * Put the key-value pair into storage
107      */
108     public void put(Object key, Object value){
109         if (table==null) {
110             table = new Object[] {key, value};
111         } else {
112             int size = size();
113             if (size < ARRAY_BOUNDARY) {              // We are an array
114                 if (containsKey(key)) {
115                     Object[] tmp = (Object[])table;
116                     for (int i = 0; i<tmp.length-1; i+=2) {
117                         if (tmp[i].equals(key)) {
118                             tmp[i+1]=value;
119                             break;
120                         }
121                     }
122                 } else {
123                     Object[] array = (Object[])table;
124                     int i = array.length;
125                     Object[] tmp = new Object[i+2];
126                     System.arraycopy(array, 0, tmp, 0, i);
127 
128                     tmp[i] = key;
129                     tmp[i+1] = value;
130                     table = tmp;
131                 }
132             } else {                 // We are a hashtable
133                 if ((size==ARRAY_BOUNDARY) && isArray()) {
134                     grow();
135                 }
136                 ((Hashtable<Object,Object>)table).put(key, value);
137             }
138         }
139     }
140 
141     /*
142      * Gets the value for key
143      */
144     public Object get(Object key) {
145         Object value = null;
146         if (table !=null) {
147             if (isArray()) {
148                 Object[] array = (Object[])table;
149                 for (int i = 0; i<array.length-1; i+=2) {
150                     if (array[i].equals(key)) {
151                         value = array[i+1];
152                         break;
153                     }
154                 }
155             } else {
156                 value = ((Hashtable)table).get(key);
157             }
158         }
159         return value;
160     }
161 
162     /*
163      * Returns the number of pairs in storage
164      */
165     public int size() {
166         int size;
167         if (table==null)
168             return 0;
169         if (isArray()) {
170             size = ((Object[])table).length/2;
171         } else {
172             size = ((Hashtable)table).size();
173         }
174         return size;
175     }
176 
177     /*
178      * Returns true if we have a value for the key
179      */
180     public boolean containsKey(Object key) {
181         boolean contains = false;
182         if (table !=null) {
183             if (isArray()) {
184                 Object[] array = (Object[])table;
185                 for (int i = 0; i<array.length-1; i+=2) {
186                     if (array[i].equals(key)) {
187                         contains = true;
188                         break;
189                     }
190                 }
191             } else {
192                 contains = ((Hashtable)table).containsKey(key);
193             }
194         }
195         return contains;
196     }
197 
198     /*
199      * Removes the key and its value
200      * Returns the value for the pair removed
201      */
202     public Object remove(Object key){
203         Object value = null;
204         if (key==null) {
205             return null;
206         }
207         if (table !=null) {
208             if (isArray()){
209                 // Is key on the list?
210                 int index = -1;
211                 Object[] array = (Object[])table;
212                 for (int i = array.length-2; i>=0; i-=2) {
213                     if (array[i].equals(key)) {
214                         index = i;
215                         value = array[i+1];
216                         break;
217                     }
218                 }
219 
220                 // If so,  remove it
221                 if (index != -1) {
222                     Object[] tmp = new Object[array.length-2];
223                     // Copy the list up to index
224                     System.arraycopy(array, 0, tmp, 0, index);
225                     // Copy from two past the index, up to
226                     // the end of tmp (which is two elements
227                     // shorter than the old list)
228                     if (index < tmp.length)
229                         System.arraycopy(array, index+2, tmp, index,
230                                          tmp.length - index);
231                     // set the listener array to the new array or null
232                     table = (tmp.length == 0) ? null : tmp;
233                 }
234             } else {
235                 value = ((Hashtable)table).remove(key);
236             }
237             if (size()==ARRAY_BOUNDARY - 1 && !isArray()) {
238                 shrink();
239             }
240         }
241         return value;
242     }
243 
244     /**
245      * Removes all the mappings.
246      */
247     public void clear() {
248         table = null;
249     }
250 
251     /*
252      * Returns a clone of the <code>ArrayTable</code>.
253      */
254     public Object clone() {
255         ArrayTable newArrayTable = new ArrayTable();
256         if (isArray()) {
257             Object[] array = (Object[])table;
258             for (int i = 0 ;i < array.length-1 ; i+=2) {
259                 newArrayTable.put(array[i], array[i+1]);
260             }
261         } else {
262             Hashtable<?,?> tmp = (Hashtable)table;
263             Enumeration<?> keys = tmp.keys();
264             while (keys.hasMoreElements()) {
265                 Object o = keys.nextElement();
266                 newArrayTable.put(o,tmp.get(o));
267             }
268         }
269         return newArrayTable;
270     }
271 
272     /**
273      * Returns the keys of the table, or <code>null</code> if there
274      * are currently no bindings.
275      * @param keys  array of keys
276      * @return an array of bindings
277      */
278     public Object[] getKeys(Object[] keys) {
279         if (table == null) {
280             return null;
281         }
282         if (isArray()) {
283             Object[] array = (Object[])table;
284             if (keys == null) {
285                 keys = new Object[array.length / 2];
286             }
287             for (int i = 0, index = 0 ;i < array.length-1 ; i+=2,
288                      index++) {
289                 keys[index] = array[i];
290             }
291         } else {
292             Hashtable<?,?> tmp = (Hashtable)table;
293             Enumeration<?> enum_ = tmp.keys();
294             int counter = tmp.size();
295             if (keys == null) {
296                 keys = new Object[counter];
297             }
298             while (counter > 0) {
299                 keys[--counter] = enum_.nextElement();
300             }
301         }
302         return keys;
303     }
304 
305     /*
306      * Returns true if the current storage mechanism is
307      * an array of alternating key-value pairs.
308      */
309     private boolean isArray(){
310         return (table instanceof Object[]);
311     }
312 
313     /*
314      * Grows the storage from an array to a hashtable.
315      */
316     private void grow() {
317         Object[] array = (Object[])table;
318         Hashtable<Object, Object> tmp = new Hashtable<Object, Object>(array.length/2);
319         for (int i = 0; i<array.length; i+=2) {
320             tmp.put(array[i], array[i+1]);
321         }
322         table = tmp;
323     }
324 
325     /*
326      * Shrinks the storage from a hashtable to an array.
327      */
328     private void shrink() {
329         Hashtable<?,?> tmp = (Hashtable)table;
330         Object[] array = new Object[tmp.size()*2];
331         Enumeration<?> keys = tmp.keys();
332         int j = 0;
333 
334         while (keys.hasMoreElements()) {
335             Object o = keys.nextElement();
336             array[j] = o;
337             array[j+1] = tmp.get(o);
338             j+=2;
339         }
340         table = array;
341     }
342 }