Project

General

Profile

Download (13.9 KB) Statistics
| Branch: | Tag: | Revision:
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
package eu.etaxonomy.taxeditor.bulkeditor.input;
10

    
11
import java.util.ArrayList;
12
import java.util.Comparator;
13
import java.util.HashMap;
14
import java.util.HashSet;
15
import java.util.List;
16
import java.util.Map;
17
import java.util.Map.Entry;
18
import java.util.Set;
19
import java.util.UUID;
20
import java.util.stream.Collectors;
21

    
22
import org.eclipse.core.runtime.ICoreRunnable;
23
import org.eclipse.core.runtime.jobs.Job;
24
import org.eclipse.jface.viewers.IStructuredSelection;
25

    
26
import ca.odell.glazedlists.BasicEventList;
27
import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
28
import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
29
import eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator;
30
import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
31
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
32
import eu.etaxonomy.cdm.model.common.CdmBase;
33
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
34
import eu.etaxonomy.cdm.model.common.MarkerType;
35
import eu.etaxonomy.cdm.strategy.merge.IMergable;
36
import eu.etaxonomy.cdm.strategy.merge.MergeException;
37
import eu.etaxonomy.taxeditor.annotatedlineeditor.IEntityCreator;
38
import eu.etaxonomy.taxeditor.annotatedlineeditor.IEntityPersistenceService;
39
import eu.etaxonomy.taxeditor.bulkeditor.BulkEditorQuery;
40
import eu.etaxonomy.taxeditor.bulkeditor.IBulkEditorSortProvider;
41
import eu.etaxonomy.taxeditor.bulkeditor.input.sortprovider.CdmBaseSortProvider;
42
import eu.etaxonomy.taxeditor.bulkeditor.input.sortprovider.TitleCacheComparator;
43
import eu.etaxonomy.taxeditor.bulkeditor.internal.TaxeditorBulkeditorPlugin;
44
import eu.etaxonomy.taxeditor.editor.CdmEntitySessionInput;
45
import eu.etaxonomy.taxeditor.event.EventUtility;
46
import eu.etaxonomy.taxeditor.event.WorkbenchEventConstants;
47
import eu.etaxonomy.taxeditor.l10n.Messages;
48
import eu.etaxonomy.taxeditor.model.MessagingUtils;
49
import eu.etaxonomy.taxeditor.store.CdmStore;
50

    
51
/**
52
 * @author p.ciardelli
53
 * @created 25.06.2009
54
 * @version 1.0
55
 * @param <T>
56
 */
