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