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 }