Clover coverage report - DrJava Test Coverage (drjava-20120304-r5456)
Coverage timestamp: Sun Mar 4 2012 03:13:23 CST
file stats: LOC: 282   Methods: 25
NCLOC: 175   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
KeyBindingManager.java 34.4% 35.2% 52% 37.6%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.drjava.ui;
 38   
 39    import edu.rice.cs.drjava.*;
 40    import edu.rice.cs.drjava.config.*;
 41    import javax.swing.*;
 42    import java.util.*;
 43    import java.awt.event.*;
 44   
 45    /** Contains Hashtables that are used in the key-binding process along with methods to build them and access their
 46    * contents. Performs the assigning of keys to actions, checking for and resolving conflicts, and setting appropriate
 47    * menu accelerators.
 48    * @version $Id: KeyBindingManager.java 5364 2010-08-16 05:35:23Z mgricken $
 49    */
 50    public class KeyBindingManager {
 51   
 52    public static final KeyBindingManager ONLY = new KeyBindingManager();
 53   
 54  7 private KeyBindingManager() { }
 55   
 56    // Key-binding configuration tables
 57    private HashMap<KeyStroke, KeyStrokeData> _keyToDataMap = new HashMap<KeyStroke, KeyStrokeData>();
 58    private HashMap<Action, KeyStrokeData> _actionToDataMap = new HashMap<Action, KeyStrokeData>();
 59   
 60    private MainFrame _mainFrame = null;
 61   
 62    /** Should only check conflicts when the keyboard configuration options are first entered into the maps. Afterwards,
 63    * the GUI configuration will warn the user about actions whose key-bindings will be overwritten in the GetKeyDialog,
 64    * and the preferences panel will reflect the changes. When the user hit apply, no conflicts should exist in the
 65    * preferences panel, and there should be no need to check for conflicts in the configuration.
 66    */
 67    private boolean _shouldCheckConflict = true;
 68   
 69  38 public void setMainFrame (MainFrame mainFrame) { _mainFrame = mainFrame; }
 70   
 71  38 public void setShouldCheckConflict (boolean bool) { _shouldCheckConflict = bool; }
 72   
 73  114 public Collection<KeyStrokeData> getKeyStrokeData() { return _actionToDataMap.values(); }
 74   
 75  4902 public void put(VectorOption<KeyStroke> vkso, Action a, JMenuItem jmi, String name) {
 76  4902 Vector<KeyStroke> keys = DrJava.getConfig().getSetting(vkso);
 77  4902 Vector<KeyStroke> retained = new Vector<KeyStroke>();
 78  4902 KeyStrokeData ksd = new KeyStrokeData(keys, a, jmi, name, vkso);
 79  4902 _actionToDataMap.put(a, ksd);
 80  4902 for(KeyStroke ks: keys) {
 81  3610 if (shouldUpdate(ks, a)) {
 82  3610 retained.add(ks);
 83  3610 _keyToDataMap.put(ks, ksd);
 84    }
 85    }
 86  4902 DrJava.getConfig().addOptionListener(vkso, new VectorKeyStrokeOptionListener(jmi, a, retained));
 87  4902 if (retained.size() != keys.size()) {
 88    // not all keys were added
 89  0 DrJava.getConfig().setSetting(vkso,retained);
 90    }
 91    }
 92   
 93    /** Takes a KeyStroke and gets its Action from the keyToActionMap
 94    * @param ks KeyStroke to look up
 95    * @return the corresponding Action or null if there is no Action associated with the KeyStroke
 96    */
 97  2680 public Action get(KeyStroke ks) {
 98  2680 KeyStrokeData ksd = _keyToDataMap.get(ks);
 99  53 if (ksd == null) return null;
 100  2627 return ksd.getAction();
 101    }
 102   
 103  4 public String getName(KeyStroke ks) {
 104  4 KeyStrokeData ksd = _keyToDataMap.get(ks);
 105  4 if (ksd == null) return null;
 106  0 return ksd.getName();
 107    }
 108   
 109  0 public String getName(Action a) {
 110  0 KeyStrokeData ksd = _actionToDataMap.get(a);
 111  0 if (ksd == null) return null;
 112  0 return ksd.getName();
 113    }
 114   
 115    /** Inserts a KeyStroke/Action pair into the _keyToActionMap. Checks for conflicts and displays an option pane if
 116    * they are any.
 117    * @param ks the KeyStroke
 118    * @param a the Action
 119    * @return whether a map insertion was done
 120    */
 121  3610 private boolean shouldUpdate(KeyStroke ks, Action a) {
 122  3610 if (ks == KeyStrokeOption.NULL_KEYSTROKE) {
 123    // filter out NULL_KEYSTROKE
 124  0 return false;
 125    }
 126   
 127  3610 if (!_keyToDataMap.containsKey(ks) ) {
 128    // the key is not assigned to any action yet, put it in
 129  570 return true;
 130    }
 131  3040 else if (_keyToDataMap.get(ks).getAction().equals(a)) {
 132    // this key is already assigned to the same action; no action necessary, but updating doesn't hurt either
 133    // and a gratuitous update simplifies dealing with multiple menu bars
 134  576 return true;
 135    }
 136    else { // key-binding conflict
 137  2464 if (_shouldCheckConflict) {
 138  0 KeyStrokeOption opt = new KeyStrokeOption(null,null);
 139  0 KeyStrokeData conflictKSD = _keyToDataMap.get(ks);
 140  0 String key = opt.format(ks);
 141  0 KeyStrokeData newKSD = _actionToDataMap.get(a);
 142  0 String text = "\"" + key + "\"" + " is already assigned to \"" + conflictKSD.getName() +
 143    "\".\nWould you like to assign \"" + key + "\" to \"" + newKSD.getName() + "\"?";
 144  0 int rc = JOptionPane.showConfirmDialog(_mainFrame, text, "DrJava", JOptionPane.YES_NO_OPTION);
 145   
 146  0 switch (rc) {
 147  0 case JOptionPane.YES_OPTION:
 148  0 removeExistingKeyStroke(ks);
 149  0 return true;
 150  0 case JOptionPane.NO_OPTION:
 151  0 case JOptionPane.CLOSED_OPTION:
 152  0 case JOptionPane.CANCEL_OPTION:
 153  0 return false;
 154  0 default:
 155  0 throw new RuntimeException("Invalid rc: " + rc);
 156    }
 157    }
 158  2464 else return true;
 159    }
 160    }
 161   
 162  0 private void removeExistingKeyStroke(KeyStroke ks) {
 163    // check for conflicting key binding
 164  0 if (_keyToDataMap.containsKey(ks) && _shouldCheckConflict) {
 165    // if new key in map, and shouldUpdate returns true, we are overwriting it
 166  0 KeyStrokeData conflictKSD = _keyToDataMap.get(ks);
 167    // remove ks from the conflicting keystroke data
 168  0 Set<KeyStroke> conflictKeys = new LinkedHashSet<KeyStroke>(conflictKSD.getKeyStrokes());
 169  0 conflictKeys.remove(ks);
 170  0 conflictKSD.setKeyStrokes(new Vector<KeyStroke>(conflictKeys));
 171  0 updateMenuItem(conflictKSD);
 172  0 _keyToDataMap.remove(ks);
 173  0 DrJava.getConfig().setSetting(conflictKSD.getOption(), conflictKSD.getKeyStrokes());
 174    }
 175    }
 176   
 177  0 private void updateMenuItem(KeyStrokeData data) {
 178  0 JMenuItem jmi = data.getJMenuItem();
 179   
 180    // Check associated Menu Item. If jmi is null, this keystroke maps to an action that isn't in the menu
 181  0 if (jmi != null) {
 182  0 Vector<KeyStroke> keys = data.getKeyStrokes();
 183  0 if (keys.size() > 0) {
 184    // Since we can have multiple keys mapped to the same action, we use the first key as menu item accelerator
 185  0 jmi.setAccelerator(keys.get(0));
 186    }
 187    else {
 188    // Clear the menu item's accelerator
 189  0 jmi.setAccelerator(null);
 190    }
 191    }
 192    }
 193   
 194    /** A listener that can be attached to VectorKeyStrokeOptions that automatically updates the Hashtables in
 195    * KeyBindingManager, the corresponding selection Action bindings, and the menu accelerators.
 196    */
 197    public class VectorKeyStrokeOptionListener implements OptionListener<Vector<KeyStroke>> {
 198    protected JMenuItem _jmi; // the JMenuItem associated with this option
 199    protected Action _a; // the Action associated with this option
 200    protected Set<KeyStroke> _oldKeys; // the old KeyStroke value
 201   
 202  4902 public VectorKeyStrokeOptionListener(JMenuItem jmi, Action a, Vector<KeyStroke> keys) {
 203  4902 _jmi = jmi;
 204  4902 _a = a;
 205  4902 _oldKeys = new LinkedHashSet<KeyStroke>(keys);
 206    }
 207   
 208  0 public VectorKeyStrokeOptionListener(Action a, Vector<KeyStroke> keys) {
 209  0 this(null, a, keys);
 210    }
 211   
 212  0 public void optionChanged(OptionEvent<Vector<KeyStroke>> oce) {
 213  0 Set<KeyStroke> newKeys = new LinkedHashSet<KeyStroke>(oce.value);
 214  0 Set<KeyStroke> removed = new LinkedHashSet<KeyStroke>(_oldKeys);
 215  0 removed.removeAll(newKeys); // the keys that were removed
 216  0 Set<KeyStroke> added = new LinkedHashSet<KeyStroke>(newKeys);
 217  0 added.removeAll(_oldKeys); // the keys that were added
 218  0 Set<KeyStroke> retained = new LinkedHashSet<KeyStroke>(_oldKeys);
 219  0 retained.retainAll(newKeys); // the keys that were kept the same
 220  0 boolean update = false;
 221  0 KeyStrokeData data = _actionToDataMap.get(_a);
 222  0 if (data == null) {
 223    // Nothing to change
 224  0 return;
 225    }
 226   
 227    // check for removed keys
 228  0 for(KeyStroke ks: removed) {
 229    // Only remove the old keystroke from the map if it is currently mapped to our data. If not, our old
 230    // keystroke has already been redefined and should not be removed!
 231  0 if (data.equals(_keyToDataMap.get(ks))) {
 232  0 _keyToDataMap.remove(ks);
 233  0 update = true;
 234    }
 235    }
 236   
 237    // check added keys for conflicts
 238  0 for(KeyStroke ks: added) {
 239  0 if (shouldUpdate(ks, _a)) {
 240  0 _keyToDataMap.put(ks,data);
 241  0 retained.add(ks);
 242  0 update = true;
 243    }
 244    }
 245   
 246  0 if (update) {
 247  0 Vector<KeyStroke> v = new Vector<KeyStroke>(retained);
 248  0 data.setKeyStrokes(v);
 249  0 updateMenuItem(data);
 250  0 _oldKeys = retained;
 251    }
 252    }
 253    }
 254   
 255    public static class KeyStrokeData {
 256    private Vector<KeyStroke> _ks;
 257    private Action _a;
 258    private JMenuItem _jmi;
 259    private String _name;
 260    private VectorOption<KeyStroke> _vkso;
 261   
 262  4902 public KeyStrokeData(Vector<KeyStroke> ks, Action a, JMenuItem jmi, String name, VectorOption<KeyStroke> vkso) {
 263  4902 _ks = new Vector<KeyStroke>(ks);
 264  4902 _a = a;
 265  4902 _jmi = jmi;
 266  4902 _name = name;
 267  4902 _vkso = vkso;
 268    }
 269   
 270  0 public Vector<KeyStroke> getKeyStrokes() { return _ks; }
 271  29217 public Action getAction() { return _a; }
 272  0 public JMenuItem getJMenuItem() { return _jmi; }
 273  22520 public String getName() { return _name; }
 274  156812 public VectorOption<KeyStroke> getOption() { return _vkso; }
 275   
 276  0 public void setKeyStrokes(Vector<KeyStroke> ks) { _ks = new Vector<KeyStroke>(ks); }
 277  0 public void setAction(Action a) { _a = a; }
 278  0 public void setJMenuItem(JMenuItem jmi) { _jmi = jmi; }
 279  0 public void setName(String name) { _name = name; }
 280  0 public void setOption(VectorOption<KeyStroke> vkso) { _vkso = vkso; }
 281    }
 282    }