57
public abstract class AbstractBulkEditorInput<T extends CdmBase> extends CdmEntitySessionInput implements
58
    IEntityPersistenceService<T> {
59

    
60

    
61
    private static final String PROPERTY_PROTECTED_TITLECACHE = "Protect TitleCache";
62
    private static final String TYPE_PROPERTY = Messages.BulkEditorE4_TYPE;
63
    private static final String ID_PROPERTY = "Id"; //$NON-NLS-1$
64
    private static final String UUID_PROPERTY = "Uuid"; //$NON-NLS-1$
65

    
66
	private UUID entityUuid;
67

    
68
	private BasicEventList<T> model = new BasicEventList<>();
69

    
70
	private Map<T, DeleteConfiguratorBase> toDelete = new HashMap<>();
71
	private Set<T> saveCandidates = new HashSet<>();
72

    
73

    
74
	private Set<T> markedMergeCandidates = new HashSet<>();
75
	private T markedMergeTarget = null;
76

    
77
	private HashMap<T, Set<T>> mergedEntities = new HashMap<>();
78

    
79
	private IEntityCreator<T> entityCreator;
80
	private final ConversationHolder conversation;
81

    
82
    private Job searchJob;
83

    
84
	public AbstractBulkEditorInput() {
85
	    super(true);
86
	    this.conversation = CdmStore.createConversation();
87
	}
88

    
89
	static public AbstractBulkEditorInput NewInstance(BulkEditorInputType inputType) {
90

    
91
		return BulkEditorInputType.getInput(inputType);
92
	}
93

    
94
	public static AbstractBulkEditorInput NewInstance(IdentifiableEntity entity) {
95

    
96
		BulkEditorInputType inputType = BulkEditorInputType.getByType(entity.getClass());
97

    
98
		AbstractBulkEditorInput editorInput = NewInstance(inputType);
99

    
100
		editorInput.setEntityUuid(entity.getUuid());
101

    
102
		return editorInput;
103
	}
104

    
105
    public abstract String getName();
106

    
107
    public String getEditorName(){
108
        return getName();
109
    }
110

    
111
    protected int getPageSize(){
112
        return 100;
113
    }
114

    
115
	protected abstract List<T> listEntities(IIdentifiableEntityServiceConfigurator configurator);
116

    
117
	protected abstract long countEntities(IIdentifiableEntityServiceConfigurator configurator);
118

    
119
	protected abstract T loadEntity(UUID entityUuid);
120

    
121
    public List<String> getPropertyKeys(){
122
        List<String> properties = new ArrayList<>();
123
        properties.add(getName());
124
        properties.add(PROPERTY_PROTECTED_TITLECACHE);
125
        properties.addAll(getPropertyKeys_internal());
126
        properties.add(TYPE_PROPERTY);
127
        properties.add(ID_PROPERTY);
128
        properties.add(UUID_PROPERTY);
129
        return properties;
130
    }
131

    
132
    protected abstract List<String> getPropertyKeys_internal();
133

    
134

    
135
    public Object getPropertyValue(T cdmBase, String property){
136
        if(property.equals(getName())){
137
            return getText(cdmBase);
138
        }
139
        else if(property.equals(PROPERTY_PROTECTED_TITLECACHE)
140
                &&cdmBase.isInstanceOf(IdentifiableEntity.class)){
141
            return HibernateProxyHelper.deproxy(cdmBase, IdentifiableEntity.class).isProtectedTitleCache();
142
        }
143
        else if(property.equals(TYPE_PROPERTY)){
144
            return getTypeText(cdmBase);
145
        }
146
        else if(property.equals(UUID_PROPERTY)){
147
            return cdmBase.getUuid();
148
        }
149
        else if(property.equals(ID_PROPERTY)){
150
            return cdmBase.getId();
151
        }
152
        return null;
153
    }
154

    
155
	public Comparator<T> getTitleComparator(){
156
	    return new TitleCacheComparator();
157
	}
158

    
159
	public void setMergeTarget(T t){
160
	    markedMergeTarget = t;
161
	}
162

    
163
    public Set<T> getMergeCandidates() {
164
        return markedMergeCandidates;
165
    }
166

    
167
    public T getMergeTarget() {
168
        return markedMergeTarget;
169
    }
170

    
171
	public void removeMergeTarget(){
172
	    markedMergeTarget = null;
173
	}
174

    
175
	public void addMergeCandidate(T t){
176
	    markedMergeCandidates.add(t);
177
	}
178

    
179
	public void removeMergeCandidate(T t){
180
		markedMergeCandidates.remove(t);
181
	}
182

    
183
    public void addToDelete(T t, DeleteConfiguratorBase config) {
184
        toDelete.put(t, config);
185
    }
186
    public void addSaveCandidate(T t){
187
        saveCandidates.add(t);
188
    }
189
	private void setEntityUuid(UUID entityUuid){
190
		this.entityUuid = entityUuid;
191
	}
192

    
193
	public UUID getEntityUuid() {
194
		return entityUuid;
195
	}
196

    
197
	public void performSearch(final BulkEditorQuery bulkEditorQuery, IStructuredSelection selection) {
198
	    //cancel previous search job
199
	    if(searchJob!=null && searchJob.getState()!=Job.NONE){
200
	        searchJob.cancel();
201
	        searchJob = null;
202
	        /*
203
	         * wait for a little while for the job to finish
204
	         * to avoid asynchronously loaded results of the
205
	         * previous search being shown in the next search
206
	         * (not critical but explicitly waiting for the job to finish
207
	         * could run into an endless loop by mistake)
208
	         */
209
	        try {
210
                Thread.sleep(500);
211
            } catch (InterruptedException e) {
212
            }
213
	    }
214
	    model.clear();
215

    
216
		if(getEntityUuid() != null){
217
			T entity = loadEntity(getEntityUuid());
218
			model.add(entity);
219
		}
220
		else if(bulkEditorQuery != null){
221
            IIdentifiableEntityServiceConfigurator configurator = bulkEditorQuery.getSearchConfigurator();
222

    
223
            // check for UUID search
224
            String titleSearchString = configurator.getTitleSearchString();
225
            try {
226
                UUID uuid = UUID.fromString(titleSearchString);
227
                T entity = loadEntity(uuid);
228
                //UUID search found -> add entity to list and return
229
                model.add(entity);
230
                return;
231
            } catch (IllegalArgumentException e) {
232
                // search string was no UUID
233
            }
234
            //-> continue with standard search
235

    
236
            int pageSize = configurator.getPageSize()!=null?configurator.getPageSize():getPageSize();
237
            configurator.setPageSize(pageSize);
238
			long count = countEntities(configurator);
239
			int totalWork = count>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)count;
240
			String jobLabel = String.format(Messages.AbstractBulkEditorInput_LOADING, getName(), bulkEditorQuery.getSearchString());
241
	        searchJob = Job.create(jobLabel, (ICoreRunnable) monitor -> {
242
	            monitor.beginTask(jobLabel, totalWork);
243
	            int pageNumber = 0;
244
	            List<T> entities;
245

    
246
	            //load previously selected element
247
	            UUID selectedUuid = null;
248
	            if(selection!=null && selection.getFirstElement() instanceof CdmBase){
249
	                selectedUuid = ((CdmBase) selection.getFirstElement()).getUuid();
250
	                T entity = loadEntity(selectedUuid);
251
	                model.add(entity);
252
	            }
253

    
254
                do {
255
                    if (monitor.isCanceled()) {
256
                        break;
257
                    }
258
                    configurator.setPageNumber(pageNumber);
259
                    entities = listEntities(configurator);
260

    
261
                    addToModel(entities, selectedUuid);
262

    
263
                    pageNumber++;
264
                    monitor.worked(pageSize);
265
                    long workedLong = pageSize*pageNumber;
266
                    int loadedCount =  workedLong>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)workedLong;
267
                    monitor.setTaskName(String.format(Messages.AbstractBulkEditorInput_LOADED, loadedCount, totalWork, getName()));
268

    
269
                    //Update selection
270
                    EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
271
                } while (!entities.isEmpty());
272
	            monitor.done();
273
	            EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
274
	        });
275
	        searchJob.schedule();
276
		}
