ConfigurableWindowDisplayHandler.java
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(HANDLERinstanceof 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(HANDLERinstanceof 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             ((RunnableWithArgshandler).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 }