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