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