minor refactor in UseObjectManager and bugfix for NPE during application start (...
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / MultiPageTaxonEditor.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.taxeditor.editor;
11
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.UUID;
15
16 import org.eclipse.core.commands.operations.IUndoContext;
17 import org.eclipse.core.commands.operations.UndoContext;
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.jface.dialogs.MessageDialog;
20 import org.eclipse.ui.IEditorInput;
21 import org.eclipse.ui.IEditorPart;
22 import org.eclipse.ui.IEditorSite;
23 import org.eclipse.ui.PartInitException;
24 import org.eclipse.ui.forms.editor.FormEditor;
25
26 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
27 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
28 import eu.etaxonomy.cdm.model.common.CdmBase;
29 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
30 import eu.etaxonomy.cdm.model.taxon.Taxon;
31 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
32 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
33 import eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor;
34 import eu.etaxonomy.taxeditor.editor.name.container.AbstractGroupedContainer;
35 import eu.etaxonomy.taxeditor.model.DataChangeBridge;
36 import eu.etaxonomy.taxeditor.model.IDataChangeBehavior;
37 import eu.etaxonomy.taxeditor.model.IDirtyMarkableSelectionProvider;
38 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
39 import eu.etaxonomy.taxeditor.operation.IPostOperationEnabled;
40
41 /**
42 *
43 * Generates the tabbed editor with <code>TaxonNameEditor</code> on top and tabs
44 * for "Descriptions", "Concepts", "Geography", etc.
45 *
46 * @author p.ciardelli
47 * @author n.hoffmann
48 * @created 15.05.2008
49 * @version 1.0
50 */
51 public class MultiPageTaxonEditor extends FormEditor implements
52 IConversationEnabled, IPostOperationEnabled,
53 IDirtyMarkableSelectionProvider, IPartContentHasDetails {
54
55 /** Constant <code>ID="eu.etaxonomy.taxeditor.editor.taxon"{trunked}</code> */
56 public static final String ID = "eu.etaxonomy.taxeditor.editor.taxon";
57
58 private boolean dirty;
59
60 private ConversationHolder conversation;
61 private IDataChangeBehavior dataChangeBehavior;
62 private IUndoContext undoContext;
63
64 private TaxonEditorInput input;
65
66 /**
67 * <p>
68 * Constructor for MultiPageTaxonEditor.
69 * </p>
70 */
71 public MultiPageTaxonEditor() {
72 super();
73 undoContext = new UndoContext();
74 }
75
76 /** {@inheritDoc} */
77 @Override
78 public void dispose() {
79 conversation.unregisterForDataStoreChanges(this);
80 conversation.close();
81 super.dispose();
82 }
83
84 /*
85 * (non-Javadoc)
86 *
87 * @see org.eclipse.ui.forms.editor.FormEditor#addPages()
88 */
89 /** {@inheritDoc} */
90 @Override
91 protected void addPages() {
92 input = (TaxonEditorInput) getEditorInput();
93 conversation = input.getConversationHolder();
94 conversation.registerForDataStoreChanges(this);
95
96 try {
97 addPage(Page.NAME.getIndex(), new TaxonNameEditor(this),
98 getEditorInput());
99 // setPageText(Page.NAME.getIndex(), Page.NAME.getTitle());
100
101 // TODO lazy create
102 // addPage(Page.DESCRIPTIVE.getIndex(), new
103 // TaxonDescriptionTreeEditor(this), getEditorInput());
104 // setPageText(Page.DESCRIPTIVE.getIndex(),
105 // Page.DESCRIPTIVE.getTitle());
106
107 // EditorUtil.showPropertySheet();
108
109 } catch (PartInitException e) {
110 EditorUtil.error(getClass(), e);
111 }
112 }
113
114 /** {@inheritDoc} */
115 @Override
116 public void doSave(IProgressMonitor monitor) {
117 monitor.beginTask("Saving Editor", 4);
118 try {
119 if (!conversation.isBound()) {
120 conversation.bind();
121 }
122 monitor.worked(1);
123
124 for (IEditorPart editorPage : getPages()) {
125 if (editorPage instanceof TaxonNameEditor) {
126 if (((TaxonNameEditor) editorPage).checkForEmptyNames()) {
127 MessageDialog
128 .openWarning(
129 EditorUtil.getShell(),
130 "No Name Specified",
131 "An attempt was made to save a taxon or synonym with "
132 + "an empty name. Operation was cancelled.");
133 return;
134 }
135 }
136
137 editorPage.doSave(monitor);
138 monitor.worked(1);
139 }
140
141 // commit the conversation and start a new transaction immediately
142 conversation.commit(true);
143 monitor.worked(1);
144
145 this.setDirty(false);
146 monitor.worked(1);
147 } catch (Exception e) {
148 setFocus();
149 EditorUtil
150 .errorDialog(
151 "An error occurred while saving",
152 getClass(),
153 "An error occurred while saving the editor. Please close and reopen the taxon again.",
154 e);
155 disableEditor();
156 } finally {
157 monitor.done();
158 }
159 }
160
161 private void disableEditor() {
162 for (IMultiPageTaxonEditorPage editorPage : getPages()) {
163 editorPage.setDisabled();
164 }
165 conversation.unregisterForDataStoreChanges(this);
166 conversation.close();
167 setDirty(false);
168 }
169
170 private void setDirty(boolean dirty) {
171 this.dirty = dirty;
172 firePropertyChange(PROP_DIRTY);
173 }
174
175 /*
176 * (non-Javadoc)
177 *
178 * @see org.eclipse.ui.part.MultiPageEditorPart#isDirty()
179 */
180 /**
181 * <p>
182 * isDirty
183 * </p>
184 *
185 * @return a boolean.
186 */
187 public boolean isDirty() {
188 return dirty;
189 }
190
191 /*
192 * (non-Javadoc)
193 *
194 * @see org.eclipse.ui.forms.editor.FormEditor#editorDirtyStateChanged()
195 */
196 /** {@inheritDoc} */
197 @Override
198 public void editorDirtyStateChanged() {
199 dirty = true;
200 super.editorDirtyStateChanged();
201 }
202
203 /**
204 * {@inheritDoc}
205 *
206 * Checks whether nested editors are calling
207 * <code>firePropertyChange(PROP_DIRTY)</code> to signal an edit has taken
208 * place before passing property change along to
209 * <code>super.handlePropertyChange(int propertyId)</code>.
210 */
211 /*
212 * (non-Javadoc)
213 *
214 * @see org.eclipse.ui.part.MultiPageEditorPart#handlePropertyChange(int)
215 */
216 protected void handlePropertyChange(int propertyId) {
217 if (propertyId == PROP_DIRTY) {
218 setDirty(true);
219 }
220 super.handlePropertyChange(propertyId);
221 }
222
223 /** {@inheritDoc} */
224 @Override
225 public void doSaveAs() {
226 }
227
228 /** {@inheritDoc} */
229 @Override
230 public boolean isSaveAsAllowed() {
231 return false;
232 }
233
234 /** {@inheritDoc} */
235 @Override
236 public void init(IEditorSite site, IEditorInput input)
237 throws PartInitException {
238
239 if (!(input instanceof TaxonEditorInput))
240 throw new PartInitException(
241 "Invalid Input: Must be TaxonEditorInput");
242
243 this.input = (TaxonEditorInput) input;
244
245 // try {
246 // // Listen for name changes,
247 // // change tab for this taxon editor accordingly
248 // getTaxon().addPropertyChangeListener("name",
249 // new PropertyChangeListener() {
250 // public void propertyChange(PropertyChangeEvent e) {
251 // setPartName();
252 // }
253 // });
254 // } catch (NullPointerException e) {
255 // EditorUtil.warn(getClass(),
256 // "Caught an NPE while initing an editor. This is most " +
257 // "likely due to the unsuccesful attempt to restore the former " +
258 // "state of the application. We ignore this because the workbench " +
259 // "will simply be reset.");
260 // }
261 setPartName();
262
263 super.init(site, input);
264 }
265
266 /**
267 * Calls <code>MultiPageEditorPart.setPartName(String partName)</code> with
268 * text appropriate to the state of the taxon: any taxon that has been saved
269 * will by necessity have a name to display; a new taxon should display
270 * "New taxon" in the editor tab.
271 */
272 protected void setPartName() {
273
274 String partName = null;
275 TaxonNameBase<?, ?> name = getTaxon().getName();
276
277 if (name != null) {
278 partName = name.getTitleCache();
279 }
280
281 if (partName == null || partName.equals("")) {
282 partName = ("New taxon");
283 }
284
285 setPartName(partName);
286 }
287
288 /**
289 * {@inheritDoc}
290 *
291 * Editor pages call this in their postOperation to notify the
292 * MultiPageTaxonEditor of unsaved changes
293 */
294 public void changed(Object element) {
295 // setDirty(true);
296 dirty = true;
297 super.editorDirtyStateChanged();
298 if (element instanceof TaxonBase) {
299 TaxonNameEditor page = (TaxonNameEditor) getPage(Page.NAME);
300 AbstractGroupedContainer container = page
301 .getContainer((TaxonBase) element);
302 if (container != null) {
303 container.refresh();
304 }
305 }
306 }
307
308 /**
309 * The accepted taxon that is the input for this editor
310 *
311 * @return the accepted taxon
312 */
313 public Taxon getTaxon() {
314 return input.getTaxon();
315 }
316
317 /*
318 * (non-Javadoc)
319 *
320 * @see
321 * eu.etaxonomy.cdm.api.conversation.IConversationEnabled#getConversationHolder
322 * ()
323 */
324 /**
325 * <p>
326 * getConversationHolder
327 * </p>
328 *
329 * @return a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
330 * object.
331 */
332 public ConversationHolder getConversationHolder() {
333 return conversation;
334 }
335
336 /**
337 * <p>
338 * setConversationHolder
339 * </p>
340 *
341 * @param conversation
342 * a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
343 * object.
344 */
345 public void setConversationHolder(ConversationHolder conversation) {
346 this.conversation = conversation;
347 }
348
349 /**
350 * <p>
351 * Getter for the field <code>undoContext</code>.
352 * </p>
353 *
354 * @return a {@link org.eclipse.core.commands.operations.IUndoContext}
355 * object.
356 */
357 public IUndoContext getUndoContext() {
358 return undoContext;
359 }
360
361 /**
362 * <p>
363 * Setter for the field <code>undoContext</code>.
364 * </p>
365 *
366 * @param undoContext
367 * a {@link org.eclipse.core.commands.operations.IUndoContext}
368 * object.
369 */
370 public void setUndoContext(IUndoContext undoContext) {
371 this.undoContext = undoContext;
372 }
373
374 /** {@inheritDoc} */
375 @Override
376 public void setFocus() {
377 // logger.warn("Setting focus to editor");
378 // bind the conversation
379 getConversationHolder().bind();
380 // pass focus to the active editor page
381 getActiveEditor().setFocus();
382 }
383
384 /*
385 * (non-Javadoc)
386 *
387 * @see
388 * eu.etaxonomy.cdm.persistence.hibernate.ICdmPostCrudObserver#update(eu
389 * .etaxonomy.cdm.persistence.hibernate.CdmCrudEvent)
390 */
391 /** {@inheritDoc} */
392 public void update(CdmDataChangeMap events) {
393 if (dataChangeBehavior == null) {
394 dataChangeBehavior = new MultiPageTaxonEditorDataChangeBehaviour(
395 this);
396 }
397
398 DataChangeBridge.handleDataChange(events, dataChangeBehavior);
399 }
400
401 /*
402 * (non-Javadoc)
403 *
404 * @see
405 * eu.etaxonomy.taxeditor.store.operations.IPostOperationEnabled#postOperation
406 * ()
407 */
408 /** {@inheritDoc} */
409 public boolean postOperation(CdmBase objectAffectedByOperation) {
410 setDirty(true);
411
412 for (IEditorPart editor : this.getPages()) {
413 if (editor instanceof IPostOperationEnabled) {
414 ((IPostOperationEnabled) editor)
415 .postOperation(objectAffectedByOperation);
416 } else {
417 EditorUtil.warn(getClass(),
418 "postOperation not enabled for editor " + editor);
419 }
420 }
421 EditorUtil
422 .warn(getClass(),
423 "postOperation called on MultiPageTaxonEditor. Can you make it more specific?");
424
425 return false;
426 }
427
428 /**
429 * Returns an <code>IEditorPart</code> implementation by type
430 *
431 * @param page
432 * the page type
433 * @return a {@link eu.etaxonomy.taxeditor.editor.IMultiPageTaxonEditorPage}
434 * object.
435 */
436 public IMultiPageTaxonEditorPage getPage(Page page) {
437 for (IEditorPart editor : this.getPages()) {
438 if (editor.getClass().equals(page.getClazz())) {
439 return (IMultiPageTaxonEditorPage) editor;
440 }
441 }
442 return null;
443 }
444
445 /**
446 * Return a list of <code>AbstractTaxonEditor</code>s registered with this
447 * <code>MultiPageTaxonEditor</code>.
448 *
449 * @return a {@link java.util.List} object.
450 */
451 public List<IMultiPageTaxonEditorPage> getPages() {
452 ArrayList<IMultiPageTaxonEditorPage> editors = new ArrayList<IMultiPageTaxonEditorPage>();
453 for (int i = 0; i < this.getPageCount(); i++) {
454
455 editors.add((IMultiPageTaxonEditorPage) this.getEditor(i));
456 }
457 return editors;
458 }
459
460 /**
461 * Refreshes a certain page of the MultipageTaxonEditor
462 *
463 * @param page
464 * a {@link eu.etaxonomy.taxeditor.editor.Page} object.
465 * @return a boolean.
466 */
467 public boolean redraw(Page page) {
468 return redraw(page, true);
469 }
470
471 /**
472 * Refreshes a certain page of the MultipageTaxonEditor and sets focus to
473 * that page
474 *
475 * @param page
476 * a {@link eu.etaxonomy.taxeditor.editor.Page} object.
477 * @param focus
478 * a boolean.
479 * @return a boolean.
480 */
481 public boolean redraw(Page page, boolean focus) {
482 IMultiPageTaxonEditorPage editorPage = getPage(page);
483 return editorPage != null && editorPage.redraw(focus);
484 }
485
486 /**
487 * <p>
488 * onComplete
489 * </p>
490 *
491 * @return a boolean.
492 */
493 public boolean onComplete() {
494 return false;
495 }
496
497 /**
498 * Reloads the data for this
499 */
500 public void reload() {
501 if (isDirty()) {
502 EditorUtil
503 .warningDialog(
504 "Editor has unsaved data",
505 getClass(),
506 "This editor can not be "
507 + "refreshed because it contains unsaved data. Refreshing "
508 + "this editor would discard the changes. Please save this editor, "
509 + "close and reopen it manually in order to get the latest content");
510 } else {
511 TaxonEditorInput input = (TaxonEditorInput) getEditorInput();
512
513 UUID uuid = input.getTaxonNode().getUuid();
514
515 conversation.clear();
516
517 try {
518 TaxonEditorInput newInput = TaxonEditorInput.NewInstance(uuid);
519 setInput(newInput);
520 for (IMultiPageTaxonEditorPage editorPart : getPages()) {
521 editorPart.redraw();
522 }
523 } catch (Exception e) {
524 EditorUtil.errorDialog("Error refreshing editor", getClass(),
525 "Could not refresh this editor", e);
526 }
527 }
528 }
529
530 @Override
531 public String toString() {
532 return String.format("%s[%s]", this.getClass().getSimpleName(), getEditorInput());
533 }
534 }