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