1/*
2 * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package javax.swing.plaf.basic;
26
27import java.lang.reflect.*;
28import javax.swing.*;
29import javax.swing.plaf.*;
30
31/**
32 * An ActionMap that populates its contents as necessary. The
33 * contents are populated by invoking the <code>loadActionMap</code>
34 * method on the passed in Object.
35 *
36 * @author Scott Violet
37 */
38@SuppressWarnings("serial") // Same-version serialization only
39class LazyActionMap extends ActionMapUIResource {
40    /**
41     * Object to invoke <code>loadActionMap</code> on. This may be
42     * a Class object.
43     */
44    private transient Object _loader;
45
46    /**
47     * Installs an ActionMap that will be populated by invoking the
48     * <code>loadActionMap</code> method on the specified Class
49     * when necessary.
50     * <p>
51     * This should be used if the ActionMap can be shared.
52     *
53     * @param c JComponent to install the ActionMap on.
54     * @param loaderClass Class object that gets loadActionMap invoked
55     *                    on.
56     * @param defaultsKey Key to use to defaults table to check for
57     *        existing map and what resulting Map will be registered on.
58     */
59    static void installLazyActionMap(JComponent c, Class<?> loaderClass,
60                                     String defaultsKey) {
61        ActionMap map = (ActionMap)UIManager.get(defaultsKey);
62        if (map == null) {
63            map = new LazyActionMap(loaderClass);
64            UIManager.getLookAndFeelDefaults().put(defaultsKey, map);
65        }
66        SwingUtilities.replaceUIActionMap(c, map);
67    }
68
69    /**
70     * Returns an ActionMap that will be populated by invoking the
71     * <code>loadActionMap</code> method on the specified Class
72     * when necessary.
73     * <p>
74     * This should be used if the ActionMap can be shared.
75     *
76     * @param loaderClass Class object that gets loadActionMap invoked
77     *                    on.
78     * @param defaultsKey Key to use to defaults table to check for
79     *        existing map and what resulting Map will be registered on.
80     */
81    static ActionMap getActionMap(Class<?> loaderClass,
82                                  String defaultsKey) {
83        ActionMap map = (ActionMap)UIManager.get(defaultsKey);
84        if (map == null) {
85            map = new LazyActionMap(loaderClass);
86            UIManager.getLookAndFeelDefaults().put(defaultsKey, map);
87        }
88        return map;
89    }
90
91
92    private LazyActionMap(Class<?> loader) {
93        _loader = loader;
94    }
95
96    public void put(Action action) {
97        put(action.getValue(Action.NAME), action);
98    }
99
100    public void put(Object key, Action action) {
101        loadIfNecessary();
102        super.put(key, action);
103    }
104
105    public Action get(Object key) {
106        loadIfNecessary();
107        return super.get(key);
108    }
109
110    public void remove(Object key) {
111        loadIfNecessary();
112        super.remove(key);
113    }
114
115    public void clear() {
116        loadIfNecessary();
117        super.clear();
118    }
119
120    public Object[] keys() {
121        loadIfNecessary();
122        return super.keys();
123    }
124
125    public int size() {
126        loadIfNecessary();
127        return super.size();
128    }
129
130    public Object[] allKeys() {
131        loadIfNecessary();
132        return super.allKeys();
133    }
134
135    public void setParent(ActionMap map) {
136        loadIfNecessary();
137        super.setParent(map);
138    }
139
140    private void loadIfNecessary() {
141        if (_loader != null) {
142            Object loader = _loader;
143
144            _loader = null;
145            Class<?> klass = (Class<?>)loader;
146            try {
147                Method method = klass.getDeclaredMethod("loadActionMap",
148                                      new Class<?>[] { LazyActionMap.class });
149                method.invoke(klass, new Object[] { this });
150            } catch (NoSuchMethodException nsme) {
151                assert false : "LazyActionMap unable to load actions " +
152                        klass;
153            } catch (IllegalAccessException iae) {
154                assert false : "LazyActionMap unable to load actions " +
155                        iae;
156            } catch (InvocationTargetException ite) {
157                assert false : "LazyActionMap unable to load actions " +
158                        ite;
159            } catch (IllegalArgumentException iae) {
160                assert false : "LazyActionMap unable to load actions " +
161                        iae;
162            }
163        }
164    }
165}
166