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 }
|