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