Project

General

Profile

Download (15.6 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.Collection;
13
import java.util.Comparator;
14
import java.util.HashMap;
15
import java.util.HashSet;
16
import java.util.List;
17
import java.util.Map;
18
import java.util.Map.Entry;
19
import java.util.Set;
20
import java.util.UUID;
21
import java.util.stream.Collectors;
22

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

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

    
53
/**
54
 * @author p.ciardelli
55
 * @created 25.06.2009
56
 */
57
public abstract class AbstractBulkEditorInput<T extends CdmBase>
58
        extends CdmEntitySessionInput<T>
59
        implements IEntityPersistenceService<T> {
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
    private static final String CREATED_BY_PROPERTY = "Created by"; //$NON-NLS-1$
66

    
67
	private UUID entityUuid;
68

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

    
71
	private Map<T, DeleteConfiguratorBase> toDelete = new HashMap<>();
72
	public Map<T, DeleteConfiguratorBase> getToDelete() {
73
        return toDelete;
74
    }
75

    
76
    private Set<T> saveCandidates = new HashSet<>();
77

    
78

    
79
	private Set<T> markedMergeCandidates = new HashSet<>();
80
	private T markedMergeTarget = null;
81

    
82
	private HashMap<T, Set<T>> mergedEntities = new HashMap<>();
83

    
84
	private IEntityCreator<T> entityCreator;
85
	private final ConversationHolder conversation;
86

    
87
    private Job searchJob;
88

    
89
	public AbstractBulkEditorInput() {
90
	    super(true);
91
	    this.conversation = CdmStore.createConversation();
92
	}
93

    
94
	static public AbstractBulkEditorInput<?> NewInstance(BulkEditorInputType inputType) {
95

    
96
		return BulkEditorInputType.getInput(inputType);
97
	}
98

    
99
	public static AbstractBulkEditorInput<?> NewInstance(IdentifiableEntity entity) {
100

    
101
		BulkEditorInputType inputType = BulkEditorInputType.getByType(entity.getClass());
102

    
103
		AbstractBulkEditorInput<?> editorInput = NewInstance(inputType);
104

    
105
		editorInput.setEntityUuid(entity.getUuid());
106

    
107
		return editorInput;
108
	}
109

    
110
	public static AbstractBulkEditorInput<?> NewInstance(Class clazz, UUID uuid) {
111

    
112
        BulkEditorInputType inputType = BulkEditorInputType.getByType(clazz);
113

    
114
        AbstractBulkEditorInput<?> editorInput = NewInstance(inputType);
115

    
116
        editorInput.setEntityUuid(uuid);
117

    
118
        return editorInput;
119
    }
120

    
121
    public abstract String getName();
122

    
123
    public String getEditorName(){
124
        return getName();
125
    }
126

    
127
    protected int getPageSize(){
128
        return 100;
129
    }
130

    
131
	protected abstract List<T> listEntities(IIdentifiableEntityServiceConfigurator configurator);
132

    
133
	protected abstract long countEntities(IIdentifiableEntityServiceConfigurator configurator);
134

    
135
	protected abstract T loadEntity(UUID entityUuid);
136

    
137
    public List<String> getPropertyKeys(){
138
        List<String> properties = new ArrayList<>();
139
        properties.add(getName());
140
        properties.add(PROPERTY_PROTECTED_TITLECACHE);
141
        properties.addAll(getPropertyKeys_internal());
142
        properties.add(TYPE_PROPERTY);
143
        properties.add(ID_PROPERTY);
144
        properties.add(UUID_PROPERTY);
145
        properties.add(CREATED_BY_PROPERTY);
146
        return properties;
147
    }
148

    
149
    protected abstract List<String> getPropertyKeys_internal();
150

    
151

    
152
    public Object getPropertyValue(T cdmBase, String property){
153
        if(property.equals(getName())){
154
            return getText(cdmBase);
155
        }
156
        else if(property.equals(PROPERTY_PROTECTED_TITLECACHE)
157
                &&cdmBase.isInstanceOf(IdentifiableEntity.class)){
158
            return HibernateProxyHelper.deproxy(cdmBase, IdentifiableEntity.class).isProtectedTitleCache();
159
        }
160
        else if(property.equals(TYPE_PROPERTY)){
161
            return getTypeText(cdmBase);
162
        }
163
        else if(property.equals(UUID_PROPERTY)){
164
            return cdmBase.getUuid();
165
        }
166
        else if(property.equals(ID_PROPERTY)){
167
            return cdmBase.getId();
168
        }
169
        else if(property.equals(CREATED_BY_PROPERTY)){
170
            if (cdmBase.getCreatedBy() != null){
171
                return cdmBase.getCreatedBy().getUsername();
172
            } else {
173
                return null;
174
            }
175
        }
176
        return null;
177
    }
178

    
179
    public boolean isBooleanProperty(String property) {
180
        if(property.equals(PROPERTY_PROTECTED_TITLECACHE)){
181
            return true;
182
        }
183
        return false;
184
    }
185

    
186
    public boolean isCacheProperty(String property) {
187
        if(property.equals(PROPERTY_PROTECTED_TITLECACHE)){
188
            return true;
189
        }
190
        return false;
191
    }
192

    
193
	public <T extends IdentifiableEntity> Comparator<T> getTitleComparator(){
194
	    return new TitleCacheComparator<T>();
195
	}
196

    
197
	public void setMergeTarget(T t){
198
	    markedMergeTarget = t;
199
	}
200

    
201
    public Set<T> getMergeCandidates() {
202
        return markedMergeCandidates;
203
    }
204

    
205
    public T getMergeTarget() {
206
        return markedMergeTarget;
207
    }
208

    
209
	public void removeMergeTarget(){
210
	    markedMergeTarget = null;
211
	}
212

    
213
	public void addMergeCandidate(T t){
214
	    markedMergeCandidates.add(t);
215
	}
216

    
217
	public void removeMergeCandidate(T t){
218
		markedMergeCandidates.remove(t);
219
	}
220

    
221
    public void addToDelete(T t, DeleteConfiguratorBase config) {
222
        toDelete.put(t, config);
223
    }
224
    public void addSaveCandidate(T t){
225
        saveCandidates.add(t);
226
    }
227
	public void setEntityUuid(UUID entityUuid){
228
		this.entityUuid = entityUuid;
229
	}
230

    
231
	public UUID getEntityUuid() {
232
		return entityUuid;
233
	}
234

    
235
	public void performSearch(final BulkEditorQuery bulkEditorQuery, IStructuredSelection selection) {
236
	    //cancel previous search job
237
	    if(searchJob!=null && searchJob.getState()!=Job.NONE){
238
	        boolean isCanceled = searchJob.cancel();
239
	        if (!isCanceled){
240
	            while (!isCanceled){
241
    	            try {
242
    	                Thread.sleep(200);
243
    	            } catch (InterruptedException e) {
244
    	            }
245
    	            isCanceled = searchJob.cancel();
246
	            }
247
	        }
248
	        searchJob = null;
249
//	        /*
250
//	         * wait for a little while for the job to finish
251
//	         * to avoid asynchronously loaded results of the
252
//	         * previous search being shown in the next search
253
//	         * (not critical but explicitly waiting for the job to finish
254
//	         * could run into an endless loop by mistake)
255
//	         */
256
//	        try {
257
//                Thread.sleep(500);
258
//            } catch (InterruptedException e) {
259
//            }
260
	    }
261
        model.clear();
262
        markedMergeCandidates.clear();
263
        markedMergeTarget = null;
264

    
265
		if(getEntityUuid() != null){
266
			T entity = loadEntity(getEntityUuid());
267
			model.add(entity);
268
			setEntityUuid(null);
269
		}
270
		else if(bulkEditorQuery != null){
271
            IIdentifiableEntityServiceConfigurator<?> configurator = bulkEditorQuery.getSearchConfigurator();
272

    
273
            // check for UUID search
274
            String titleSearchString = configurator.getTitleSearchString();
275
            try {
276
                UUID uuid = UUID.fromString(titleSearchString);
277
                T entity = loadEntity(uuid);
278
                //UUID search found -> add entity to list and return
279
                model.add(entity);
280
                return;
281
            } catch (IllegalArgumentException e) {
282
                // search string was no UUID
283
            }
284
            //-> continue with standard search
285

    
286
            int pageSize = configurator.getPageSize()!=null?configurator.getPageSize():getPageSize();
287
            configurator.setPageSize(pageSize);
288
			long count = countEntities(configurator);
289
			int totalWork = count>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)count;
290
			String jobLabel = String.format(Messages.AbstractBulkEditorInput_LOADING, getName(), bulkEditorQuery.getSearchString());
291
	        searchJob = Job.create(jobLabel, (ICoreRunnable) monitor -> {
292
	            monitor.beginTask(jobLabel, totalWork);
293
	            int pageNumber = 0;
294
	            List<T> entities;
295

    
296
	            //load previously selected element
297
	            UUID selectedUuid = null;
298
	            if(selection!=null && selection.getFirstElement() instanceof CdmBase){
299
	                selectedUuid = ((CdmBase) selection.getFirstElement()).getUuid();
300
	                T entity = loadEntity(selectedUuid);
301
	                model.add(entity);
302
	            }
303

    
304
                do {
305
                    if (monitor.isCanceled()) {
306
                        break;
307
                    }
308
                    configurator.setPageNumber(pageNumber);
309
                    entities = listEntities(configurator);
310

    
311
                    addToModel(entities, selectedUuid);
312

    
313
                    pageNumber++;
314
                    monitor.worked(pageSize);
315
                    long workedLong = pageSize*pageNumber;
316
                    int loadedCount =  workedLong>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)workedLong;
317
                    monitor.setTaskName(String.format(Messages.AbstractBulkEditorInput_LOADED, loadedCount, totalWork, getName()));
318

    
319
                    //Update selection
320
                    EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
321
                } while (!entities.isEmpty());
322
	            monitor.done();
323
	            EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
324
	        });
325
	        searchJob.schedule();
326
		}
327
	}
