Project

General

Profile

Download (14.2 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 boolean isBooleanProperty(String property) {
156
        if(property.equals(PROPERTY_PROTECTED_TITLECACHE)){
157
            return true;
158
        }
159
        return false;
160
    }
161

    
162
    public boolean isCacheProperty(String property) {
163
        if(property.equals(PROPERTY_PROTECTED_TITLECACHE)){
164
            return true;
165
        }
166
        return false;
167
    }
168

    
169
	public Comparator<T> getTitleComparator(){
170
	    return new TitleCacheComparator();
171
	}
172

    
173
	public void setMergeTarget(T t){
174
	    markedMergeTarget = t;
175
	}
176

    
177
    public Set<T> getMergeCandidates() {
178
        return markedMergeCandidates;
179
    }
180

    
181
    public T getMergeTarget() {
182
        return markedMergeTarget;
183
    }
184

    
185
	public void removeMergeTarget(){
186
	    markedMergeTarget = null;
187
	}
188

    
189
	public void addMergeCandidate(T t){
190
	    markedMergeCandidates.add(t);
191
	}
192

    
193
	public void removeMergeCandidate(T t){
194
		markedMergeCandidates.remove(t);
195
	}
196

    
197
    public void addToDelete(T t, DeleteConfiguratorBase config) {
198
        toDelete.put(t, config);
199
    }
200
    public void addSaveCandidate(T t){
201
        saveCandidates.add(t);
202
    }
203
	private void setEntityUuid(UUID entityUuid){
204
		this.entityUuid = entityUuid;
205
	}
206

    
207
	public UUID getEntityUuid() {
208
		return entityUuid;
209
	}
210

    
211
	public void performSearch(final BulkEditorQuery bulkEditorQuery, IStructuredSelection selection) {
212
	    //cancel previous search job
213
	    if(searchJob!=null && searchJob.getState()!=Job.NONE){
214
	        searchJob.cancel();
215
	        searchJob = null;
216
	        /*
217
	         * wait for a little while for the job to finish
218
	         * to avoid asynchronously loaded results of the
219
	         * previous search being shown in the next search
220
	         * (not critical but explicitly waiting for the job to finish
221
	         * could run into an endless loop by mistake)
222
	         */
223
	        try {
224
                Thread.sleep(500);
225
            } catch (InterruptedException e) {
226
            }
227
	    }
228
	    model.clear();
229

    
230
		if(getEntityUuid() != null){
231
			T entity = loadEntity(getEntityUuid());
232
			model.add(entity);
233
		}
234
		else if(bulkEditorQuery != null){
235
            IIdentifiableEntityServiceConfigurator configurator = bulkEditorQuery.getSearchConfigurator();
236

    
237
            // check for UUID search
238
            String titleSearchString = configurator.getTitleSearchString();
239
            try {
240
                UUID uuid = UUID.fromString(titleSearchString);
241
                T entity = loadEntity(uuid);
242
                //UUID search found -> add entity to list and return
243
                model.add(entity);
244
                return;
245
            } catch (IllegalArgumentException e) {
246
                // search string was no UUID
247
            }
248
            //-> continue with standard search
249

    
250
            int pageSize = configurator.getPageSize()!=null?configurator.getPageSize():getPageSize();
251
            configurator.setPageSize(pageSize);
252
			long count = countEntities(configurator);
253
			int totalWork = count>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)count;
254
			String jobLabel = String.format(Messages.AbstractBulkEditorInput_LOADING, getName(), bulkEditorQuery.getSearchString());
255
	        searchJob = Job.create(jobLabel, (ICoreRunnable) monitor -> {
256
	            monitor.beginTask(jobLabel, totalWork);
257
	            int pageNumber = 0;
258
	            List<T> entities;
259

    
260
	            //load previously selected element
261
	            UUID selectedUuid = null;
262
	            if(selection!=null && selection.getFirstElement() instanceof CdmBase){
263
	                selectedUuid = ((CdmBase) selection.getFirstElement()).getUuid();
264
	                T entity = loadEntity(selectedUuid);
265
	                model.add(entity);
266
	            }
267

    
268
                do {
269
                    if (monitor.isCanceled()) {
270
                        break;
271
                    }
272
                    configurator.setPageNumber(pageNumber);
273
                    entities = listEntities(configurator);
274

    
275
                    addToModel(entities, selectedUuid);
276

    
277
                    pageNumber++;
278
                    monitor.worked(pageSize);
279
                    long workedLong = pageSize*pageNumber;
280
                    int loadedCount =  workedLong>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)workedLong;
281
                    monitor.setTaskName(String.format(Messages.AbstractBulkEditorInput_LOADED, loadedCount, totalWork, getName()));
282

    
283
                    //Update selection
284
                    EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
285
                } while (!entities.isEmpty());
286
	            monitor.done();
287
	            EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
288
	        });
289
	        searchJob.schedule();
290
		}
291
	}
292

    
293
	private void addToModel(List<T> entities, UUID selectedUuid){
294
	    //filter pre-loaded previously selected element
295
	    if(selectedUuid!=null){
296
	        entities = entities.stream().filter(entity->!entity.getUuid().equals(selectedUuid)).collect(Collectors.toList());
297
	    }
298
	    /*
299
         * IMPORTANT!
300
         * Entities have to be loaded into the main session because they are
301
         * loaded in a parallel asynchronous thread
302
         */
303
        if(getCdmEntitySession()!=null){//is null when closing the bulk editor during loading
304
            getCdmEntitySession().load(entities, true);
305
        }
306
        model.addAll(entities);
307
	}
