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.ApplicationEvent;
019 import griffon.core.GriffonApplication;
020 import griffon.core.env.ApplicationPhase;
021 import griffon.core.event.EventRouter;
022 import griffon.core.view.WindowDisplayHandler;
023 import griffon.core.view.WindowManager;
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 java.util.Collection;
032 import java.util.Collections;
033 import java.util.LinkedHashMap;
034 import java.util.List;
035 import java.util.Map;
036 import java.util.Set;
037
038 import static griffon.util.GriffonNameUtils.requireNonBlank;
039 import static java.util.Arrays.asList;
040 import static java.util.Collections.unmodifiableCollection;
041 import static java.util.Objects.requireNonNull;
042
043 /**
044 * @author Andres Almiray
045 * @since 2.0.0
046 */
047 public abstract class AbstractWindowManager<W> implements WindowManager<W> {
048 protected static final String ERROR_NAME_BLANK = "Argument 'name' must not be blank";
049 protected static final String ERROR_WINDOW_NULL = "Argument 'window' must not be null";
050 private static final Logger LOG = LoggerFactory.getLogger(AbstractWindowManager.class);
051 private final Map<String, W> windows = Collections.<String, W>synchronizedMap(new LinkedHashMap<String, W>());
052
053 private final GriffonApplication application;
054 private final WindowDisplayHandler<W> windowDisplayHandler;
055
056 @Inject
057 public AbstractWindowManager(@Nonnull GriffonApplication application, @Nonnull WindowDisplayHandler<W> windowDisplayHandler) {
058 this.application = requireNonNull(application, "Argument 'application' must not be null");
059 requireNonNull(application.getConfiguration(), "Argument 'application.configuration' must not be null");
060 requireNonNull(application.getUIThreadManager(), "Argument 'application.uiThreadManager' must not be null");
061 this.windowDisplayHandler = requireNonNull(windowDisplayHandler, "Argument 'windowDisplayHandler' must not be null");
062 }
063
064 protected GriffonApplication getApplication() {
065 return application;
066 }
067
068 @Nonnull
069 @Override
070 public Set<String> getWindowNames() {
071 return Collections.unmodifiableSet(windows.keySet());
072 }
073
074 @Nullable
075 @Override
076 public String findWindowName(@Nonnull W window) {
077 requireNonNull(window, ERROR_WINDOW_NULL);
078 synchronized (windows) {
079 for (Map.Entry<String, W> e : windows.entrySet()) {
080 if (e.getValue().equals(window)) {
081 return e.getKey();
082 }
083 }
084 }
085 return null;
086 }
087
088 @Override
089 public int indexOf(@Nonnull W window) {
090 requireNonNull(window, ERROR_WINDOW_NULL);
091 synchronized (windows) {
092 int index = 0;
093 for (W w : windows.values()) {
094 if (window.equals(w)) {
095 return index;
096 }
097 index++;
098 }
099 }
100 return -1;
101 }
102
103 @Override
104 @Nullable
105 public W findWindow(@Nonnull String name) {
106 requireNonBlank(name, ERROR_NAME_BLANK);
107 return windows.get(name);
108 }
109
110 @Override
111 @Nullable
112 public W getAt(int index) {
113 synchronized (windows) {
114 int size = windows.size();
115 if (index < 0 || index >= size) {
116 throw new ArrayIndexOutOfBoundsException(index);
117 }
118
119 int i = 0;
120 for (W window : windows.values()) {
121 if (index == i++) {
122 return window;
123 }
124 }
125 }
126 throw new ArrayIndexOutOfBoundsException(index);
127 }
128
129 @Override
130 @Nullable
131 public W getStartingWindow() {
132 W window;
133 Object value = resolveStartingWindowFromConfiguration();
134 LOG.debug("windowManager.startingWindow configured to {}", value);
135
136 if (value instanceof String) {
137 String windowName = (String) value;
138 LOG.debug("Selecting window {} as starting window", windowName);
139 window = findWindow(windowName);
140 } else if (value instanceof Number) {
141 int index = ((Number) value).intValue();
142 LOG.debug("Selecting window at index {} as starting window", index);
143 window = getAt(index);
144 } else {
145 LOG.debug("No startingWindow configured, selecting the first one from the windows list");
146 window = getAt(0);
147 }
148
149 LOG.debug("Starting Window is {}", window);
150
151 return window;
152 }
153
154 @Nullable
155 protected Object resolveStartingWindowFromConfiguration() {
156 return application.getConfiguration().get("windowManager.startingWindow", null);
157 }
158
159 @Override
160 @Nonnull
161 public Collection<W> getWindows() {
162 return unmodifiableCollection(windows.values());
163 }
164
165 @Override
166 public void attach(@Nonnull String name, @Nonnull W window) {
167 requireNonBlank(name, ERROR_NAME_BLANK);
168 requireNonNull(window, ERROR_WINDOW_NULL);
169 if (windows.containsKey(name)) {
170 W window2 = windows.get(name);
171 if (window2 != window) {
172 detach(name);
173 }
174 }
175
176 doAttach(window);
177
178 LOG.debug("Attaching window with name: '{}' at index {} {}", name, windows.size(), window);
179 windows.put(name, window);
180 event(ApplicationEvent.WINDOW_ATTACHED, asList(name, window));
181 }
182
183 protected abstract void doAttach(@Nonnull W window);
184
185 @Override
186 public void detach(@Nonnull String name) {
187 requireNonBlank(name, ERROR_NAME_BLANK);
188 if (windows.containsKey(name)) {
189 W window = windows.get(name);
190
191 doDetach(window);
192
193 LOG.debug("Detaching window with name: '{}' {}", name, window);
194 windows.remove(name);
195 event(ApplicationEvent.WINDOW_DETACHED, asList(name, window));
196 }
197 }
198
199 protected abstract void doDetach(@Nonnull W window);
200
201 @Override
202 public void show(@Nonnull final W window) {
203 requireNonNull(window, ERROR_WINDOW_NULL);
204 if (!windows.containsValue(window)) {
205 return;
206 }
207
208 String windowName = null;
209 int windowIndex = -1;
210 synchronized (windows) {
211 int i = 0;
212 for (Map.Entry<String, W> entry : windows.entrySet()) {
213 if (entry.getValue() == window) {
214 windowName = entry.getKey();
215 windowIndex = i;
216 break;
217 }
218 i++;
219 }
220 }
221
222 final String name = windowName;
223 final int index = windowIndex;
224
225 application.getUIThreadManager().runInsideUIAsync(new Runnable() {
226 public void run() {
227 LOG.debug("Showing window with name: '{}' at index {} {}", name, index, window);
228 //noinspection ConstantConditions
229 resolveWindowDisplayHandler().show(name, window);
230 }
231 });
232 }
233
234 @Override
235 public void show(@Nonnull String name) {
236 requireNonBlank(name, ERROR_NAME_BLANK);
237 W window = findWindow(name);
238 if (window != null) {
239 show(window);
240 }
241 }
242
243 @Override
244 public void hide(@Nonnull final W window) {
245 requireNonNull(window, ERROR_WINDOW_NULL);
246 if (!windows.containsValue(window)) {
247 return;
248 }
249
250 String windowName = null;
251 int windowIndex = -1;
252 synchronized (windows) {
253 int i = 0;
254 for (Map.Entry<String, W> entry : windows.entrySet()) {
255 if (entry.getValue() == window) {
256 windowName = entry.getKey();
257 windowIndex = i;
258 break;
259 }
260 i++;
261 }
262 }
263
264 final String name = windowName;
265 final int index = windowIndex;
266
267 application.getUIThreadManager().runInsideUIAsync(new Runnable() {
268 public void run() {
269 LOG.debug("Hiding window with name: '{}' at index {} {}", name, index, window);
270 //noinspection ConstantConditions
271 resolveWindowDisplayHandler().hide(name, window);
272 }
273 });
274 }
275
276 @Nonnull
277 protected WindowDisplayHandler<W> resolveWindowDisplayHandler() {
278 return windowDisplayHandler;
279 }
280
281 @Override
282 public void hide(@Nonnull String name) {
283 requireNonBlank(name, ERROR_NAME_BLANK);
284 W window = findWindow(name);
285 if (window != null) {
286 hide(window);
287 }
288 }
289
290 @Override
291 public boolean canShutdown(@Nonnull GriffonApplication app) {
292 return true;
293 }
294
295 @Override
296 public void onShutdown(@Nonnull GriffonApplication app) {
297 for (W window : windows.values()) {
298 if (isWindowVisible(window)) hide(window);
299 }
300 }
301
302 protected abstract boolean isWindowVisible(@Nonnull W window);
303
304 @Override
305 public int countVisibleWindows() {
306 int visibleWindows = 0;
307 for (W window : windows.values()) {
308 if (isWindowVisible(window)) {
309 visibleWindows++;
310 }
311 }
312 return visibleWindows;
313 }
314
315 @Override
316 public boolean isAutoShutdown() {
317 return application.getConfiguration().getAsBoolean("application.autoShutdown", true);
318 }
319
320 protected void event(@Nonnull ApplicationEvent evt, @Nonnull List<?> args) {
321 event(evt.getName(), args);
322 }
323
324 protected void event(@Nonnull String evt, @Nonnull List<?> args) {
325 try {
326 EventRouter eventRouter = getApplication().getEventRouter();
327 eventRouter.publishEvent(evt, args);
328 } catch (InstanceNotFoundException infe) {
329 if (getApplication().getPhase() != ApplicationPhase.SHUTDOWN) {
330 throw infe;
331 }
332 }
333 }
334 }
|