328

    
329
    private void addToModel(List<T> entities, UUID selectedUuid){
330
	    //filter pre-loaded previously selected element
331
	    if(selectedUuid!=null){
332
	        entities = entities.stream().filter(entity->!entity.getUuid().equals(selectedUuid)).collect(Collectors.toList());
333
	    }
334
	    /*
335
         * IMPORTANT!
336
         * Entities have to be loaded into the main session because they are
337
         * loaded in a parallel asynchronous thread
338
         */
339
        if(getCdmEntitySession()!=null){//is null when closing the bulk editor during loading
340
            getCdmEntitySession().load(entities, true);
341
        }
342
        model.addAll(entities);
343
	}
344

    
345
	public boolean isMergingEnabled() {
346
		return false;
347
	}
348

    
349
	public boolean isConvertingEnabled() {
350
		return false;
351
	}
352

    
353
	public boolean isMarkerTypeEditingEnabled(MarkerType markerType) {
354
		return false;
355
	}
356

    
357
	@Override
358
    public boolean merge(T entity, T mergeTarget) {
359
		if (entity instanceof IMergable) {
360
			try {
361
				CdmStore.getCommonService().merge(mergeTarget.getUuid(), entity.getUuid(), (Class<? extends CdmBase>)entity.getClass());
362
			} catch (MergeException e) {
363
				MessagingUtils.errorDialog(Messages.AbstractBulkEditorInput_MERGE_ERROR_TITLE,
364
						this,
365
						String.format(Messages.AbstractBulkEditorInput_MERGE_ERROR_MESSAGE, entity.getClass().getName()),
366
						TaxeditorBulkeditorPlugin.PLUGIN_ID,
367
						e,
368
						true);
369
			}
370
		}
371
		return true;
372
	}
