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