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}