373

    
374
	public void saveModel(){
375
	    saveModel(true);
376
	}
377

    
378
	public void saveModel(boolean resetMerge){
379
	    //delete entities
380
	    for(Entry<T, DeleteConfiguratorBase> entry:toDelete.entrySet()){
381
	        try {
382
                delete(entry.getKey(), entry.getValue());
383
            } catch (ReferencedObjectUndeletableException e) {
384
                e.printStackTrace();
385
            }
386
	    }
387
	    if (!saveCandidates.isEmpty()){
388
	        List<MergeResult<T>> results = CdmStore.getService(saveCandidates.iterator().next()).merge(new ArrayList<>(saveCandidates), true);
389
	        for (MergeResult<T> result: results){
390
	            if (result.getMergedEntity() != null){
391
	                T entity = result.getMergedEntity();
392
	            }
393
	        }
394
        }
395
	    if(resetMerge){
396
	        //merge entities
397
	        for(T mergeTarget:mergedEntities.keySet()){
398
	            for (T mergeCandidate: mergedEntities.get(mergeTarget)){
399
	                merge(mergeCandidate, mergeTarget);
400
	            }
401
	        }
402
	    }
403
	    toDelete.clear();
404
	    saveCandidates.clear();
405
	    mergedEntities.clear();
406
	}
