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