1 /* 2 * Copyright (c) 1999, 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.ObjectInputStream; 29 import java.io.ObjectOutputStream; 30 import java.io.Serializable; 31 import java.util.HashMap; 32 import java.util.Set; 33 34 /** 35 * {@code ActionMap} provides mappings from 36 * {@code Object}s 37 * (called <em>keys</em> or <em>{@code Action} names</em>) 38 * to {@code Action}s. 39 * An {@code ActionMap} is usually used with an {@code InputMap} 40 * to locate a particular action 41 * when a key is pressed. As with {@code InputMap}, 42 * an {@code ActionMap} can have a parent 43 * that is searched for keys not defined in the {@code ActionMap}. 44 * <p>As with {@code InputMap} if you create a cycle, eg: 45 * <pre> 46 * ActionMap am = new ActionMap(); 47 * ActionMap bm = new ActionMap(): 48 * am.setParent(bm); 49 * bm.setParent(am); 50 * </pre> 51 * some of the methods will cause a StackOverflowError to be thrown. 52 * 53 * @see InputMap 54 * 55 * @author Scott Violet 56 * @since 1.3 57 */ 58 @SuppressWarnings("serial") 59 public class ActionMap implements Serializable { 60 /** Handles the mapping between Action name and Action. */ 61 private transient ArrayTable arrayTable; 62 /** Parent that handles any bindings we don't contain. */ 63 private ActionMap parent; 64 65 66 /** 67 * Creates an {@code ActionMap} with no parent and no mappings. 68 */ 69 public ActionMap() { 70 } 71 72 /** 73 * Sets this {@code ActionMap}'s parent. 74 * 75 * @param map the {@code ActionMap} that is the parent of this one 76 */ 77 public void setParent(ActionMap map) { 78 this.parent = map; 79 } 80 81 /** 82 * Returns this {@code ActionMap}'s parent. 83 * 84 * @return the {@code ActionMap} that is the parent of this one, 85 * or null if this {@code ActionMap} has no parent 86 */ 87 public ActionMap getParent() { 88 return parent; 89 } 90 91 /** 92 * Adds a binding for {@code key} to {@code action}. 93 * If {@code action} is null, this removes the current binding 94 * for {@code key}. 95 * <p>In most instances, {@code key} will be 96 * {@code action.getValue(NAME)}. 97 * 98 * @param key a key 99 * @param action a binding for {@code key} 100 */ 101 public void put(Object key, Action action) { 102 if (key == null) { 103 return; 104 } 105 if (action == null) { 106 remove(key); 107 } 108 else { 109 if (arrayTable == null) { 110 arrayTable = new ArrayTable(); 111 } 112 arrayTable.put(key, action); 113 } 114 } 115 116 /** 117 * Returns the binding for {@code key}, messaging the 118 * parent {@code ActionMap} if the binding is not locally defined. 119 * 120 * @param key a key 121 * @return the binding for {@code key} 122 */ 123 public Action get(Object key) { 124 Action value = (arrayTable == null) ? null : 125 (Action)arrayTable.get(key); 126 127 if (value == null) { 128 ActionMap parent = getParent(); 129 130 if (parent != null) { 131 return parent.get(key); 132 } 133 } 134 return value; 135 } 136 137 /** 138 * Removes the binding for {@code key} from this {@code ActionMap}. 139 * 140 * @param key a key 141 */ 142 public void remove(Object key) { 143 if (arrayTable != null) { 144 arrayTable.remove(key); 145 } 146 } 147 148 /** 149 * Removes all the mappings from this {@code ActionMap}. 150 */ 151 public void clear() { 152 if (arrayTable != null) { 153 arrayTable.clear(); 154 } 155 } 156 157 /** 158 * Returns the {@code Action} names that are bound in this {@code ActionMap}. 159 * 160 * @return an array of the keys 161 */ 162 public Object[] keys() { 163 if (arrayTable == null) { 164 return null; 165 } 166 return arrayTable.getKeys(null); 167 } 168 169 /** 170 * Returns the number of bindings in this {@code ActionMap}. 171 * 172 * @return the number of bindings in this {@code ActionMap} 173 */ 174 public int size() { 175 if (arrayTable == null) { 176 return 0; 177 } 178 return arrayTable.size(); 179 } 180 181 /** 182 * Returns an array of the keys defined in this {@code ActionMap} and 183 * its parent. This method differs from {@code keys()} in that 184 * this method includes the keys defined in the parent. 185 * 186 * @return an array of the keys 187 */ 188 public Object[] allKeys() { 189 int count = size(); 190 ActionMap parent = getParent(); 191 192 if (count == 0) { 193 if (parent != null) { 194 return parent.allKeys(); 195 } 196 return keys(); 197 } 198 if (parent == null) { 199 return keys(); 200 } 201 Object[] keys = keys(); 202 Object[] pKeys = parent.allKeys(); 203 204 if (pKeys == null) { 205 return keys; 206 } 207 if (keys == null) { 208 // Should only happen if size() != keys.length, which should only 209 // happen if mutated from multiple threads (or a bogus subclass). 210 return pKeys; 211 } 212 213 HashMap<Object, Object> keyMap = new HashMap<Object, Object>(); 214 int counter; 215 216 for (counter = keys.length - 1; counter >= 0; counter--) { 217 keyMap.put(keys[counter], keys[counter]); 218 } 219 for (counter = pKeys.length - 1; counter >= 0; counter--) { 220 keyMap.put(pKeys[counter], pKeys[counter]); 221 } 222 return keyMap.keySet().toArray(); 223 } 224 225 private void writeObject(ObjectOutputStream s) throws IOException { 226 s.defaultWriteObject(); 227 228 ArrayTable.writeArrayTable(s, arrayTable); 229 } 230 231 private void readObject(ObjectInputStream s) throws ClassNotFoundException, 232 IOException { 233 s.defaultReadObject(); 234 for (int counter = s.readInt() - 1; counter >= 0; counter--) { 235 put(s.readObject(), (Action)s.readObject()); 236 } 237 } 238 }