1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.javatest.tool; 28 29 import java.beans.PropertyChangeEvent; 30 import java.beans.PropertyChangeListener; 31 import java.lang.ref.WeakReference; 32 import java.util.HashMap; 33 import java.util.Map; 34 import javax.swing.Action; 35 import javax.swing.Icon; 36 import javax.swing.KeyStroke; 37 38 import com.sun.javatest.util.DynamicArray; 39 import com.sun.javatest.util.I18NResourceBundle; 40 41 /** 42 * Standard template for creation of an Action to be used in a Tool. 43 */ 44 public abstract class ToolAction implements Action 45 { 46 /** 47 * Construct an action with a specific mnemonic. This is the 48 * non-internationalized version and not recommended. See 49 * <code>Action</code> for details on the parameters. 50 * @param name Name of this action 51 * @param desc Description of this action 52 * @param mnemonic Mnemonic associated with this action 53 * @see javax.swing.Action 54 */ 55 public ToolAction(String name, String desc, int mnemonic) { 56 this.name = name; 57 this.desc = desc; 58 this.mnemonic = new Integer(mnemonic); 59 enabled = true; 60 } 61 62 /** 63 * Construct an internationalized action. 64 * @param uif Factory to use for getting strings. 65 * @param key Key for retrieving internationalized strings from the 66 * bundle. 67 * @see #ToolAction(I18NResourceBundle,String) 68 */ 69 public ToolAction(UIFactory uif, String key) { 70 this(uif.getI18NResourceBundle(), key); 71 } 72 73 /** 74 * Construct an internationalized action. 75 * @param uif Factory to use for getting strings. 76 * @param key Key for retrieving internationalized strings from the 77 * bundle. 78 * @param needIcon True if an icon resource should be associated with 79 * this action. Will be retrieved through the uif. And 80 * put into the <code>SMALL_ICON</code> property. 81 * @see #ToolAction(I18NResourceBundle,String) 82 * @see javax.swing.Action#SMALL_ICON 83 */ 84 public ToolAction(UIFactory uif, String key, boolean needIcon) { 85 this(uif, key); 86 if (needIcon) 87 putValue(Action.SMALL_ICON, uif.createIcon(key)); 88 } 89 90 /** 91 * Construct an internationalized action. 92 * The resources used are: 93 * <table> 94 * <tr><td><i>uiKey</i>.act <td>the name for the button 95 * <tr><td><i>uiKey</i>.tip <td>the tool tip for the action 96 * <tr><td><i>uiKey</i>.mne <td>mnemonic for this action 97 * </table> 98 * @param i18n Resource bundle to use when getting action properties 99 * @param key Key for retrieving internationalized strings from the 100 * bundle. 101 */ 102 public ToolAction(I18NResourceBundle i18n, String key) { 103 this( i18n.getString(key + ".act"), 104 i18n.getString(key + ".tip"), 105 getMnemonic(i18n, key + ".mne") ); 106 } 107 108 /** 109 * Gets one of this object's properties using the associated key. 110 * @param key the key of the property to be returned 111 * @return the value of the property with the given key 112 * @see #putValue 113 */ 114 public Object getValue(String key) { 115 if (key == null) 116 throw new NullPointerException(); 117 118 if (key.equals(NAME)) 119 return name; 120 else if (key.equals(SHORT_DESCRIPTION)) 121 return desc; 122 else if (key.equals(MNEMONIC_KEY)) 123 return mnemonic; 124 else if (key.equals(SMALL_ICON)) 125 return icon; 126 else 127 return (misc == null ? null : misc.get(key)); 128 } 129 130 /** 131 * Sets one of this object's properties using the associated key. 132 * If the value has changed, a <code>PropertyChangeEvent</code> is sent 133 * to listeners. 134 * @param key the key of the property to be stored 135 * @param newVal the new value for the property 136 */ 137 public void putValue(String key, Object newVal) { 138 Object oldVal; 139 140 if (key.equals(NAME)) { 141 if (equal(newVal, name)) 142 return; 143 oldVal = name; 144 name = (String) newVal; 145 } 146 else if (key.equals(SHORT_DESCRIPTION)) { 147 if (equal(newVal, desc)) 148 return; 149 oldVal = desc; 150 desc = (String) newVal; 151 } 152 else if (key.equals(MNEMONIC_KEY)) { 153 if (equal(newVal, mnemonic)) 154 return; 155 oldVal = mnemonic; 156 mnemonic = (Integer) newVal; 157 } 158 else if (key.equals(SMALL_ICON)) { 159 if (equal(newVal, icon)) 160 return; 161 oldVal = icon; 162 icon = (Icon) newVal; 163 } 164 else { 165 if (misc == null) 166 misc = new HashMap<>(); 167 oldVal = misc.get(key); 168 if (equal(newVal, oldVal)) 169 return; 170 misc.put(key, newVal); 171 } 172 firePropertyChangeEvent(key, oldVal, newVal); 173 } 174 175 public boolean isEnabled() { 176 // no need to synchronize just to read a single simple boolean 177 return enabled; 178 } 179 180 public void setEnabled(boolean newVal) { 181 if (enabled == newVal) 182 return; 183 184 boolean oldVal = enabled; 185 enabled = newVal; 186 187 if (listeners.length > 0) 188 firePropertyChangeEvent("enabled", new Boolean(oldVal), new Boolean(newVal)); 189 } 190 191 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { 192 listeners = DynamicArray.append(listeners, new WeakReference<>(listener)); 193 } 194 195 public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { 196 WeakReference[] l = listeners; 197 int size = l.length; 198 for (int i = size - 1; i >= 0; i--) { 199 if (l[i].get() == listener) 200 System.arraycopy(l, i+1, l, i, (--size) - i); 201 } 202 203 if (size < l.length) { 204 listeners = new WeakReference[size]; 205 System.arraycopy(l, 0, listeners, 0, size); 206 } 207 } 208 209 private void firePropertyChangeEvent(String name, Object oldVal, Object newVal) { 210 PropertyChangeEvent ev = null; // lazy create event if needed 211 WeakReference[] l = listeners; 212 if (l.length > 0) { 213 for (int i = l.length - 1; i >= 0; i--) { 214 PropertyChangeListener pcl = (PropertyChangeListener) (l[i].get()); 215 if (pcl != null) { 216 if (ev == null) 217 ev = new PropertyChangeEvent(this, name, oldVal, newVal); 218 pcl.propertyChange(ev); 219 } 220 } 221 } 222 } 223 224 225 private static int getMnemonic(I18NResourceBundle i18n, String key) { 226 String keyString = i18n.getString(key); 227 KeyStroke keyStroke = KeyStroke.getKeyStroke(keyString); 228 return (keyStroke == null ? 0 : keyStroke.getKeyCode()); 229 } 230 231 private static boolean equal(Object a, Object b) { 232 return (a == null ? b == null : a.equals(b)); 233 } 234 235 private String name; 236 private String desc; 237 private Integer mnemonic; 238 private Icon icon; 239 private Map<String, Object> misc; 240 private boolean enabled = true; 241 242 private WeakReference[] listeners = new WeakReference[0]; 243 }