001/* Methods that clean up possible memory leaks caused by listeners. 002 003 Copyright (c) 2011-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 */ 027package ptolemy.gui; 028 029import java.awt.Component; 030import java.awt.Window; 031import java.awt.event.ActionListener; 032import java.awt.event.WindowListener; 033 034import javax.swing.AbstractButton; 035import javax.swing.ActionMap; 036import javax.swing.JButton; 037import javax.swing.JComponent; 038import javax.swing.JMenu; 039import javax.swing.JMenuBar; 040import javax.swing.JToolBar; 041 042/** 043 * Methods that clean up possible memory leaks caused by listeners. 044 * Experimental methods will be marked so in their javadoc 045 * comments until they are proven to be effective. 046 * 047 * The static methods of this class can be used to remove listeners 048 * when disposing of Windows. Swing, for various reasons, does not 049 * know when a window has been disposed and thus never garbage 050 * collects the listeners. These listeners each have a reference 051 * to the window which means the window (and everything it references) 052 * never gets garbage collected. 053 * 054 * @author Aaron Schultz 055 * @version $Id$ 056 * @since Ptolemy II 10.0 057 * @Pt.ProposedRating Red (cxh) 058 * @Pt.AcceptedRating Red (cxh) 059 */ 060public class MemoryCleaner { 061 062 /////////////////////////////////////////////////////////////////// 063 //// public methods //// 064 065 /** 066 * Remove ActionListeners from a JMenuBar. 067 * 068 * @param menubar The menubar from which the ActionListeners 069 * are to be removed. 070 * @return The number of listeners removed. 071 */ 072 public static int removeActionListeners(JMenuBar menubar) { 073 int listenersRemoved = 0; 074 if (menubar != null) { 075 int count = menubar.getMenuCount(); 076 if (_isDebugging) { 077 System.out.println("menu count: " + count); 078 } 079 080 for (int m = 0; m < count; m++) { 081 JMenu menu = menubar.getMenu(m); 082 083 int removed = removeActionListeners(menu); 084 listenersRemoved += removed; 085 } 086 } 087 return listenersRemoved; 088 } 089 090 /** 091 * Remove ActionListeners from an AbstractButton (such as a JMenuItem. 092 * If the AbstractButton is a JMenu, the ActionListeners of all the 093 * child items get removed too. 094 * 095 * @param button The button from which the ActionListeners 096 * are to be removed. 097 * @return The number of listeners removed. 098 */ 099 public static int removeActionListeners(AbstractButton button) { 100 int listenersRemoved = 0; 101 if (button != null) { 102 ActionListener[] listeners = button.getActionListeners(); 103 if (listeners != null) { 104 int count = listeners.length; 105 for (ActionListener listener : listeners) { 106 button.removeActionListener(listener); 107 } 108 int countAfter = button.getActionListeners().length; 109 listenersRemoved += count - countAfter; 110 //System.out.println("Removed "+listenersRemoved+" for "+button.getText()); 111 if (countAfter != 0) { 112 //System.out.println("cOUNTaFTERrEMOVE: "+countAfter); 113 } 114 } 115 116 if (button instanceof JMenu) { 117 JMenu jmenu = (JMenu) button; 118 int totalMenuItems = jmenu.getMenuComponentCount(); 119 for (int n = 0; n < totalMenuItems; n++) { 120 Component component = jmenu.getMenuComponent(n); 121 if (component instanceof AbstractButton) { 122 AbstractButton b = (AbstractButton) component; 123 listenersRemoved += removeActionListeners(b); 124 } 125 } 126 } 127 } 128 return listenersRemoved; 129 } 130 131 /** 132 * Remove ActionMapListeners from a JComponent. 133 * This is useful for removing key bindings. 134 * 135 * @param component The JComponent from which the Action in a ActionMap 136 * are to be removed. 137 * @return The number of listeners removed. 138 */ 139 public static int removeActionListeners(JComponent component) { 140 int listenersRemoved = 0; 141 if (component != null) { 142 ActionMap actionMap = component.getActionMap(); 143 int count = actionMap.size(); 144 if (_isDebugging) { 145 System.out.println("actionMap count: " + count); 146 } 147 148 for (int a = 0; a < count; a++) { 149 Object[] keys = actionMap.keys(); 150 if (keys != null) { 151 for (int k = 0; k < keys.length; k++) { 152 actionMap.remove(keys[k]); 153 listenersRemoved++; 154 } 155 } 156 } 157 } 158 return listenersRemoved; 159 } 160 161 /** 162 * Remove WindowListeners from a Window. 163 * Experimental. 164 * 165 * @param window The Window from which WindowListeners 166 * are to be removed. 167 * @return The number of listeners removed. 168 */ 169 public static int removeWindowListeners(Window window) { 170 int listenersRemoved = 0; 171 if (window != null) { 172 WindowListener[] listeners = window.getWindowListeners(); 173 int count = listeners.length; 174 for (WindowListener listener : listeners) { 175 window.removeWindowListener(listener); 176 } 177 int countAfter = window.getWindowListeners().length; 178 listenersRemoved = count - countAfter; 179 } 180 return listenersRemoved; 181 } 182 183 /** 184 * Remove ActionListeners from a JToolBar. 185 * 186 * @param toolbar The tool bar from which the ActionListeners 187 * are to be removed. 188 * @return The number of listeners removed. 189 */ 190 public static int removeActionListeners(JToolBar toolbar) { 191 int listenersRemoved = 0; 192 if (toolbar != null) { 193 int count = toolbar.getComponentCount(); 194 if (_isDebugging) { 195 System.out.println("component count: " + count); 196 } 197 198 for (int m = 0; m < count; m++) { 199 Component c = toolbar.getComponentAtIndex(m); 200 if (c instanceof JButton) { 201 JButton b = (JButton) c; 202 b.setAction(null); 203 listenersRemoved++; 204 } else { 205 if (_isDebugging) { 206 System.out.println( 207 (c == null ? "null" : c.getClass().getName())); 208 } 209 } 210 } 211 } 212 return listenersRemoved; 213 } 214 215 /////////////////////////////////////////////////////////////////// 216 //// private variables //// 217 218 private static boolean _isDebugging = false; 219}