Project

General

Profile

Download (14.1 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 Comparator<T> getTitleComparator(){
163
	    return new TitleCacheComparator();
164
	}
165

    
166
	public void setMergeTarget(T t){
167
	    markedMergeTarget = t;
168
	}
169

    
170
    public Set<T> getMergeCandidates() {
171
        return markedMergeCandidates;
172
    }
173

    
174
    public T getMergeTarget() {
175
        return markedMergeTarget;
176
    }
177

    
178
	public void removeMergeTarget(){
179
	    markedMergeTarget = null;
180
	}
181

    
182
	public void addMergeCandidate(T t){
183
	    markedMergeCandidates.add(t);
184
	}
185

    
186
	public void removeMergeCandidate(T t){
187
		markedMergeCandidates.remove(t);
188
	}
189

    
190
    public void addToDelete(T t, DeleteConfiguratorBase config) {
191
        toDelete.put(t, config);
192
    }
193
    public void addSaveCandidate(T t){
194
        saveCandidates.add(t);
195
    }
196
	private void setEntityUuid(UUID entityUuid){
197
		this.entityUuid = entityUuid;
198
	}
199

    
200
	public UUID getEntityUuid() {
201
		return entityUuid;
202
	}
203

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

    
223
		if(getEntityUuid() != null){
224
			T entity = loadEntity(getEntityUuid());
225
			model.add(entity);
226
		}
227
		else if(bulkEditorQuery != null){
228
            IIdentifiableEntityServiceConfigurator configurator = bulkEditorQuery.getSearchConfigurator();
229

    
230
            // check for UUID search
231
            String titleSearchString = configurator.getTitleSearchString();
232
            try {
233
                UUID uuid = UUID.fromString(titleSearchString);
234
                T entity = loadEntity(uuid);
235
                //UUID search found -> add entity to list and return
236
                model.add(entity);
237
                return;
238
            } catch (IllegalArgumentException e) {
239
                // search string was no UUID
240
            }
241
            //-> continue with standard search
242

    
243
            int pageSize = configurator.getPageSize()!=null?configurator.getPageSize():getPageSize();
244
            configurator.setPageSize(pageSize);
245
			long count = countEntities(configurator);
246
			int totalWork = count>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)count;
247
			String jobLabel = String.format(Messages.AbstractBulkEditorInput_LOADING, getName(), bulkEditorQuery.getSearchString());
248
	        searchJob = Job.create(jobLabel, (ICoreRunnable) monitor -> {
249
	            monitor.beginTask(jobLabel, totalWork);
250
	            int pageNumber = 0;
251
	            List<T> entities;
252

    
253
	            //load previously selected element
254
	            UUID selectedUuid = null;
255
	            if(selection!=null && selection.getFirstElement() instanceof CdmBase){
256
	                selectedUuid = ((CdmBase) selection.getFirstElement()).getUuid();
257
	                T entity = loadEntity(selectedUuid);
258
	                model.add(entity);
259
	            }
260

    
261
                do {
262
                    if (monitor.isCanceled()) {
263
                        break;
264
                    }
265
                    configurator.setPageNumber(pageNumber);
266
                    entities = listEntities(configurator);
267

    
268
                    addToModel(entities, selectedUuid);
269

    
270
                    pageNumber++;
271
                    monitor.worked(pageSize);
272
                    long workedLong = pageSize*pageNumber;
273
                    int loadedCount =  workedLong>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)workedLong;
274
                    monitor.setTaskName(String.format(Messages.AbstractBulkEditorInput_LOADED, loadedCount, totalWork, getName()));
275

    
276
                    //Update selection
277
                    EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
278
                } while (!entities.isEmpty());
279
	            monitor.done();
280
	            EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
281
	        });
282
	        searchJob.schedule();
283
		}
284
	}
285

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

    
302
	public boolean isMergingEnabled() {
303
		return false;
304
	}
305

    
306
	public boolean isConvertingEnabled() {
307
		return false;
308
	}
309

    
310
	public boolean isMarkerTypeEditingEnabled(MarkerType markerType) {
311
		return false;
312
	}
313

    
314

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

    
333
	public void saveModel(){
334
	    saveModel(true);
335
	}
336

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

    
362

    
363
	/** {@inheritDoc} */
364
	@Override
365
    public T create(T entity) {
366
		return save(entity);
367
	}
368

    
369
	public IEntityCreator<T> getEntityCreator(){
370
		if(entityCreator == null){
371
			entityCreator = createEntityCreator();
372
		}
373
		return entityCreator;
374
	}
375

    
376
	protected abstract IEntityCreator<T> createEntityCreator();
377

    
378
	/**
379
	 * The default implementation returns an empty list of sort providers.
380
	 * @return
381
	 */
382
	public List<IBulkEditorSortProvider<T>> getSortProviders(){
383
		List<IBulkEditorSortProvider<T>> sortProviders = new ArrayList<IBulkEditorSortProvider<T>>();
384

    
385
		sortProviders.add(new CdmBaseSortProvider<T>());
386

    
387
		return sortProviders;
388
	}
389

    
390
	/**
391
	 * Returns a textual representation given object. The default implementation
392
	 * in the abstract base class returns the simple name of the class, this may
393
	 * be overwritten to something more specific in subclasses.
394
	 *
395
	 * @param entity
396
	 * @return a textual representation given object.
397
	 */
398
	public String getTypeText(Object entity){
399
		return entity.getClass().getSimpleName();
400
	}
401

    
402
	public String getText(T entity) {
403
		if(entity.isInstanceOf(IdentifiableEntity.class)){
404
			IdentifiableEntity identifiableEntity = HibernateProxyHelper.deproxy(entity, IdentifiableEntity.class);
405
			String text = identifiableEntity.getTitleCache();
406
			return text;
407
		}
408

    
409
		return "No text. Implement in subclass"; //$NON-NLS-1$
410
	}
411

    
412
	public BasicEventList<T> getModel() {
413
		return model;
414
	}
415

    
416
	public boolean replaceInModel(T entity) {
417
	    int index = model.indexOf(entity);
418
	    if(index >= 0) {
419
	        model.set(index, entity);
420
	        return true;
421
	    } else {
422
	        return false;
423
	    }
424
	}
425

    
426
    @Override
427
    public List<T> getRootEntities() {
428
        return getModel();
429
    }
430

    
431

    
432
    @Override
433
    public Map<Object, List<String>> getPropertyPathsMap() {
434
        // TODO Auto-generated method stub
435
        return null;
436
    }
437

    
438
	public ConversationHolder getConversation() {
439
		return conversation;
440
	}
441

    
442
	public Set<T> getSaveCandidates() {
443
        return saveCandidates;
444
    }
445

    
446
    public HashMap<T, Set<T>> getMergedEntities() {
447
        return mergedEntities;
448
    }
449

    
450
    public void setMergedEntities(HashMap<T, Set<T>> mergedEntities) {
451
        this.mergedEntities = mergedEntities;
452
    }
453

    
454
}
(1-1/11)