Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
Application |
|
| 2.642857142857143;2.643 |
1 | /* | |
2 | * Copyright (c) 2009, Swiss Federal Department of Defence Civil Protection and Sport | |
3 | * (http://www.vbs.admin.ch) | |
4 | * Copyright (c) 2009, Ergon Informatik AG (http://www.ergon.ch) | |
5 | * All rights reserved. | |
6 | * | |
7 | * Licensed under the Open Permis License which accompanies this distribution, | |
8 | * and is available at http://www.openpermis.org/BSDlicenceKent.txt | |
9 | */ | |
10 | package org.openpermis.editor.policy; | |
11 | ||
12 | import java.io.File; | |
13 | import java.io.IOException; | |
14 | import java.io.InputStream; | |
15 | import java.net.URL; | |
16 | import java.util.Enumeration; | |
17 | import java.util.Properties; | |
18 | ||
19 | import javax.swing.JFileChooser; | |
20 | import javax.swing.JOptionPane; | |
21 | ||
22 | import org.jdesktop.application.Action; | |
23 | import org.jdesktop.application.ApplicationActionMap; | |
24 | import org.jdesktop.application.ResourceMap; | |
25 | import org.jdesktop.application.SingleFrameApplication; | |
26 | import org.picocontainer.MutablePicoContainer; | |
27 | import org.picocontainer.PicoBuilder; | |
28 | import org.picocontainer.PicoContainer; | |
29 | import org.slf4j.Logger; | |
30 | import org.slf4j.LoggerFactory; | |
31 | ||
32 | import org.openpermis.editor.policy.adapter.BasicAdapterTrader; | |
33 | import org.openpermis.editor.policy.gui.PolicyIconRegistry; | |
34 | import org.openpermis.editor.policy.view.PolicyView; | |
35 | ||
36 | /** | |
37 | * The singleton policy editor application instance. | |
38 | * <p>In addition to the usual {@link org.jdesktop.application.Application} responsibilities | |
39 | * the policy editor application instance manages the persistent application state.</p> | |
40 | * @since 0.1.0 | |
41 | */ | |
42 | 0 | public class Application |
43 | extends SingleFrameApplication | |
44 | { | |
45 | ||
46 | //---- Static | |
47 | ||
48 | /** | |
49 | * The logger object of this class. | |
50 | * @since 0.1.0 | |
51 | */ | |
52 | 0 | private static final Logger LOGGER = |
53 | LoggerFactory.getLogger(Application.class); | |
54 | ||
55 | /** | |
56 | * Name of the file that holds the persistent application state. | |
57 | * @since 0.1.0 | |
58 | */ | |
59 | private static final String APPLICATION_STATE = "state.xml"; | |
60 | ||
61 | /** | |
62 | * The classpath location of the version property file. | |
63 | * @since 0.3.0 | |
64 | */ | |
65 | private static final String VERSION_FILE = "version.properties"; | |
66 | ||
67 | /** | |
68 | * The version file module name of the editor. | |
69 | * <p>This is used to identify the versioning file for the editor.</p> | |
70 | * @since 0.3.0 | |
71 | */ | |
72 | private static final String VERSION_APPLICATION = "openpermis"; | |
73 | ||
74 | /** | |
75 | * Returns the singleton policy editor application instance. | |
76 | * @return the singleton policy editor application instance. | |
77 | * @since 0.1.0 | |
78 | */ | |
79 | public static synchronized Application getInstance () { | |
80 | 0 | return Application.class.cast(org.jdesktop.application.Application.getInstance()); |
81 | } | |
82 | ||
83 | ||
84 | //---- State | |
85 | ||
86 | /** | |
87 | * The dependency injection container of this application. | |
88 | * @see #createContainer(Object...) | |
89 | * @since 0.1.0 | |
90 | */ | |
91 | private PicoContainer container; | |
92 | ||
93 | /** | |
94 | * The persistent state of this application. | |
95 | * @see #getApplicationState() | |
96 | * @see #shutdown() | |
97 | * @since 0.1.0 | |
98 | */ | |
99 | private ApplicationState state; | |
100 | ||
101 | //---- Methods | |
102 | ||
103 | /** | |
104 | * Returns the context resource map of this class. | |
105 | * @return the context resource map of this class. | |
106 | * @since 0.1.0 | |
107 | */ | |
108 | private ResourceMap getResourceMap () { | |
109 | 0 | return getContext().getResourceMap(Application.class); |
110 | } | |
111 | ||
112 | /** | |
113 | * Returns the context application action map of this class. | |
114 | * @return the context application action map of this class. | |
115 | * @since 0.1.0 | |
116 | */ | |
117 | private ApplicationActionMap getActionMap () { | |
118 | 0 | return getContext().getActionMap(Application.class, this); |
119 | } | |
120 | ||
121 | /** | |
122 | * Reads the version properties of the editor. | |
123 | * <p>The array returned will contain the master version, the build number and date.</p> | |
124 | * @return the version property array, {@code null} if no versioning information is available. | |
125 | * @since 0.3.0 | |
126 | */ | |
127 | private String[] readVersionProperties () { | |
128 | try { | |
129 | 0 | final Enumeration<URL> versionFiles = |
130 | Application.class.getClassLoader().getResources(VERSION_FILE); | |
131 | 0 | while (versionFiles.hasMoreElements()) { |
132 | 0 | final URL versionFile = versionFiles.nextElement(); |
133 | 0 | final Properties p = new Properties(); |
134 | try { | |
135 | 0 | final InputStream is = versionFile.openStream(); |
136 | 0 | if (is != null) { |
137 | 0 | p.load(is); |
138 | 0 | if (VERSION_APPLICATION.equals(p.getProperty("version.application"))) { |
139 | 0 | final String master = p.getProperty("version.master"); |
140 | 0 | final String build = p.getProperty("version.build"); |
141 | 0 | final String date = p.getProperty("version.date"); |
142 | 0 | return new String[] { |
143 | master == null ? "?" : master.trim(), | |
144 | build == null ? "?" : build.trim(), | |
145 | date == null ? "?" : date.trim() | |
146 | }; | |
147 | } | |
148 | } | |
149 | 0 | } catch (IOException e) { |
150 | 0 | LOGGER.debug("Cannot parse version file [" + versionFile + "].", e); |
151 | 0 | } |
152 | 0 | } |
153 | 0 | } catch (Exception e) { |
154 | 0 | LOGGER.debug("Excepted while reading versioning properties.", e); |
155 | 0 | } |
156 | 0 | return null; |
157 | } | |
158 | ||
159 | /** | |
160 | * Displays the application about box. | |
161 | * @since 0.1.0 | |
162 | */ | |
163 | @Action | |
164 | public void about () { | |
165 | 0 | final Object[] versionProperties = readVersionProperties(); |
166 | 0 | final String aboutText = "About.text" + (versionProperties == null ? ".noVersion" : ""); |
167 | 0 | final String aboutTitle = "About.title" + (versionProperties == null ? ".noVersion" : ""); |
168 | 0 | final Object[] args = versionProperties == null ? new Object[0] : versionProperties; |
169 | 0 | JOptionPane.showMessageDialog( |
170 | getMainFrame(), | |
171 | getResourceMap().getString(aboutText, args), | |
172 | getResourceMap().getString(aboutTitle, args), | |
173 | JOptionPane.PLAIN_MESSAGE, | |
174 | getResourceMap().getIcon("About.icon") | |
175 | ); | |
176 | 0 | } |
177 | ||
178 | /** | |
179 | * Action wrapper for the application exit. | |
180 | * @since 0.1.0 | |
181 | */ | |
182 | @Action | |
183 | public void quit () { | |
184 | 0 | exit(); |
185 | 0 | } |
186 | ||
187 | /** | |
188 | * Opens the file chooser and returns the user selection. | |
189 | * <p>The file chooser is displayed as dialog with the application frame | |
190 | * as parent and will use the specified file chooser type.</p> | |
191 | * @param type the file chooser type, either {@link JFileChooser#OPEN_DIALOG} or | |
192 | * {@link JFileChooser#SAVE_DIALOG}. | |
193 | * @param addToRecentFiles wether to add current file to the recent files list. | |
194 | * @param filter file filter, possibly null. | |
195 | * @return the file chosen or <code>null</code> if the user cancelled. | |
196 | * @since 0.1.0 | |
197 | */ | |
198 | public File chooseFile (int type, boolean addToRecentFiles, PolicyFileFilter filter) { | |
199 | ||
200 | // create chooser | |
201 | 0 | final JFileChooser chooser = new JFileChooser(); |
202 | 0 | chooser.setCurrentDirectory(getApplicationState().getWorkingDirectoryFile()); |
203 | 0 | chooser.setDialogType(type); |
204 | 0 | chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); |
205 | ||
206 | // set chooser filter | |
207 | 0 | if (filter != null) { |
208 | 0 | chooser.setFileFilter(filter); |
209 | } | |
210 | ||
211 | // handle chooser result | |
212 | 0 | if (chooser.showDialog(getMainFrame(), null) == JFileChooser.APPROVE_OPTION) { |
213 | 0 | final File file = chooser.getSelectedFile(); |
214 | 0 | getApplicationState().setWorkingDirectoryFile(file); |
215 | 0 | if (addToRecentFiles && file.isFile()) { |
216 | 0 | getApplicationState().addRecentFiles(file.getAbsolutePath()); |
217 | } | |
218 | 0 | return file; |
219 | } | |
220 | ||
221 | // return | |
222 | 0 | return null; |
223 | } | |
224 | ||
225 | /** | |
226 | * Loads the persistent application state. | |
227 | * @return the application state or a default state if there is either no | |
228 | * application state or there is an error reading the state. | |
229 | * @since 0.1.0 | |
230 | */ | |
231 | private ApplicationState loadApplicationState () { | |
232 | 0 | ApplicationState appState = null; |
233 | try { | |
234 | 0 | appState = (ApplicationState) getContext().getLocalStorage().load(APPLICATION_STATE); |
235 | 0 | } catch (Exception e) { |
236 | 0 | LOGGER.warn("Application state could not be loaded.", e); |
237 | 0 | } |
238 | 0 | if (appState == null) { |
239 | 0 | appState = new ApplicationState(); |
240 | 0 | LOGGER.debug("Creating default application state [{}].", appState); |
241 | } else { | |
242 | 0 | LOGGER.debug("Application state loaded [{}].", appState); |
243 | } | |
244 | 0 | return appState; |
245 | } | |
246 | ||
247 | /** | |
248 | * Stores the application state persistently in the application state file. | |
249 | * <p>Errors while storing the application state are ignored. | |
250 | * @param appState the state to store. | |
251 | * @since 0.1.0 | |
252 | */ | |
253 | private void storeApplicationState (ApplicationState appState) { | |
254 | try { | |
255 | 0 | LOGGER.debug("Storing application state [{}].", appState); |
256 | 0 | getContext().getLocalStorage().save(appState, APPLICATION_STATE); |
257 | 0 | } catch (IOException e) { |
258 | 0 | LOGGER.warn("Application state could not be stored.", e); |
259 | 0 | } |
260 | 0 | } |
261 | ||
262 | /** | |
263 | * Returns the application state. | |
264 | * <p>The state is lazily loaded or initialized on first use.</p> | |
265 | * @return the application state requested. | |
266 | * @since 0.1.0 | |
267 | */ | |
268 | public synchronized ApplicationState getApplicationState () { | |
269 | 0 | if (this.state == null) { |
270 | 0 | this.state = loadApplicationState(); |
271 | } | |
272 | 0 | return this.state; |
273 | } | |
274 | ||
275 | /** | |
276 | * Creates and configures the pico container for this application. | |
277 | * @param components an array of components to add. | |
278 | * @return the dependency injection container for this application. | |
279 | * @since 0.1.0 | |
280 | * @since 0.3.0 added components. | |
281 | */ | |
282 | private final PicoContainer createContainer (Object... components) { | |
283 | 0 | final MutablePicoContainer pico = new PicoBuilder().build(); |
284 | 0 | pico.addComponent(Application.class, this); |
285 | 0 | pico.addComponent(getContext()); |
286 | 0 | pico.addComponent(PolicyView.class); |
287 | 0 | pico.addComponent(BasicAdapterTrader.class); |
288 | 0 | pico.addAdapter(new FrameInjector()); |
289 | 0 | for (Object component : components) { |
290 | 0 | pico.addComponent(component); |
291 | } | |
292 | 0 | return pico; |
293 | } | |
294 | ||
295 | //---- Application | |
296 | ||
297 | /** | |
298 | * @since 0.1.0 | |
299 | */ | |
300 | @Override | |
301 | protected void initialize (final String[] args) { | |
302 | 0 | LOGGER.debug("Initializing application."); |
303 | 0 | super.initialize(args); |
304 | 0 | if (MacAdapter.isMac()) { |
305 | 0 | final MacAdapter adapter = new MacAdapter(); |
306 | 0 | adapter.registerAboutAction(getActionMap().get("about")); |
307 | 0 | adapter.registerPreferencesAction(getActionMap().get("preferences")); |
308 | 0 | adapter.registerQuitAction(getActionMap().get("quit")); |
309 | 0 | adapter.setDockImage(getResourceMap().getImageIcon("About.icon").getImage()); |
310 | } | |
311 | 0 | this.container = createContainer(PolicyIconRegistry.create(getContext())); |
312 | 0 | } |
313 | ||
314 | /** | |
315 | * @since 0.1.0 | |
316 | */ | |
317 | @Override | |
318 | protected void shutdown () { | |
319 | 0 | LOGGER.debug("Terminating application."); |
320 | 0 | storeApplicationState(getApplicationState()); |
321 | 0 | LOGGER.debug("Shutdown complete."); |
322 | 0 | super.shutdown(); |
323 | 0 | } |
324 | ||
325 | /** | |
326 | * @since 0.1.0 | |
327 | */ | |
328 | @Override | |
329 | protected void startup () { | |
330 | 0 | LOGGER.debug("Starting application."); |
331 | 0 | final PolicyView policyView = this.container.getComponent(PolicyView.class); |
332 | 0 | show(policyView.configure(getMainView())); |
333 | 0 | } |
334 | ||
335 | } |