001: /*
002: *
003: * JMoney - A Personal Finance Manager
004: * Copyright (c) 2002 Johann Gyger <johann.gyger@switzerland.org>
005: * Copyright (c) 2004 Nigel Westbury <westbury@users.sourceforge.net>
006: *
007: *
008: * This program is free software; you can redistribute it and/or modify
009: * it under the terms of the GNU General Public License as published by
010: * the Free Software Foundation; either version 2 of the License, or
011: * (at your option) any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
021: *
022: */
023:
024: package net.sf.jmoney.serializeddatastore;
025:
026: import java.io.File;
027: import java.util.Collection;
028: import java.util.Collections;
029: import java.util.HashMap;
030: import java.util.Map;
031: import java.util.Vector;
032:
033: import net.sf.jmoney.model2.Account;
034: import net.sf.jmoney.model2.DatastoreManager;
035: import net.sf.jmoney.model2.Entry;
036: import net.sf.jmoney.model2.Session;
037:
038: import org.eclipse.core.runtime.Assert;
039: import org.eclipse.core.runtime.CoreException;
040: import org.eclipse.core.runtime.IConfigurationElement;
041: import org.eclipse.jface.dialogs.IDialogConstants;
042: import org.eclipse.jface.dialogs.MessageDialog;
043: import org.eclipse.swt.SWT;
044: import org.eclipse.swt.widgets.FileDialog;
045: import org.eclipse.swt.widgets.MessageBox;
046: import org.eclipse.ui.IMemento;
047: import org.eclipse.ui.IPersistableElement;
048: import org.eclipse.ui.IWorkbenchWindow;
049:
050: /**
051: * Provides the ISessionManager implementation.
052: * All plug-ins that provide a datastore
053: * implementation must provide an implementation of the
054: * ISessionManager interface.
055: */
056: public class SessionManager extends DatastoreManager {
057:
058: Session session = null;
059:
060: String fileDatastoreId = null;
061:
062: IFileDatastore fileDatastore = null;
063:
064: File sessionFile = null;
065:
066: boolean modified = false;
067:
068: Map<Account, Collection<Entry>> accountEntriesListsMap = new HashMap<Account, Collection<Entry>>();
069:
070: /**
071: * Construct the session manager.
072: * <P>
073: * Note that the session manager is constructed without a session. A session
074: * must be created and set using the <code>setSession</code> method before
075: * this session manager is usable. You may think that the session should be
076: * passed to the constructor. However this is not done because then we would
077: * have a 'chicken and egg' problem. The session object constructor needs
078: * the SessionManager object. Hence the two part initialization of the
079: * session manager.
080: *
081: * @param fileDatastore
082: * @param fileFormatId
083: * @param sessionFile
084: */
085: public SessionManager(String fileFormatId,
086: IFileDatastore fileDatastore, File sessionFile) {
087: this .fileDatastoreId = fileFormatId;
088: this .fileDatastore = fileDatastore;
089: this .sessionFile = sessionFile;
090: this .session = null;
091: }
092:
093: /**
094: * Used for two-part construction.
095: */
096: public void setSession(Session session) {
097: this .session = session;
098: }
099:
100: @Override
101: public Session getSession() {
102: return session;
103: }
104:
105: public File getFile() {
106: return sessionFile;
107: }
108:
109: public void setFile(File file) {
110: this .sessionFile = file;
111:
112: // The brief description of this session contains the file name, so we must
113: // fire a change so views that show this session description are updated.
114: // FIXME: Title is not updated because this is commented out.
115: /*
116: fireEvent(
117: new ISessionChangeFirer() {
118: public void fire(SessionChangeListener listener) {
119: listener.sessionPropertyChange("briefDescription", null, getBriefDescription());
120: }
121: });
122: */
123: }
124:
125: private boolean isModified() {
126: return modified;
127: }
128:
129: /**
130: * This plug-in needs to know if a session has been modified so
131: * that it knows whether to save the session. This method must
132: * be called whenever the session is modified.
133: */
134: void setModified() {
135: modified = true;
136: }
137:
138: boolean requestSave(IWorkbenchWindow window) {
139: String title = SerializedDatastorePlugin
140: .getResourceString("MainFrame.saveOldSessionTitle");
141: String question = SerializedDatastorePlugin
142: .getResourceString("MainFrame.saveOldSessionQuestion");
143: MessageDialog dialog = new MessageDialog(window.getShell(),
144: title, null, // accept the default window icon
145: question, MessageDialog.QUESTION, new String[] {
146: IDialogConstants.YES_LABEL,
147: IDialogConstants.NO_LABEL,
148: IDialogConstants.CANCEL_LABEL }, 2); // CANCEL is the default
149:
150: int answer = dialog.open();
151: switch (answer) {
152: case 0: // YES
153: saveSession(window);
154: return true;
155: case 1: // NO
156: return true;
157: case 2: // CANCEL
158: return false;
159: default:
160: throw new RuntimeException("bad switch value");
161: }
162: }
163:
164: /**
165: * Saves the session in the selected file.
166: */
167: public void saveSession(IWorkbenchWindow window) {
168: if (getFile() == null) {
169: saveSessionAs(window);
170: } else {
171: fileDatastore.writeSession(this , getFile(), window);
172: modified = false;
173: }
174: }
175:
176: /**
177: * Prompt the user for a file name and save the session
178: * to that file. The user is prompted for a file even
179: * if a file is already set in this session manager.
180: *
181: * @param window the workbench window to be used for UI
182: */
183: public void saveSessionAs(IWorkbenchWindow window) {
184: File newSessionFile = obtainFileName(window);
185: if (newSessionFile != null) {
186: String fileName = newSessionFile.getName();
187: IConfigurationElement elements[] = SerializedDatastorePlugin
188: .getElements(fileName);
189:
190: if (elements.length == 0) {
191: /*
192: * The user has entered an extension that is not recognized.
193: */
194: MessageDialog
195: .openError(
196: window.getShell(),
197: "Invalid Filename",
198: "You have entered a file name with an unrecognized extension. The supported extensions can be found by using the 'Save as type' drop-down in the 'Save As' dialog.");
199: return;
200: }
201:
202: // TODO: It is possible that multiple plug-ins may
203: // use the same file extension. The only solution to
204: // this is to ask the user which format to use.
205:
206: // For time being, we simply use the first entry.
207: try {
208: fileDatastore = (IFileDatastore) elements[0]
209: .createExecutableExtension("class");
210: fileDatastoreId = elements[0].getDeclaringExtension()
211: .getNamespaceIdentifier()
212: + '.' + elements[0].getAttribute("id");
213: } catch (CoreException e) {
214: e.printStackTrace();
215: throw new RuntimeException("internal error");
216: }
217:
218: // Write the file and then set the session file to the new file.
219: // Note that we do not set the new session file until the file is
220: // successfully written. If the file cannot be written to the new
221: // file then we must leave the old file as the current file.
222: fileDatastore.writeSession(this , newSessionFile, window);
223: this .sessionFile = newSessionFile;
224:
225: modified = false;
226: }
227: }
228:
229: /**
230: * Obtain the file name if a file is not already associated with this session.
231: *
232: * @return true if a file name was obtained from the user,
233: * false if no file name was obtained.
234: */
235: public File obtainFileName(IWorkbenchWindow window) {
236: FileDialog dialog = new FileDialog(window.getShell(), SWT.SAVE);
237: dialog.setFilterExtensions(SerializedDatastorePlugin
238: .getFilterExtensions());
239: dialog.setFilterNames(SerializedDatastorePlugin
240: .getFilterNames());
241: String fileName = dialog.open();
242:
243: if (fileName != null) {
244: File file = new File(fileName);
245: if (dontOverwrite(file, window))
246: return null;
247:
248: return file;
249: }
250: return null;
251: }
252:
253: private boolean dontOverwrite(File file, IWorkbenchWindow window) {
254: if (file.exists()) {
255: String question = SerializedDatastorePlugin
256: .getResourceString("MainFrame.OverwriteExistingFile")
257: + " " + file.getPath() + "?";
258: String title = SerializedDatastorePlugin
259: .getResourceString("MainFrame.FileExists");
260:
261: boolean answer = MessageDialog.openQuestion(window
262: .getShell(), title, question);
263: return !answer;
264: } else {
265: return false;
266: }
267: }
268:
269: @Override
270: public boolean canClose(IWorkbenchWindow window) {
271: if (isModified()) {
272: return requestSave(window);
273: } else {
274: return true;
275: }
276: }
277:
278: @Override
279: public void close() {
280: // There is nothing to do here. No files, connections or other resources
281: // are kept open so there is nothing to close.
282: }
283:
284: @Override
285: public String getBriefDescription() {
286: if (sessionFile == null) {
287: return null;
288: } else {
289: return sessionFile.getName();
290: }
291: }
292:
293: private IPersistableElement persistableElement = new IPersistableElement() {
294: public String getFactoryId() {
295: return "net.sf.jmoney.serializeddatastore.SessionFactory";
296: }
297:
298: public void saveState(IMemento memento) {
299: // If no session file is set then the session has never been saved.
300: // Although the canClose method will have been called prior to this
301: // during the shutdown process, it is possible that the user answered
302: // 'no' when asked if the session should be saved. We write no file name
303: // in this situation and the code to re-create the session will, in this case,
304: // re-create an empty session.
305: if (sessionFile != null) {
306: memento.putString("fileFormatId", fileDatastoreId);
307: memento.putString("fileName", sessionFile.getPath());
308: }
309: }
310: };
311:
312: /* (non-Javadoc)
313: * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
314: */
315: public Object getAdapter(Class adapter) {
316: if (adapter == IPersistableElement.class) {
317: return persistableElement;
318: }
319: return null;
320: }
321:
322: /**
323: * @param account
324: * @param entry
325: */
326: public void addEntryToList(Account account, Entry entry) {
327: Collection<Entry> accountEntriesList = accountEntriesListsMap
328: .get(account);
329: accountEntriesList.add(entry);
330: }
331:
332: /**
333: * @param account
334: * @param entry
335: */
336: public void removeEntryFromList(Account account, Entry entry) {
337: Collection<Entry> accountEntriesList = accountEntriesListsMap
338: .get(account);
339: accountEntriesList.remove(entry);
340: }
341:
342: /**
343: * @param account
344: */
345: public void addAccountList(Account account) {
346: Assert.isTrue(!accountEntriesListsMap.containsKey(account));
347: accountEntriesListsMap.put(account, new Vector<Entry>());
348: }
349:
350: /**
351: * @param account
352: */
353: public void removeAccountList(Account account) {
354: Assert.isTrue(accountEntriesListsMap.containsKey(account));
355: accountEntriesListsMap.remove(account);
356: }
357:
358: @Override
359: public boolean hasEntries(Account account) {
360: Collection<Entry> entriesList = accountEntriesListsMap
361: .get(account);
362: Assert.isNotNull(entriesList);
363: return !entriesList.isEmpty();
364: }
365:
366: @Override
367: public Collection<Entry> getEntries(Account account) {
368: Collection<Entry> entriesList = accountEntriesListsMap
369: .get(account);
370: Assert.isNotNull(entriesList);
371: return Collections.unmodifiableCollection(entriesList);
372: }
373:
374: @Override
375: public void startTransaction() {
376: // Nothing to do
377: }
378:
379: @Override
380: public void commitTransaction() {
381: // Nothing to do
382: }
383: }
|