/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
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 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();
}
((
Hashtable<
Object,
Object>)
table).
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;
}
}