AbstractWindowManager.java
001 /*
002  * Copyright 2008-2016 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;
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             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 }