277
	}
278

    
279
	private void addToModel(List<T> entities, UUID selectedUuid){
280
	    //filter pre-loaded previously selected element
281
	    if(selectedUuid!=null){
282
	        entities = entities.stream().filter(entity->!entity.getUuid().equals(selectedUuid)).collect(Collectors.toList());
283
	    }
284
	    /*
285
         * IMPORTANT!
286
         * Entities have to be loaded into the main session because they are
287
         * loaded in a parallel asynchronous thread
288
         */
289
        if(getCdmEntitySession()!=null){//is null when closing the bulk editor during loading
290
            getCdmEntitySession().load(entities, true);
291
        }
292
        model.addAll(entities);
293
	}
294

    
295
	public boolean isMergingEnabled() {
296
		return false;
297
	}
298

    
299
	public boolean isConvertingEnabled() {
300
		return false;
301
	}
302

    
303
	public boolean isMarkerTypeEditingEnabled(MarkerType markerType) {
304
		return false;
305
	}
306

    
307

    
308
	/** {@inheritDoc} */
309
	@Override
310
    public boolean merge(T entity, T mergeTarget) {
311
		if (entity instanceof IMergable) {
312
			try {
313
				CdmStore.getCommonService().merge(mergeTarget.getUuid(), entity.getUuid(), (Class<? extends CdmBase>)entity.getClass());
314
			} catch (MergeException e) {
315
				MessagingUtils.errorDialog(Messages.AbstractBulkEditorInput_MERGE_ERROR_TITLE,
316
						this,
317
						String.format(Messages.AbstractBulkEditorInput_MERGE_ERROR_MESSAGE, entity.getClass().getName()),
318
						TaxeditorBulkeditorPlugin.PLUGIN_ID,
319
						e,
320
						true);
321
			}
322
		}
323
		return true;
324
	}
