AbstractWindowManager.java
001 /*
002  * Copyright 2008-2017 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 < || 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 = null;
133         Object value = resolveStartingWindowFromConfiguration();
134         LOG.debug("windowManager.startingWindow configured to {}", value);
135 
136         if (value instanceof String) {
137             String windowName = (Stringvalue;
138             LOG.debug("Selecting window {} as starting window", windowName);
139             window = findWindow(windowName);
140         else if (value instanceof Number) {
141             int index = ((Numbervalue).intValue();
142             LOG.debug("Selecting window at index {} as starting window", index);
143             try {
144                 window = getAt(index);
145             catch (ArrayIndexOutOfBoundsException e) {
146                 LOG.warn("Window at index {} was not found", index);
147             }
148         else {
149             LOG.debug("No startingWindow configured, selecting the first one from the windows list");
150             try {
151                 window = getAt(0);
152             catch (ArrayIndexOutOfBoundsException e) {
153                 LOG.warn("Window at index 0 was not found");
154             }
155         }
156 
157         LOG.debug("Starting Window is {}", window);
158 
159         return window;
160     }
161 
162     @Nullable
163     protected Object resolveStartingWindowFromConfiguration() {
164         return application.getConfiguration().get("windowManager.startingWindow"null);
165     }
166 
167     @Override
168     @Nonnull
169     public Collection<W> getWindows() {
170         return unmodifiableCollection(windows.values());
171     }
172 
173     @Override
174     public void attach(@Nonnull String name, @Nonnull W window) {
175         requireNonBlank(name, ERROR_NAME_BLANK);
176         requireNonNull(window, ERROR_WINDOW_NULL);
177         if (windows.containsKey(name)) {
178             W window2 = windows.get(name);
179             if (window2 != window) {
180                 detach(name);
181             }
182         }
183 
184         doAttach(window);
185 
186         LOG.debug("Attaching window with name: '{}' at index {} {}", name, windows.size(), window);
187         windows.put(name, window);
188         event(ApplicationEvent.WINDOW_ATTACHED, asList(name, window));
189     }
190 
191     protected abstract void doAttach(@Nonnull W window);
192 
193     @Override
194     public void detach(@Nonnull String name) {
195         requireNonBlank(name, ERROR_NAME_BLANK);
196         if (windows.containsKey(name)) {
197             W window = windows.get(name);
198 
199             doDetach(window);
200 
201             LOG.debug("Detaching window with name: '{}' {}", name, window);
202             windows.remove(name);
203             event(ApplicationEvent.WINDOW_DETACHED, asList(name, window));
204         }
205     }
206 
207     protected abstract void doDetach(@Nonnull W window);
208 
209     @Override
210     public void show(@Nonnull final W window) {
211         requireNonNull(window, ERROR_WINDOW_NULL);
212         if (!windows.containsValue(window)) {
213             return;
214         }
215 
216         String windowName = null;
217         int windowIndex = -1;
218         synchronized (windows) {
219             int i = 0;
220             for (Map.Entry<String, W> entry : windows.entrySet()) {
221                 if (entry.getValue() == window) {
222                     windowName = entry.getKey();
223                     windowIndex = i;
224                     break;
225                 }
226                 i++;
227             }
228         }
229 
230         final String name = windowName;
231         final int index = windowIndex;
232 
233         application.getUIThreadManager().runInsideUIAsync(new Runnable() {
234             public void run() {
235                 LOG.debug("Showing window with name: '{}' at index {} {}", name, index, window);
236                 //noinspection ConstantConditions
237                 resolveWindowDisplayHandler().show(name, window);
238             }
239         });
240     }
241 
242     @Override
243     public void show(@Nonnull String name) {
244         requireNonBlank(name, ERROR_NAME_BLANK);
245         W window = findWindow(name);
246         if (window != null) {
247             show(window);
248         }
249     }
250 
251     @Override
252     public void hide(@Nonnull final W window) {
253         requireNonNull(window, ERROR_WINDOW_NULL);
254         if (!windows.containsValue(window)) {
255             return;
256         }
257 
258         String windowName = null;
259         int windowIndex = -1;
260         synchronized (windows) {
261             int i = 0;
262             for (Map.Entry<String, W> entry : windows.entrySet()) {
263                 if (entry.getValue() == window) {
264                     windowName = entry.getKey();
265                     windowIndex = i;
266                     break;
267                 }
268                 i++;
269             }
270         }
271 
272         final String name = windowName;
273         final int index = windowIndex;
274 
275         application.getUIThreadManager().runInsideUIAsync(new Runnable() {
276             public void run() {
277                 LOG.debug("Hiding window with name: '{}' at index {} {}", name, index, window);
278                 //noinspection ConstantConditions
279                 resolveWindowDisplayHandler().hide(name, window);
280             }
281         });
282     }
283 
284     @Nonnull
285     protected WindowDisplayHandler<W> resolveWindowDisplayHandler() {
286         return windowDisplayHandler;
287     }
288 
289     @Override
290     public void hide(@Nonnull String name) {
291         requireNonBlank(name, ERROR_NAME_BLANK);
292         W window = findWindow(name);
293         if (window != null) {
294             hide(window);
295         }
296     }
297 
298     @Override
299     public boolean canShutdown(@Nonnull GriffonApplication app) {
300         return true;
301     }
302 
303     @Override
304     public void onShutdown(@Nonnull GriffonApplication app) {
305         for (W window : windows.values()) {
306             if (isWindowVisible(window)) { hide(window)}
307         }
308     }
309 
310     protected abstract boolean isWindowVisible(@Nonnull W window);
311 
312     @Override
313     public int countVisibleWindows() {
314         int visibleWindows = 0;
315         for (W window : windows.values()) {
316             if (isWindowVisible(window)) {
317                 visibleWindows++;
318             }
319         }
320         return visibleWindows;
321     }
322 
323     @Override
324     public boolean isAutoShutdown() {
325         return application.getConfiguration().getAsBoolean("application.autoShutdown"true);
326     }
327 
328     protected void event(@Nonnull ApplicationEvent evt, @Nonnull List<?> args) {
329         event(evt.getName(), args);
330     }
331 
332     protected void event(@Nonnull String evt, @Nonnull List<?> args) {
333         try {
334             EventRouter eventRouter = getApplication().getEventRouter();
335             eventRouter.publishEvent(evt, args);
336         catch (InstanceNotFoundException infe) {
337             if (getApplication().getPhase() != ApplicationPhase.SHUTDOWN) {
338                 throw infe;
339             }
340         }
341     }
342 }