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