325

    
326
	public void saveModel(){
327
	    saveModel(true);
328
	}
329

    
330
	public void saveModel(boolean resetMerge){
331
	    //delete entities
332
	    for(Entry<T, DeleteConfiguratorBase> entry:toDelete.entrySet()){
333
	        try {
334
                delete(entry.getKey(), entry.getValue());
335
            } catch (ReferencedObjectUndeletableException e) {
336
                e.printStackTrace();
337
            }
338
	    }
339
	    if (!saveCandidates.isEmpty()){
340
	        CdmStore.getService(saveCandidates.iterator().next()).merge(new ArrayList<>(saveCandidates), true);
341
        }
342
	    if(resetMerge){
343
	        //merge entities
344
	        for(T mergeTarget:mergedEntities.keySet()){
345
	            for (T mergeCandidate: mergedEntities.get(mergeTarget)){
346
	                merge(mergeCandidate, mergeTarget);
347
	            }
348
	        }
349
	    }
350
	    toDelete.clear();
351
	    saveCandidates.clear();
352
	    mergedEntities.clear();
353
	}
354

    
355

    
356
	/** {@inheritDoc} */
357
	@Override
358
    public T create(T entity) {
359
		return save(entity);
360
	}
361

    
362
	public IEntityCreator<T> getEntityCreator(){
363
		if(entityCreator == null){
364
			entityCreator = createEntityCreator();
365
		}
366
		return entityCreator;
367
	}
368

    
369
	protected abstract IEntityCreator<T> createEntityCreator();
370

    
371
	/**
372
	 * The default implementation returns an empty list of sort providers.
373
	 * @return
374
	 */
375
	public List<IBulkEditorSortProvider<T>> getSortProviders(){
376
		List<IBulkEditorSortProvider<T>> sortProviders = new ArrayList<IBulkEditorSortProvider<T>>();
377

    
378
		sortProviders.add(new CdmBaseSortProvider<T>());
379

    
380
		return sortProviders;
381
	}
382

    
383
	/**
384
	 * Returns a textual representation given object. The default implementation
385
	 * in the abstract base class returns the simple name of the class, this may
386
	 * be overwritten to something more specific in subclasses.
387
	 *
388
	 * @param entity
389
	 * @return a textual representation given object.
390
	 */
391
	public String getTypeText(Object entity){
392
		return entity.getClass().getSimpleName();
393
	}
394

    
395
	public String getText(T entity) {
396
		if(entity.isInstanceOf(IdentifiableEntity.class)){
397
			IdentifiableEntity identifiableEntity = HibernateProxyHelper.deproxy(entity, IdentifiableEntity.class);
398
			String text = identifiableEntity.getTitleCache();
399
			return text;
400
		}
401

    
402
		return "No text. Implement in subclass"; //$NON-NLS-1$
403
	}
404

    
405
	public BasicEventList<T> getModel() {
406
		return model;
407
	}
408

    
409
	public boolean replaceInModel(T entity) {
410
	    int index = model.indexOf(entity);
411
	    if(index >= 0) {
412
	        model.set(index, entity);
413
	        return true;
414
	    } else {
415
	        return false;
416
	    }
417
	}
418

    
419
    @Override
420
    public List<T> getRootEntities() {
421
        return getModel();
422
    }
423

    
424

    
425
    @Override
426
    public Map<Object, List<String>> getPropertyPathsMap() {
427
        // TODO Auto-generated method stub
428
        return null;
429
    }
430

    
431
	public ConversationHolder getConversation() {
432
		return conversation;
433
	}
434

    
435
	public Set<T> getSaveCandidates() {
436
        return saveCandidates;
437
    }
438

    
439
    public HashMap<T, Set<T>> getMergedEntities() {
440
        return mergedEntities;
441
    }
442

    
443
    public void setMergedEntities(HashMap<T, Set<T>> mergedEntities) {
444
        this.mergedEntities = mergedEntities;
445
    }
446

    
447
}
(1-1/11)