001 /*
002 * Copyright 2008-2015 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 ((RunnableWithArgs) handler).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 }
|