added conversationholder.commit fix to AbstractFilteredCdmResourceSelectionDialog...
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / ui / dialog / selection / AbstractFilteredCdmResourceSelectionDialog.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10
11 package eu.etaxonomy.taxeditor.ui.dialog.selection;
12
13 import java.lang.reflect.Field;
14 import java.text.Collator;
15 import java.util.Comparator;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Set;
19 import java.util.UUID;
20
21 import org.eclipse.core.runtime.CoreException;
22 import org.eclipse.core.runtime.IProgressMonitor;
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.OperationCanceledException;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.jface.dialogs.IDialogSettings;
27 import org.eclipse.jface.viewers.ILabelProvider;
28 import org.eclipse.jface.viewers.LabelProvider;
29 import org.eclipse.jface.window.Window;
30 import org.eclipse.jface.wizard.WizardDialog;
31 import org.eclipse.swt.SWT;
32 import org.eclipse.swt.events.SelectionAdapter;
33 import org.eclipse.swt.events.SelectionEvent;
34 import org.eclipse.swt.events.SelectionListener;
35 import org.eclipse.swt.widgets.Composite;
36 import org.eclipse.swt.widgets.Control;
37 import org.eclipse.swt.widgets.Link;
38 import org.eclipse.swt.widgets.Shell;
39 import org.eclipse.swt.widgets.Text;
40 import org.eclipse.ui.IMemento;
41 import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog;
42
43 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
44 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
45 import eu.etaxonomy.cdm.model.common.CdmBase;
46 import eu.etaxonomy.cdm.model.common.ICdmBase;
47 import eu.etaxonomy.cdm.model.common.IIdentifiableEntity;
48 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;
49 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
50 import eu.etaxonomy.taxeditor.model.AbstractUtility;
51 import eu.etaxonomy.taxeditor.newWizard.AbstractNewEntityWizard;
52 import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
53
54 /**
55 * <p>Abstract AbstractFilteredCdmResourceSelectionDialog class.</p>
56 *
57 * @author n.hoffmann
58 * @created 04.06.2009
59 * @version 1.0
60 */
61 public abstract class AbstractFilteredCdmResourceSelectionDialog<T extends ICdmBase> extends
62 FilteredItemsSelectionDialog implements IConversationEnabled {
63
64 private final ConversationHolder conversation;
65
66 protected List<UuidAndTitleCache<T>> model;
67 private final Set<T> transientCdmObjects = new HashSet<T>();
68 private final String settings;
69
70 protected T cdmBaseToBeFiltered;
71
72 /**
73 * <p>Constructor for AbstractFilteredCdmResourceSelectionDialog.</p>
74 *
75 * @param shell a {@link org.eclipse.swt.widgets.Shell} object.
76 * @param conversation
77 * @param title a {@link java.lang.String} object.
78 * @param multi a boolean.
79 * @param settings a {@link java.lang.String} object.
80 * @param cdmObject a T object.
81 * @param <T> a T object.
82 */
83 protected AbstractFilteredCdmResourceSelectionDialog(Shell shell, ConversationHolder conversation, String title, boolean multi, String settings, T cdmObject) {
84 super(shell, multi);
85 setTitle(title);
86 setMessage("Use * for wildcard, or ? to see all entries");
87 this.settings = settings;
88
89 this.conversation = conversation;
90
91 init();
92
93 initModel();
94
95 String objectTitle = getTitle(cdmObject);
96 if (objectTitle != null) {
97 setInitialPattern(objectTitle);
98 }
99
100 setListLabelProvider(createListLabelProvider());
101 setDetailsLabelProvider(createDetailsLabelProvider());
102
103 setSelectionHistory(new ResourceSelectionHistory());
104 }
105
106 /**
107 * By default, we are returning the standard list label provider
108 *
109 * Override in subclasses if you want different behavior
110 *
111 * @return
112 */
113 protected ILabelProvider createDetailsLabelProvider() {
114 return createListLabelProvider();
115 }
116
117 /**
118 *
119 * @return
120 */
121 protected ILabelProvider createListLabelProvider() {
122 return new FilteredCdmResourceLabelProvider();
123 }
124
125 /**
126 * Override in subclasses.
127 * Will run before initModel()
128 */
129 protected void init() {
130
131 }
132
133 /**
134 * <p>getSelectionFromDialog</p>
135 *
136 * @param dialog a {@link eu.etaxonomy.taxeditor.ui.dialog.selection.AbstractFilteredCdmResourceSelectionDialog} object.
137 * @param <TYPE> a TYPE object.
138 * @return a TYPE object.
139 */
140 protected static <TYPE extends CdmBase> TYPE getSelectionFromDialog(AbstractFilteredCdmResourceSelectionDialog<TYPE> dialog) {
141 //dialog.setInitialPattern("");
142 int result = dialog.open();
143
144 if (result == Window.CANCEL) {
145 return null;
146 }
147
148 UUID uuid = dialog.getSelectedUuidAndTitleCache().getUuid();
149 if(uuid == null){
150 return null;
151 }
152 return dialog.getCdmObjectByUuid(uuid);
153 }
154
155 /**
156 * Check if object was created during the life of this dialog. If not,
157 * retrieve it from the CdmStore.
158 *
159 * @param cdmUuid a {@link java.util.UUID} object.
160 * @return a T object.
161 */
162 protected T getCdmObjectByUuid(UUID cdmUuid) {
163 for (T cdmObject : transientCdmObjects) {
164 if (cdmObject.getUuid().equals(cdmUuid)) {
165 return cdmObject;
166 }
167 }
168 return getPersistentObject(cdmUuid);
169 }
170
171 /**
172 * <p>getPersistentObject</p>
173 *
174 * @param uuid a {@link java.util.UUID} object.
175 * @return a T object.
176 */
177 abstract protected T getPersistentObject(UUID uuid);
178
179 /**
180 * @param cdmObject
181 */
182 // protected void addObjectToModel(T cdmObject) {
183 // model.add(new UuidAndTitleCache(cdmObject.getClass(), cdmObject.getUuid(), getTitle(cdmObject)));
184 // transientCdmObjects.add(cdmObject);
185 // }
186
187 /**
188 * <p>isObjectTransient</p>
189 *
190 * @param cdmObject a T object.
191 * @return a boolean.
192 */
193 protected boolean isObjectTransient(T cdmObject) {
194 return (getPersistentObject(cdmObject.getUuid()) == null);
195 }
196
197 /**
198 * <p>getTitle</p>
199 *
200 * @param cdmObject a T object.
201 * @return a {@link java.lang.String} object.
202 */
203 protected String getTitle(T cdmObject) {
204 if(cdmObject == null){
205 return "";
206 }
207
208 if (cdmObject instanceof IIdentifiableEntity) {
209 return ((IIdentifiableEntity) cdmObject).getTitleCache();
210 }
211
212 throw new IllegalArgumentException("Generic method only" +
213 " supports cdmObject of type IIdentifiableEntity." +
214 " Please implement specific method in subclass.");
215 }
216
217
218 /** {@inheritDoc} */
219 @Override
220 public void refresh() {
221 initModel();
222 filterExcludedObjects();
223 super.refresh();
224 }
225
226 /**
227 * <p>initModel</p>
228 */
229 abstract protected void initModel();
230
231 /* (non-Javadoc)
232 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#createFilter()
233 */
234 /** {@inheritDoc} */
235 @Override
236 protected ItemsFilter createFilter() {
237 return new ItemsFilter() {
238
239 /**
240 * Always returns false to enforce refiltering even if the pattern is equal
241 */
242 @Override
243 public boolean equalsFilter(ItemsFilter filter) {
244 return false;
245 }
246
247 @Override
248 public boolean isConsistentItem(Object item) {
249 return false;
250 }
251
252 @Override
253 public boolean matchItem(Object item) {
254 String text = null;
255 if(item instanceof UuidAndTitleCache){
256 text = ((UuidAndTitleCache) item).getTitleCache();
257 }else if(item instanceof String){
258 text = (String) item;
259 }
260 return text != null ? matches(text) : false;
261 }
262
263 };
264 }
265
266
267 /**
268 * Set the filter input to the Agent's title cache
269 *
270 * @param cdmObject a T object.
271 */
272 protected void setPattern(T cdmObject) {
273 // FilteredSelection does some very tricky caching to make sure it
274 // runs with high performance.
275 // This works for most use cases, but we want to change the model while the dialog is open
276 // and all the clever caching prevents the content provider from knowing that the model has changed
277 // I am aware, that this is a hack, but the FilteredSelectionDialog API does not offer a convenient
278 // way to solve the problem.
279 try {
280 Field lastCompletedFilter = this.getClass().getSuperclass().getSuperclass().getDeclaredField("lastCompletedFilter");
281 lastCompletedFilter.setAccessible(true);
282 lastCompletedFilter.set(this, null);
283 } catch (SecurityException e) {
284 AbstractUtility.error(getClass(), e);
285 } catch (NoSuchFieldException e) {
286 AbstractUtility.error(getClass(), e);
287 } catch (IllegalArgumentException e) {
288 AbstractUtility.error(getClass(), e);
289 } catch (IllegalAccessException e) {
290 AbstractUtility.error(getClass(), e);
291 }
292
293 // this also is not the nicest way to do it.
294 // I am still amazed, that FilteredSelectionDialog does not offer any methods to change its data
295 // once it was opened. Am I doing it wrong?
296 String pattern = getTitle(cdmObject);
297 ((Text) getPatternControl()).setText(pattern);
298 }
299
300 /* (non-Javadoc)
301 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#fillContentProvider(org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.AbstractContentProvider, org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.ItemsFilter, org.eclipse.core.runtime.IProgressMonitor)
302 */
303 /** {@inheritDoc} */
304 @Override
305 protected void fillContentProvider(AbstractContentProvider contentProvider,
306 ItemsFilter itemsFilter, IProgressMonitor progressMonitor)
307 throws CoreException {
308 try {
309 if(model != null){
310 progressMonitor.beginTask("Looking for entities", model.size());
311 for(UuidAndTitleCache<T> element : model){
312 contentProvider.add(element, itemsFilter);
313 if (progressMonitor.isCanceled()) {
314 throw new OperationCanceledException();
315 }
316 progressMonitor.worked(1);
317 }
318 }else{
319 AbstractUtility.warn(getClass(), "Model for Filtered Selection is null:" + this.getClass().getSimpleName());
320 }
321 }
322 finally {
323 progressMonitor.done();
324 }
325 }
326
327 /* (non-Javadoc)
328 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#getDialogSettings()
329 */
330 /** {@inheritDoc} */
331 @Override
332 protected IDialogSettings getDialogSettings() {
333 IDialogSettings settings = TaxeditorStorePlugin.getDefault().getDialogSettings().getSection(getSettings());
334
335 if (settings == null) {
336 settings = TaxeditorStorePlugin.getDefault().getDialogSettings().addNewSection(getSettings());
337 }
338 return settings;
339 }
340
341 /* (non-Javadoc)
342 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#getElementName(java.lang.Object)
343 */
344 /** {@inheritDoc} */
345 @Override
346 public String getElementName(Object item) {
347 return ((UuidAndTitleCache) item).getTitleCache();
348 }
349
350 /* (non-Javadoc)
351 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#getItemsComparator()
352 */
353 /** {@inheritDoc} */
354 @Override
355 protected Comparator getItemsComparator() {
356 return new Comparator<UuidAndTitleCache>() {
357 @Override
358 public int compare(UuidAndTitleCache entity1,
359 UuidAndTitleCache entity2) {
360 Collator collator = Collator.getInstance();
361 return collator.compare(entity1.getTitleCache(), entity2.getTitleCache());
362 }
363 };
364 }
365
366 /* (non-Javadoc)
367 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#validateItem(java.lang.Object)
368 */
369 /** {@inheritDoc} */
370 @Override
371 protected IStatus validateItem(Object item) {
372 return Status.OK_STATUS;
373 }
374
375 /**
376 * <p>getSelectedUuidAndTitleCache</p>
377 *
378 * @return a {@link eu.etaxonomy.cdm.model.common.UuidAndTitleCache} object.
379 */
380 protected UuidAndTitleCache getSelectedUuidAndTitleCache() {
381 Object[] result = getResult();
382 return result[0] == null ? null : (UuidAndTitleCache) result[0];
383 }
384
385 /**
386 * <p>Getter for the field <code>settings</code>.</p>
387 *
388 * @return a {@link java.lang.String} object.
389 */
390 public String getSettings() {
391 if(settings == null){
392 throw new IllegalStateException("No SETTINGS set.");
393 }
394 return settings;
395 }
396
397 /**
398 *
399 * @author n.hoffmann
400 * @created Oct 19, 2009
401 * @version 1.0
402 */
403 private class ResourceSelectionHistory extends SelectionHistory {
404 /*
405 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.SelectionHistory#restoreItemFromMemento(org.eclipse.ui.IMemento)
406 */
407 @Override
408 protected Object restoreItemFromMemento(IMemento element) {
409 return element.getString("resource"); //$NON-NLS-1$
410 }
411 /*
412 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.SelectionHistory#storeItemToMemento(java.lang.Object,
413 * org.eclipse.ui.IMemento)
414 */
415 @Override
416 protected void storeItemToMemento(Object item, IMemento element) {
417 element.putString("resource", item.toString()); //$NON-NLS-1$
418 }
419 }
420
421 /**
422 * <p>getNewWizardLinkText</p>
423 *
424 * @return a {@link java.lang.String} object.
425 */
426 protected abstract String getNewWizardLinkText();
427
428 /**
429 * <p>getNewEntityWizard</p>
430 * @param parameter
431 * @return a {@link eu.etaxonomy.taxeditor.newWizard.AbstractNewEntityWizard} object.
432 */
433 protected abstract AbstractNewEntityWizard getNewEntityWizard(String parameter);
434
435 public class FilteredCdmResourceLabelProvider extends LabelProvider {
436 @Override
437 public String getText(Object element) {
438 if (element == null) {
439 return null;
440 }
441 return ((UuidAndTitleCache) element).getTitleCache();
442 }
443 };
444
445 /* (non-Javadoc)
446 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#createExtendedContentArea(org.eclipse.swt.widgets.Composite)
447 */
448 /** {@inheritDoc} */
449 @Override
450 protected Control createExtendedContentArea(Composite parent) {
451 String newWizardLinkText = getNewWizardLinkText();
452 if(newWizardLinkText != null){
453 Link link = new Link(parent, SWT.NONE);
454 link.setText(newWizardLinkText);
455 link.addSelectionListener(getNewWizardLinkSelectionListener());
456 return link;
457 }
458 return null;
459 }
460
461 protected SelectionListener getNewWizardLinkSelectionListener(){
462 return new SelectionAdapter() {
463
464 /* (non-Javadoc)
465 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
466 */
467 @Override
468 public void widgetSelected(SelectionEvent e) {
469
470 AbstractNewEntityWizard wizard = getNewEntityWizard(e.text);
471
472 wizard.init(null, null);
473 WizardDialog dialog = new WizardDialog(getShell(), wizard);
474 int status = dialog.open();
475
476 if (status == IStatus.OK) {
477
478 T entity = (T) wizard.getEntity();
479
480 // addObjectToModel(teamOrPerson);
481 refresh();
482 setPattern(entity);
483 getConversationHolder().bind();
484 //FIXME : Need to make sure this is a stable fix (ticket 3822)
485 getConversationHolder().commit();
486 }
487 }
488 };
489 }
490
491 /**
492 * <p>getConversationHolder</p>
493 *
494 * @return a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder} object.
495 */
496 @Override
497 public ConversationHolder getConversationHolder() {
498 return conversation;
499 }
500
501 /** {@inheritDoc} */
502 @Override
503 public void update(CdmDataChangeMap changeEvents) {}
504
505 /**
506 * Don't want to add for example a taxon or synonym to itself
507 * so filter the list to remove the taxon in question
508 * (<code>cdmBaseToBeFiltered</code>)
509 * so it is not available in the filtered list.
510 */
511 private void filterExcludedObjects() {
512 if (model != null && cdmBaseToBeFiltered != null) {
513
514 UuidAndTitleCache uuidAndTitleCacheToRemove = null;
515
516 for (UuidAndTitleCache uuidAndTitleCache : model){
517 if ((cdmBaseToBeFiltered.getUuid()).equals(uuidAndTitleCache.getUuid())) {
518 uuidAndTitleCacheToRemove = uuidAndTitleCache;
519 }
520 }
521 model.remove(uuidAndTitleCacheToRemove);
522 }
523 }
524 }