407

    
408
	@Override
409
    public T create(T entity) {
410
		return save(entity);
411
	}
412

    
413
	public IEntityCreator<T> getEntityCreator(){
414
		if(entityCreator == null){
415
			entityCreator = createEntityCreator();
416
		}
417
		return entityCreator;
418
	}
419

    
420
	protected abstract IEntityCreator<T> createEntityCreator();
421

    
422
	/**
423
	 * The default implementation returns an empty list of sort providers.
424
	 * @return
425
	 */
426
	public List<IBulkEditorSortProvider<T>> getSortProviders(){
427
		List<IBulkEditorSortProvider<T>> sortProviders = new ArrayList<>();
428

    
429
		sortProviders.add(new CdmBaseSortProvider<>());
430

    
431
		return sortProviders;
432
	}
433

    
434
	/**
435
	 * Returns a textual representation given object. The default implementation
436
	 * in the abstract base class returns the simple name of the class, this may
437
	 * be overwritten to something more specific in subclasses.
438
	 *
439
	 * @param entity
440
	 * @return a textual representation given object.
441
	 */
442
	public String getTypeText(Object entity){
443
		return entity.getClass().getSimpleName();
444
	}
445

    
446
	public String getText(T entity) {
447
		if(entity.isInstanceOf(IdentifiableEntity.class)){
448
			IdentifiableEntity<?> identifiableEntity = HibernateProxyHelper.deproxy(entity, IdentifiableEntity.class);
449
			String text = identifiableEntity.getTitleCache();
450
			return text;
451
		}
452

    
453
		return "No text. Implement in subclass"; //$NON-NLS-1$
454
	}
455

    
456
	public BasicEventList<T> getModel() {
457
		return model;
458
	}
459

    
460
	public boolean replaceInModel(T entity) {
461
	    int index = model.indexOf(entity);
462
	    if(index >= 0) {
463
	        model.set(index, entity);
464
	        return true;
465
	    } else {
466
	        return false;
467
	    }
468
	}
469

    
470
    @Override
471
    public Collection<T> getRootEntities() {
472
        return getModel();
473
    }
474

    
475
    @Override
476
    public Map<Object, List<String>> getPropertyPathsMap() {
477
        // TODO Auto-generated method stub
478
        return null;
479
    }
480

    
481
	public ConversationHolder getConversation() {
482
		return conversation;
483
	}
484

    
485
	public Set<T> getSaveCandidates() {
486
        return saveCandidates;
487
    }
488

    
489
    public HashMap<T, Set<T>> getMergedEntities() {
490
        return mergedEntities;
491
    }
492

    
493
    public void setMergedEntities(HashMap<T, Set<T>> mergedEntities) {
494
        this.mergedEntities = mergedEntities;
495
    }
496

    
497
}
(1-1/11)