| /* |
| * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package javax.swing; |
| |
| import java.io.IOException; |
| import java.io.ObjectOutputStream; |
| import java.io.Serializable; |
| import java.util.Enumeration; |
| import java.util.Hashtable; |
| |
| /* |
| * Private storage mechanism for Action key-value pairs. |
| * In most cases this will be an array of alternating |
| * key-value pairs. As it grows larger it is scaled |
| * up to a Hashtable. |
| * <p> |
| * This does no synchronization, if you need thread safety synchronize on |
| * another object before calling this. |
| * |
| * @author Georges Saab |
| * @author Scott Violet |
| */ |
| class ArrayTable implements Cloneable { |
| // Our field for storage |
| private Object table = null; |
| private static final int ARRAY_BOUNDARY = 8; |
| |
| |
| /** |
| * Writes the passed in ArrayTable to the passed in ObjectOutputStream. |
| * The data is saved as an integer indicating how many key/value |
| * pairs are being archived, followed by the key/value pairs. If |
| * <code>table</code> is null, 0 will be written to <code>s</code>. |
| * <p> |
| * This is a convenience method that ActionMap/InputMap and |
| * AbstractAction use to avoid having the same code in each class. |
| */ |
| static void writeArrayTable(ObjectOutputStream s, ArrayTable table) throws IOException { |
| Object keys[]; |
| |
| if (table == null || (keys = table.getKeys(null)) == null) { |
| s.writeInt(0); |
| } |
| else { |
| // Determine how many keys have Serializable values, when |
| // done all non-null values in keys identify the Serializable |
| // values. |
| int validCount = 0; |
| |
| for (int counter = 0; counter < keys.length; counter++) { |
| Object key = keys[counter]; |
| |
| /* include in Serialization when both keys and values are Serializable */ |
| if ( (key instanceof Serializable |
| && table.get(key) instanceof Serializable) |
| || |
| /* include these only so that we get the appropriate exception below */ |
| (key instanceof ClientPropertyKey |
| && ((ClientPropertyKey)key).getReportValueNotSerializable())) { |
| |
| validCount++; |
| } else { |
| keys[counter] = null; |
| } |
| } |
| // Write ou the Serializable key/value pairs. |
| s.writeInt(validCount); |
| if (validCount > 0) { |
| for (Object key : keys) { |
| if (key != null) { |
| s.writeObject(key); |
| s.writeObject(table.get(key)); |
| if (--validCount == 0) { |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /* |
| * Put the key-value pair into storage |
| */ |
| public void put(Object key, Object value){ |
| if (table==null) { |
| table = new Object[] {key, value}; |
| } else { |
| int size = size(); |
| if (size < ARRAY_BOUNDARY) { // We are an array |
| if (containsKey(key)) { |
| Object[] tmp = (Object[])table; |
| for (int i = 0; i<tmp.length-1; i+=2) { |
| if (tmp[i].equals(key)) { |
| tmp[i+1]=value; |
| break; |
| } |
| } |
| } else { |
| Object[] array = (Object[])table; |
| int i = array.length; |
| Object[] tmp = new Object[i+2]; |
| System.arraycopy(array, 0, tmp, 0, i); |
| |
| tmp[i] = key; |
| tmp[i+1] = value; |
| table = tmp; |
| } |
| } else { // We are a hashtable |
| if ((size==ARRAY_BOUNDARY) && isArray()) { |
| grow(); |
| } |
| @SuppressWarnings("unchecked") |
| Hashtable<Object,Object> tmp = (Hashtable<Object,Object>)table; |
| tmp.put(key, value); |
| } |
| } |
| } |
| |
| /* |
| * Gets the value for key |
| */ |
| public Object get(Object key) { |
| Object value = null; |
| if (table !=null) { |
| if (isArray()) { |
| Object[] array = (Object[])table; |
| for (int i = 0; i<array.length-1; i+=2) { |
| if (array[i].equals(key)) { |
| value = array[i+1]; |
| break; |
| } |
| } |
| } else { |
| value = ((Hashtable)table).get(key); |
| } |
| } |
| return value; |
| } |
| |
| /* |
| * Returns the number of pairs in storage |
| */ |
| public int size() { |
| int size; |
| if (table==null) |
| return 0; |
| if (isArray()) { |
| size = ((Object[])table).length/2; |
| } else { |
| size = ((Hashtable)table).size(); |
| } |
| return size; |
| } |
| |
| /* |
| * Returns true if we have a value for the key |
| */ |
| public boolean containsKey(Object key) { |
| boolean contains = false; |
| if (table !=null) { |
| if (isArray()) { |
| Object[] array = (Object[])table; |
| for (int i = 0; i<array.length-1; i+=2) { |
| if (array[i].equals(key)) { |
| contains = true; |
| break; |
| } |
| } |
| } else { |
| contains = ((Hashtable)table).containsKey(key); |
| } |
| } |
| return contains; |
| } |
| |
| /* |
| * Removes the key and its value |
| * Returns the value for the pair removed |
| */ |
| public Object remove(Object key){ |
| Object value = null; |
| if (key==null) { |
| return null; |
| } |
| if (table !=null) { |
| if (isArray()){ |
| // Is key on the list? |
| int index = -1; |
| Object[] array = (Object[])table; |
| for (int i = array.length-2; i>=0; i-=2) { |
| if (array[i].equals(key)) { |
| index = i; |
| value = array[i+1]; |
| break; |
| } |
| } |
| |
| // If so, remove it |
| if (index != -1) { |
| Object[] tmp = new Object[array.length-2]; |
| // Copy the list up to index |
| System.arraycopy(array, 0, tmp, 0, index); |
| // Copy from two past the index, up to |
| // the end of tmp (which is two elements |
| // shorter than the old list) |
| if (index < tmp.length) |
| System.arraycopy(array, index+2, tmp, index, |
| tmp.length - index); |
| // set the listener array to the new array or null |
| table = (tmp.length == 0) ? null : tmp; |
| } |
| } else { |
| value = ((Hashtable)table).remove(key); |
| } |
| if (size()==ARRAY_BOUNDARY - 1 && !isArray()) { |
| shrink(); |
| } |
| } |
| return value; |
| } |
| |
| /** |
| * Removes all the mappings. |
| */ |
| public void clear() { |
| table = null; |
| } |
| |
| /* |
| * Returns a clone of the <code>ArrayTable</code>. |
| */ |
| public Object clone() { |
| ArrayTable newArrayTable = new ArrayTable(); |
| if (isArray()) { |
| Object[] array = (Object[])table; |
| for (int i = 0 ;i < array.length-1 ; i+=2) { |
| newArrayTable.put(array[i], array[i+1]); |
| } |
| } else { |
| Hashtable<?,?> tmp = (Hashtable)table; |
| Enumeration<?> keys = tmp.keys(); |
| while (keys.hasMoreElements()) { |
| Object o = keys.nextElement(); |
| newArrayTable.put(o,tmp.get(o)); |
| } |
| } |
| return newArrayTable; |
| } |
| |
| /** |
| * Returns the keys of the table, or <code>null</code> if there |
| * are currently no bindings. |
| * @param keys array of keys |
| * @return an array of bindings |
| */ |
| public Object[] getKeys(Object[] keys) { |
| if (table == null) { |
| return null; |
| } |
| if (isArray()) { |
| Object[] array = (Object[])table; |
| if (keys == null) { |
| keys = new Object[array.length / 2]; |
| } |
| for (int i = 0, index = 0 ;i < array.length-1 ; i+=2, |
| index++) { |
| keys[index] = array[i]; |
| } |
| } else { |
| Hashtable<?,?> tmp = (Hashtable)table; |
| Enumeration<?> enum_ = tmp.keys(); |
| int counter = tmp.size(); |
| if (keys == null) { |
| keys = new Object[counter]; |
| } |
| while (counter > 0) { |
| keys[--counter] = enum_.nextElement(); |
| } |
| } |
| return keys; |
| } |
| |
| /* |
| * Returns true if the current storage mechanism is |
| * an array of alternating key-value pairs. |
| */ |
| private boolean isArray(){ |
| return (table instanceof Object[]); |
| } |
| |
| /* |
| * Grows the storage from an array to a hashtable. |
| */ |
| private void grow() { |
| Object[] array = (Object[])table; |
| Hashtable<Object, Object> tmp = new Hashtable<Object, Object>(array.length/2); |
| for (int i = 0; i<array.length; i+=2) { |
| tmp.put(array[i], array[i+1]); |
| } |
| table = tmp; |
| } |
| |
| /* |
| * Shrinks the storage from a hashtable to an array. |
| */ |
| private void shrink() { |
| Hashtable<?,?> tmp = (Hashtable)table; |
| Object[] array = new Object[tmp.size()*2]; |
| Enumeration<?> keys = tmp.keys(); |
| int j = 0; |
| |
| while (keys.hasMoreElements()) { |
| Object o = keys.nextElement(); |
| array[j] = o; |
| array[j+1] = tmp.get(o); |
| j+=2; |
| } |
| table = array; |
| } |
| } |