001 /*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2008-2017 the original author or authors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.codehaus.griffon.runtime.core.view;
019
020 import griffon.core.CallableWithArgs;
021 import griffon.core.GriffonApplication;
022 import griffon.core.RunnableWithArgs;
023 import griffon.core.view.WindowDisplayHandler;
024 import griffon.exceptions.InstanceNotFoundException;
025 import org.slf4j.Logger;
026 import org.slf4j.LoggerFactory;
027
028 import javax.annotation.Nonnull;
029 import javax.annotation.Nullable;
030 import javax.inject.Inject;
031 import javax.inject.Named;
032 import java.util.Collections;
033 import java.util.Map;
034
035 import static griffon.util.AnnotationUtils.named;
036 import static griffon.util.ConfigUtils.getConfigValue;
037 import static griffon.util.GriffonNameUtils.requireNonBlank;
038 import static java.util.Objects.requireNonNull;
039
040 /**
041 * Implementation of a per window {@code WindowDisplayHandler} that can be configured via a DSL.<p>
042 * This is the default {@code WindowDisplayHandler} used by {@code SwingApplication}. It expects a configuration
043 * entry in <code>griffon-app/conf/Config.groovy</code> that looks like the following one<p>
044 * <pre>
045 * windowManager {
046 * myWindowName = [
047 * show: {name, window -> ... },
048 * hide: {name, window -> ... }
049 * ]
050 * myOtherWindowName = [
051 * show: {name, window -> ... }
052 * ]
053 * }
054 * </pre>
055 * <p>
056 * For these settings to work you must specify a <code>name:</code> property on the Window/Frame instance. This
057 * {@code WindowDisplayHandler} is smart enough to use the default show/hide behavior should any or both are not specified
058 * or if a window name does not have a matching configuration. The default behavior will also be used if the Window/Frame
059 * does not have a value for its <code>name:</code> property.<p>
060 * There's a third option that can be set for each configured window, and that is a delegate {@code WindowDisplayHandler} that
061 * will be used for that window alone. The following example shows how it can be configured<p>
062 * <pre>
063 * windowManager {
064 * myWindowName = [
065 * handler: new MyCustomWindowDisplayHandler()
066 * ]
067 * myOtherWindowName = [
068 * show: {name, window -> ... }
069 * ]
070 * }
071 * </pre>
072 * <p>
073 * Lastly, a global handler can be specified for all windows that have not been configured. If specified, this handler will
074 * override the usage of the default one. It can be configured as follows<p>
075 * <pre>
076 * windowManager {
077 * defaultHandler = new MyCustomWindowDisplayHandler()
078 * myOtherWindowName = [
079 * show: {name, window -> ... }
080 * ]
081 * }
082 * </pre>
083 * <p>
084 * Fine grained control for default <code>show</code> and <code>hide</code> is also possible, by specifying <code>defaultShow</code>
085 * and/or <code>defaultHide</code> properties at the global level. These properties take precedence over <code>defaultHandler</code> .
086 * <p>
087 * <pre>
088 * windowManager {
089 * defaultHide = {name, window -> ... }
090 * myOtherWindowName = [
091 * show: {name, window -> ... }
092 * ]
093 * }
094 * </pre>
095 * <p>
096 * <strong>Note:</strong> the value for <code>show</code> and <code>hide</code> can be either a Closure or a {@code RunnableWithArgs}.
097 *
098 * @author Andres Almiray
099 * @since 2.0.0
100 */
101 public class ConfigurableWindowDisplayHandler<W> implements WindowDisplayHandler<W> {
102 protected static final String ERROR_NAME_BLANK = "Argument 'name' must not be blank";
103 protected static final String ERROR_WINDOW_NULL = "Argument 'window' must not be null";
104 private static final Logger LOG = LoggerFactory.getLogger(ConfigurableWindowDisplayHandler.class);
105 private static final String HANDLER = "handler";
106 private final GriffonApplication application;
107 private final WindowDisplayHandler<W> delegateWindowsDisplayHandler;
108
109 @Inject
110 public ConfigurableWindowDisplayHandler(@Nonnull GriffonApplication application, @Nonnull @Named("defaultWindowDisplayHandler") WindowDisplayHandler<W> delegateWindowsDisplayHandler) {
111 this.application = requireNonNull(application, "Argument 'application' must not be null");
112 this.delegateWindowsDisplayHandler = requireNonNull(delegateWindowsDisplayHandler, "Argument 'delegateWindowsDisplayHandler' must not be null");
113 }
114
115 @SuppressWarnings("unchecked")
116 public void show(@Nonnull String name, @Nonnull W window) {
117 requireNonBlank(name, ERROR_NAME_BLANK);
118 requireNonNull(window, ERROR_WINDOW_NULL);
119
120 Map<String, Object> options = windowBlock(name);
121 if (!options.isEmpty()) {
122 Object handler = options.get("show");
123 if (canBeRun(handler)) {
124 LOG.trace("Showing {} with show: handler", name);
125 run(handler, name, window);
126 return;
127 } else if (options.get(HANDLER) instanceof WindowDisplayHandler) {
128 LOG.trace("Showing {} with handler: handler", name);
129 ((WindowDisplayHandler<W>) options.get(HANDLER)).show(name, window);
130 return;
131 }
132 }
133
134 if (handleShowByInjectedHandler(name, window)) {
135 return;
136 }
137
138 options = windowManagerBlock();
139 if (!options.isEmpty()) {
140 Object defaultShow = options.get("defaultShow");
141 if (canBeRun(defaultShow)) {
142 LOG.trace("Showing {} with defaultShow: handler", name);
143 run(defaultShow, name, window);
144 return;
145 }
146 }
147
148 LOG.trace("Showing {} with default handler", name);
149 fetchDefaultWindowDisplayHandler().show(name, window);
150 }
151
152 @SuppressWarnings("unchecked")
153 public void hide(@Nonnull String name, @Nonnull W window) {
154 requireNonBlank(name, ERROR_NAME_BLANK);
155 requireNonNull(window, ERROR_WINDOW_NULL);
156
157 Map<String, Object> options = windowBlock(name);
158 if (!options.isEmpty()) {
159 Object handler = options.get("hide");
160 if (canBeRun(handler)) {
161 LOG.trace("Hiding {} with hide: handler", name);
162 run(handler, name, window);
163 return;
164 } else if (options.get(HANDLER) instanceof WindowDisplayHandler) {
165 LOG.trace("Hiding {} with handler: handler", name);
166 ((WindowDisplayHandler<W>) options.get(HANDLER)).hide(name, window);
167 return;
168 }
169 }
170
171 if (handleHideByInjectedHandler(name, window)) {
172 return;
173 }
174
175 options = windowManagerBlock();
176 if (!options.isEmpty()) {
177 Object defaultHide = options.get("defaultHide");
178 if (canBeRun(defaultHide)) {
179 LOG.trace("Hiding {} with defaultHide: handler", name);
180 run(defaultHide, name, window);
181 return;
182 }
183 }
184
185 LOG.trace("Hiding {} with default handler", name);
186 fetchDefaultWindowDisplayHandler().hide(name, window);
187 }
188
189 @SuppressWarnings("unchecked")
190 protected boolean handleShowByInjectedHandler(@Nonnull String name, @Nonnull W window) {
191 try {
192 WindowDisplayHandler<W> handler = getApplication().getInjector()
193 .getInstance(WindowDisplayHandler.class, named(name));
194 LOG.trace("Showing {} with injected handler", name);
195 handler.show(name, window);
196 return true;
197 } catch (InstanceNotFoundException infe) {
198 // ignore
199 }
200 return false;
201 }
202
203 @SuppressWarnings("unchecked")
204 protected boolean handleHideByInjectedHandler(@Nonnull String name, @Nonnull W window) {
205 try {
206 WindowDisplayHandler<W> handler = getApplication().getInjector()
207 .getInstance(WindowDisplayHandler.class, named(name));
208 LOG.trace("Hiding {} with injected handler", name);
209 handler.hide(name, window);
210 return true;
211 } catch (InstanceNotFoundException infe) {
212 // ignore
213 }
214 return false;
215 }
216
217 public WindowDisplayHandler<W> getDelegateWindowsDisplayHandler() {
218 return delegateWindowsDisplayHandler;
219 }
220
221 protected boolean canBeRun(@Nullable Object obj) {
222 return obj instanceof RunnableWithArgs || obj instanceof CallableWithArgs;
223 }
224
225 protected void run(@Nonnull Object handler, @Nonnull String name, @Nonnull W window) {
226 if (handler instanceof RunnableWithArgs) {
227 ((RunnableWithArgs) handler).run(name, window);
228 } else if (handler instanceof CallableWithArgs) {
229 ((CallableWithArgs<?>) handler).call(name, window);
230 }
231 }
232
233 protected Map<String, Object> windowManagerBlock() {
234 return application.getConfiguration().get("windowManager", Collections.<String, Object>emptyMap());
235 }
236
237 protected Map<String, Object> windowBlock(String windowName) {
238 Map<String, Object> options = windowManagerBlock();
239 return getConfigValue(options, windowName, Collections.<String, Object>emptyMap());
240 }
241
242 protected GriffonApplication getApplication() {
243 return application;
244 }
245
246 @Nonnull
247 @SuppressWarnings("unchecked")
248 protected WindowDisplayHandler<W> fetchDefaultWindowDisplayHandler() {
249 Object handler = windowManagerBlock().get("defaultHandler");
250 return handler instanceof WindowDisplayHandler ? (WindowDisplayHandler<W>) handler : delegateWindowsDisplayHandler;
251 }
252 }
|