merge of security branch with trunk
[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, ISecuredEditor {
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(true);
156 } finally {
157 monitor.done();
158 }
159 }
160
161 private void disableEditor(boolean isOnError) {
162 for (IMultiPageTaxonEditorPage editorPage : getPages()) {
163 if(isOnError){
164 editorPage.setOnError();
165 }else {
166 editorPage.setDisabled();
167 }
168 }
169 conversation.unregisterForDataStoreChanges(this);
170 conversation.close();
171 setDirty(false);
172 }
173
174 private void setDirty(boolean dirty) {
175 this.dirty = dirty;
176 firePropertyChange(PROP_DIRTY);
177 }
178
179 /*
180 * (non-Javadoc)
181 *
182 * @see org.eclipse.ui.part.MultiPageEditorPart#isDirty()
183 */
184 /**
185 * <p>
186 * isDirty
187 * </p>
188 *
189 * @return a boolean.
190 */
191 public boolean isDirty() {
192 return dirty;
193 }
194
195 /*
196 * (non-Javadoc)
197 *
198 * @see org.eclipse.ui.forms.editor.FormEditor#editorDirtyStateChanged()
199 */
200 /** {@inheritDoc} */
201 @Override
202 public void editorDirtyStateChanged() {
203 dirty = true;
204 super.editorDirtyStateChanged();
205 }
206
207 /**
208 * {@inheritDoc}
209 *
210 * Checks whether nested editors are calling
211 * <code>firePropertyChange(PROP_DIRTY)</code> to signal an edit has taken
212 * place before passing property change along to
213 * <code>super.handlePropertyChange(int propertyId)</code>.
214 */
215 /*
216 * (non-Javadoc)
217 *
218 * @see org.eclipse.ui.part.MultiPageEditorPart#handlePropertyChange(int)
219 */
220 protected void handlePropertyChange(int propertyId) {
221 if (propertyId == PROP_DIRTY) {
222 setDirty(true);
223 }
224 super.handlePropertyChange(propertyId);
225 }
226
227 /** {@inheritDoc} */
228 @Override
229 public void doSaveAs() {
230 }
231
232 /** {@inheritDoc} */
233 @Override
234 public boolean isSaveAsAllowed() {
235 return false;
236 }
237
238 /** {@inheritDoc} */
239 @Override
240 public void init(IEditorSite site, IEditorInput input)
241 throws PartInitException {
242
243 if (!(input instanceof TaxonEditorInput))
244 throw new PartInitException(
245 "Invalid Input: Must be TaxonEditorInput");
246
247 this.input = (TaxonEditorInput) input;
248
249 // try {
250 // // Listen for name changes,
251 // // change tab for this taxon editor accordingly
252 // getTaxon().addPropertyChangeListener("name",
253 // new PropertyChangeListener() {
254 // public void propertyChange(PropertyChangeEvent e) {
255 // setPartName();
256 // }
257 // });
258 // } catch (NullPointerException e) {
259 // EditorUtil.warn(getClass(),
260 // "Caught an NPE while initing an editor. This is most " +
261 // "likely due to the unsuccesful attempt to restore the former " +
262 // "state of the application. We ignore this because the workbench " +
263 // "will simply be reset.");
264 // }
265 setPartName();
266
267 super.init(site, input);
268 }
269
270 /**
271 * Calls <code>MultiPageEditorPart.setPartName(String partName)</code> with
272 * text appropriate to the state of the taxon: any taxon that has been saved
273 * will by necessity have a name to display; a new taxon should display
274 * "New taxon" in the editor tab.
275 */
276 protected void setPartName() {
277
278 String partName = null;
279 TaxonNameBase<?, ?> name = getTaxon().getName();
280
281 if (name != null) {
282 partName = name.getTitleCache();
283 }
284
285 if (partName == null || partName.equals("")) {
286 partName = ("New taxon");
287 }
288
289 setPartName(partName);
290 }
291
292 /**
293 * {@inheritDoc}
294 *
295 * Editor pages call this in their postOperation to notify the
296 * MultiPageTaxonEditor of unsaved changes
297 */
298 public void changed(Object element) {
299 // setDirty(true);
300 dirty = true;
301 super.editorDirtyStateChanged();
302 if (element instanceof TaxonBase) {
303 TaxonNameEditor page = (TaxonNameEditor) getPage(Page.NAME);
304 AbstractGroupedContainer container = page
305 .getContainer((TaxonBase) element);
306 if (container != null) {
307 container.refresh();
308 }
309 }
310 }
311
312 /**
313 * The accepted taxon that is the input for this editor
314 *
315 * @return the accepted taxon
316 */
317 public Taxon getTaxon() {
318 return input.getTaxon();
319 }
320
321 /*
322 * (non-Javadoc)
323 *
324 * @see
325 * eu.etaxonomy.cdm.api.conversation.IConversationEnabled#getConversationHolder
326 * ()
327 */
328 /**
329 * <p>
330 * getConversationHolder
331 * </p>
332 *
333 * @return a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
334 * object.
335 */
336 public ConversationHolder getConversationHolder() {
337 return conversation;
338 }
339
340 /**
341 * <p>
342 * setConversationHolder
343 * </p>
344 *
345 * @param conversation
346 * a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
347 * object.
348 */
349 public void setConversationHolder(ConversationHolder conversation) {
350 this.conversation = conversation;
351 }
352
353 /**
354 * <p>
355 * Getter for the field <code>undoContext</code>.
356 * </p>
357 *
358 * @return a {@link org.eclipse.core.commands.operations.IUndoContext}
359 * object.
360 */
361 public IUndoContext getUndoContext() {
362 return undoContext;
363 }
364
365 /**
366 * <p>
367 * Setter for the field <code>undoContext</code>.
368 * </p>
369 *
370 * @param undoContext
371 * a {@link org.eclipse.core.commands.operations.IUndoContext}
372 * object.
373 */
374 public void setUndoContext(IUndoContext undoContext) {
375 this.undoContext = undoContext;
376 }
377
378 /** {@inheritDoc} */
379 @Override
380 public void setFocus() {
381 // logger.warn("Setting focus to editor");
382 // bind the conversation
383 getConversationHolder().bind();
384 // pass focus to the active editor page
385 getActiveEditor().setFocus();
386 }
387
388 /*
389 * (non-Javadoc)
390 *
391 * @see
392 * eu.etaxonomy.cdm.persistence.hibernate.ICdmPostCrudObserver#update(eu
393 * .etaxonomy.cdm.persistence.hibernate.CdmCrudEvent)
394 */
395 /** {@inheritDoc} */
396 public void update(CdmDataChangeMap events) {
397 if (dataChangeBehavior == null) {
398 dataChangeBehavior = new MultiPageTaxonEditorDataChangeBehaviour(
399 this);
400 }
401
402 DataChangeBridge.handleDataChange(events, dataChangeBehavior);
403 }
404
405 /*
406 * (non-Javadoc)
407 *
408 * @see
409 * eu.etaxonomy.taxeditor.store.operations.IPostOperationEnabled#postOperation
410 * ()
411 */
412 /** {@inheritDoc} */
413 public boolean postOperation(CdmBase objectAffectedByOperation) {
414 setDirty(true);
415
416 for (IEditorPart editor : this.getPages()) {
417 if (editor instanceof IPostOperationEnabled) {
418 ((IPostOperationEnabled) editor)
419 .postOperation(objectAffectedByOperation);
420 } else {
421 EditorUtil.warn(getClass(),
422 "postOperation not enabled for editor " + editor);
423 }
424 }
425 EditorUtil
426 .warn(getClass(),
427 "postOperation called on MultiPageTaxonEditor. Can you make it more specific?");
428
429 return false;
430 }
431
432 /**
433 * Returns an <code>IEditorPart</code> implementation by type
434 *
435 * @param page
436 * the page type
437 * @return a {@link eu.etaxonomy.taxeditor.editor.IMultiPageTaxonEditorPage}
438 * object.
439 */
440 public IMultiPageTaxonEditorPage getPage(Page page) {
441 for (IEditorPart editor : this.getPages()) {
442 if (editor.getClass().equals(page.getClazz())) {
443 return (IMultiPageTaxonEditorPage) editor;
444 }
445 }
446 return null;
447 }
448
449 /**
450 * Return a list of <code>AbstractTaxonEditor</code>s registered with this
451 * <code>MultiPageTaxonEditor</code>.
452 *
453 * @return a {@link java.util.List} object.
454 */
455 public List<IMultiPageTaxonEditorPage> getPages() {
456 ArrayList<IMultiPageTaxonEditorPage> editors = new ArrayList<IMultiPageTaxonEditorPage>();
457 for (int i = 0; i < this.getPageCount(); i++) {
458
459 editors.add((IMultiPageTaxonEditorPage) this.getEditor(i));
460 }
461 return editors;
462 }
463
464 /**
465 * Refreshes a certain page of the MultipageTaxonEditor
466 *
467 * @param page
468 * a {@link eu.etaxonomy.taxeditor.editor.Page} object.
469 * @return a boolean.
470 */
471 public boolean redraw(Page page) {
472 return redraw(page, true);
473 }
474
475 /**
476 * Refreshes a certain page of the MultipageTaxonEditor and sets focus to
477 * that page
478 *
479 * @param page
480 * a {@link eu.etaxonomy.taxeditor.editor.Page} object.
481 * @param focus
482 * a boolean.
483 * @return a boolean.
484 */
485 public boolean redraw(Page page, boolean focus) {
486 IMultiPageTaxonEditorPage editorPage = getPage(page);
487 return editorPage != null && editorPage.redraw(focus);
488 }
489
490 /**
491 * <p>
492 * onComplete
493 * </p>
494 *
495 * @return a boolean.
496 */
497 public boolean onComplete() {
498 return false;
499 }
500
501 /**
502 * Reloads the data for this
503 */
504 public void reload() {
505 if (isDirty()) {
506 EditorUtil
507 .warningDialog(
508 "Editor has unsaved data",
509 getClass(),
510 "This editor can not be "
511 + "refreshed because it contains unsaved data. Refreshing "
512 + "this editor would discard the changes. Please save this editor, "
513 + "close and reopen it manually in order to get the latest content");
514 } else {
515 TaxonEditorInput input = (TaxonEditorInput) getEditorInput();
516
517 UUID uuid = input.getTaxonNode().getUuid();
518
519 conversation.clear();
520
521 try {
522 TaxonEditorInput newInput = TaxonEditorInput.NewInstance(uuid);
523 setInput(newInput);
524 for (IMultiPageTaxonEditorPage editorPart : getPages()) {
525 editorPart.redraw();
526 }
527 } catch (Exception e) {
528 EditorUtil.errorDialog("Error refreshing editor", getClass(),
529 "Could not refresh this editor", e);
530 }
531 }
532 }
533
534 @Override
535 public String toString() {
536 return String.format("%s[%s]", this.getClass().getSimpleName(), getEditorInput());
537 }
538
539 @Override
540 public boolean permissionsSatisfied() {
541 IEditorPart activeEditor = getActiveEditor();
542 if(activeEditor != null && ISecuredEditor.class.isAssignableFrom(activeEditor.getClass())){
543 return ((ISecuredEditor)activeEditor).permissionsSatisfied();
544 }
545 return true;
546 }
547 }