308

    
309
	public boolean isMergingEnabled() {
310
		return false;
311
	}
312

    
313
	public boolean isConvertingEnabled() {
314
		return false;
315
	}
316

    
317
	public boolean isMarkerTypeEditingEnabled(MarkerType markerType) {
318
		return false;
319
	}
320

    
321

    
322
	/** {@inheritDoc} */
323
	@Override
324
    public boolean merge(T entity, T mergeTarget) {
325
		if (entity instanceof IMergable) {
326
			try {
327
				CdmStore.getCommonService().merge(mergeTarget.getUuid(), entity.getUuid(), (Class<? extends CdmBase>)entity.getClass());
328
			} catch (MergeException e) {
329
				MessagingUtils.errorDialog(Messages.AbstractBulkEditorInput_MERGE_ERROR_TITLE,
330
						this,
331
						String.format(Messages.AbstractBulkEditorInput_MERGE_ERROR_MESSAGE, entity.getClass().getName()),
332
						TaxeditorBulkeditorPlugin.PLUGIN_ID,
333
						e,
334
						true);
335
			}
336
		}
337
		return true;
338
	}
339

    
340
	public void saveModel(){
341
	    saveModel(true);
342
	}
343

    
344
	public void saveModel(boolean resetMerge){
345
	    //delete entities
346
	    for(Entry<T, DeleteConfiguratorBase> entry:toDelete.entrySet()){
347
	        try {
348
                delete(entry.getKey(), entry.getValue());
349
            } catch (ReferencedObjectUndeletableException e) {
350
                e.printStackTrace();
351
            }
352
	    }
353
	    if (!saveCandidates.isEmpty()){
354
	        CdmStore.getService(saveCandidates.iterator().next()).merge(new ArrayList<>(saveCandidates), true);
355
        }
356
	    if(resetMerge){
357
	        //merge entities
358
	        for(T mergeTarget:mergedEntities.keySet()){
359
	            for (T mergeCandidate: mergedEntities.get(mergeTarget)){
360
	                merge(mergeCandidate, mergeTarget);
361
	            }
362
	        }
363
	    }
364
	    toDelete.clear();
365
	    saveCandidates.clear();
366
	    mergedEntities.clear();
367
	}
368

    
369

    
370
	/** {@inheritDoc} */
371
	@Override
372
    public T create(T entity) {
373
		return save(entity);
374
	}
375

    
376
	public IEntityCreator<T> getEntityCreator(){
377
		if(entityCreator == null){
378
			entityCreator = createEntityCreator();
379
		}
380
		return entityCreator;
381
	}
382

    
383
	protected abstract IEntityCreator<T> createEntityCreator();
384

    
385
	/**
386
	 * The default implementation returns an empty list of sort providers.
387
	 * @return
388
	 */
389
	public List<IBulkEditorSortProvider<T>> getSortProviders(){
390
		List<IBulkEditorSortProvider<T>> sortProviders = new ArrayList<IBulkEditorSortProvider<T>>();
391

    
392
		sortProviders.add(new CdmBaseSortProvider<T>());
393

    
394
		return sortProviders;
395
	}
396

    
397
	/**
398
	 * Returns a textual representation given object. The default implementation
399
	 * in the abstract base class returns the simple name of the class, this may
400
	 * be overwritten to something more specific in subclasses.
401
	 *
402
	 * @param entity
403
	 * @return a textual representation given object.
404
	 */
405
	public String getTypeText(Object entity){
406
		return entity.getClass().getSimpleName();
407
	}
408

    
409
	public String getText(T entity) {
410
		if(entity.isInstanceOf(IdentifiableEntity.class)){
411
			IdentifiableEntity identifiableEntity = HibernateProxyHelper.deproxy(entity, IdentifiableEntity.class);
412
			String text = identifiableEntity.getTitleCache();
413
			return text;
414
		}
415

    
416
		return "No text. Implement in subclass"; //$NON-NLS-1$
417
	}
418

    
419
	public BasicEventList<T> getModel() {
420
		return model;
421
	}
422

    
423
	public boolean replaceInModel(T entity) {
424
	    int index = model.indexOf(entity);
425
	    if(index >= 0) {
426
	        model.set(index, entity);
427
	        return true;
428
	    } else {
429
	        return false;
430
	    }
431
	}
432

    
433
    @Override
434
    public List<T> getRootEntities() {
435
        return getModel();
436
    }
437

    
438

    
439
    @Override
440
    public Map<Object, List<String>> getPropertyPathsMap() {
441
        // TODO Auto-generated method stub
442
        return null;
443
    }
444

    
445
	public ConversationHolder getConversation() {
446
		return conversation;
447
	}
448

    
449
	public Set<T> getSaveCandidates() {
450
        return saveCandidates;
451
    }
452

    
453
    public HashMap<T, Set<T>> getMergedEntities() {
454
        return mergedEntities;
455
    }
456

    
457
    public void setMergedEntities(HashMap<T, Set<T>> mergedEntities) {
458
        this.mergedEntities = mergedEntities;
459
    }
460

    
461
}
(1-1/11)