001/*
002 * $Id$
003 *
004 * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 * 
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 * Lesser General Public License for more details.
016 * 
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020 *
021 */
022package org.jdesktop.swingx.plaf.basic.core;
023
024
025import java.lang.reflect.*;
026import javax.swing.*;
027import javax.swing.plaf.*;
028
029/**
030 * An ActionMap that populates its contents as necessary. The
031 * contents are populated by invoking the <code>loadActionMap</code>
032 * method on the passed in Object.
033 *
034 * @version 1.6, 11/17/05
035 * @author Scott Violet
036 */
037public class LazyActionMap extends ActionMapUIResource {
038    /**
039     * Object to invoke <code>loadActionMap</code> on. This may be
040     * a Class object.
041     */
042    private transient Object _loader;
043
044    /**
045     * Installs an ActionMap that will be populated by invoking the
046     * <code>loadActionMap</code> method on the specified Class
047     * when necessary.
048     * <p>
049     * This should be used if the ActionMap can be shared.
050     *
051     * @param c JComponent to install the ActionMap on.
052     * @param loaderClass Class object that gets loadActionMap invoked
053     *                    on.
054     * @param defaultsKey Key to use to defaults table to check for
055     *        existing map and what resulting Map will be registered on.
056     */
057    public static void installLazyActionMap(JComponent c, Class loaderClass,
058                                     String defaultsKey) {
059        ActionMap map = (ActionMap)UIManager.get(defaultsKey);
060        if (map == null) {
061            map = new LazyActionMap(loaderClass);
062            UIManager.getLookAndFeelDefaults().put(defaultsKey, map);
063        }
064        SwingUtilities.replaceUIActionMap(c, map);
065    }
066
067    /**
068     * Returns an ActionMap that will be populated by invoking the
069     * <code>loadActionMap</code> method on the specified Class
070     * when necessary.
071     * <p>
072     * This should be used if the ActionMap can be shared.
073     *
074     * @param c JComponent to install the ActionMap on.
075     * @param loaderClass Class object that gets loadActionMap invoked
076     *                    on.
077     * @param defaultsKey Key to use to defaults table to check for
078     *        existing map and what resulting Map will be registered on.
079     */
080    static ActionMap getActionMap(Class loaderClass,
081                                  String defaultsKey) {
082        ActionMap map = (ActionMap)UIManager.get(defaultsKey);
083        if (map == null) {
084            map = new LazyActionMap(loaderClass);
085            UIManager.getLookAndFeelDefaults().put(defaultsKey, map);
086        }
087        return map;
088    }
089
090
091    private LazyActionMap(Class loader) {
092        _loader = loader;
093    }
094
095    public void put(Action action) {
096        put(action.getValue(Action.NAME), action);
097    }
098
099    public void put(Object key, Action action) {
100        loadIfNecessary();
101        super.put(key, action);
102    }
103
104    public Action get(Object key) {
105        loadIfNecessary();
106        return super.get(key);
107    }
108
109    public void remove(Object key) {
110        loadIfNecessary();
111        super.remove(key);
112    }
113
114    public void clear() {
115        loadIfNecessary();
116        super.clear();
117    }
118
119    public Object[] keys() {
120        loadIfNecessary();
121        return super.keys();
122    }
123
124    public int size() {
125        loadIfNecessary();
126        return super.size();
127    }
128
129    public Object[] allKeys() {
130        loadIfNecessary();
131        return super.allKeys();
132    }
133
134    public void setParent(ActionMap map) {
135        loadIfNecessary();
136        super.setParent(map);
137    }
138
139    private void loadIfNecessary() {
140        if (_loader != null) {
141            Object loader = _loader;
142
143            _loader = null;
144            Class klass = (Class)loader;
145            try {
146                Method method = klass.getDeclaredMethod("loadActionMap",
147                                      new Class[] { LazyActionMap.class });
148                method.invoke(klass, new Object[] { this });
149            } catch (NoSuchMethodException nsme) {
150                assert false : "LazyActionMap unable to load actions " +
151                        klass;
152            } catch (IllegalAccessException iae) {
153                assert false : "LazyActionMap unable to load actions " +
154                        iae;
155            } catch (InvocationTargetException ite) {
156                assert false : "LazyActionMap unable to load actions " +
157                        ite;
158            } catch (IllegalArgumentException iae) {
159                assert false : "LazyActionMap unable to load actions " +
160                        iae;
161            }
162        }
163    }
164}