Project

General

Profile

Download (13.5 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 TYPE_PROPERTY = Messages.BulkEditorE4_TYPE;
62
    private static final String ID_PROPERTY = "Id"; //$NON-NLS-1$
63
    private static final String UUID_PROPERTY = "Uuid"; //$NON-NLS-1$
64

    
65
	private UUID entityUuid;
66

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

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

    
72

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

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

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

    
81
    private Job searchJob;
82

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

    
88
	static public AbstractBulkEditorInput NewInstance(BulkEditorInputType inputType) {
89

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

    
93
	public static AbstractBulkEditorInput NewInstance(IdentifiableEntity entity) {
94

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

    
97
		AbstractBulkEditorInput editorInput = NewInstance(inputType);
98

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

    
101
		return editorInput;
102
	}
103

    
104
    public abstract String getName();
105

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

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

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

    
116
	protected abstract long countEntities(IIdentifiableEntityServiceConfigurator configurator);
117

    
118
	protected abstract T loadEntity(UUID entityUuid);
119

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

    
130
    protected abstract List<String> getPropertyKeys_internal();
131

    
132

    
133
    public Object getPropertyValue(T cdmBase, String property){
134
        if(property.equals(getName())){
135
            return getText(cdmBase);
136
        }else if(property.equals(TYPE_PROPERTY)){
137
            return getTypeText(cdmBase);
138
        }else if(property.equals(UUID_PROPERTY)){
139
            return cdmBase.getUuid();
140
        }else if(property.equals(ID_PROPERTY)){
141
            return cdmBase.getId();
142
        }
143
        return null;
144
    }
145

    
146
	public Comparator<T> getTitleComparator(){
147
	    return new TitleCacheComparator();
148
	}
149

    
150
	public void setMergeTarget(T t){
151
	    markedMergeTarget = t;
152
	}
153

    
154
    public Set<T> getMergeCandidates() {
155
        return markedMergeCandidates;
156
    }
157

    
158
    public T getMergeTarget() {
159
        return markedMergeTarget;
160
    }
161

    
162
	public void removeMergeTarget(){
163
	    markedMergeTarget = null;
164
	}
165

    
166
	public void addMergeCandidate(T t){
167
	    markedMergeCandidates.add(t);
168
	}
169

    
170
	public void removeMergeCandidate(T t){
171
		markedMergeCandidates.remove(t);
172
	}
173

    
174
    public void addToDelete(T t, DeleteConfiguratorBase config) {
175
        toDelete.put(t, config);
176
    }
177
    public void addSaveCandidate(T t){
178
        saveCandidates.add(t);
179
    }
180
	private void setEntityUuid(UUID entityUuid){
181
		this.entityUuid = entityUuid;
182
	}
183

    
184
	public UUID getEntityUuid() {
185
		return entityUuid;
186
	}
187

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

    
207
		if(getEntityUuid() != null){
208
			T entity = loadEntity(getEntityUuid());
209
			model.add(entity);
210
		}
211
		else if(bulkEditorQuery != null){
212
            IIdentifiableEntityServiceConfigurator configurator = bulkEditorQuery.getSearchConfigurator();
213

    
214
            // check for UUID search
215
            String titleSearchString = configurator.getTitleSearchString();
216
            try {
217
                UUID uuid = UUID.fromString(titleSearchString);
218
                T entity = loadEntity(uuid);
219
                //UUID search found -> add entity to list and return
220
                model.add(entity);
221
                return;
222
            } catch (IllegalArgumentException e) {
223
                // search string was no UUID
224
            }
225
            //-> continue with standard search
226

    
227
            int pageSize = configurator.getPageSize()!=null?configurator.getPageSize():getPageSize();
228
            configurator.setPageSize(pageSize);
229
			long count = countEntities(configurator);
230
			int totalWork = count>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)count;
231
			String jobLabel = String.format(Messages.AbstractBulkEditorInput_LOADING, getName(), bulkEditorQuery.getSearchString());
232
	        searchJob = Job.create(jobLabel, (ICoreRunnable) monitor -> {
233
	            monitor.beginTask(jobLabel, totalWork);
234
	            int pageNumber = 0;
235
	            List<T> entities;
236

    
237
	            //load previously selected element
238
	            UUID selectedUuid = null;
239
	            if(selection!=null && selection.getFirstElement() instanceof CdmBase){
240
	                selectedUuid = ((CdmBase) selection.getFirstElement()).getUuid();
241
	                T entity = loadEntity(selectedUuid);
242
	                model.add(entity);
243
	            }
244

    
245
                do {
246
                    if (monitor.isCanceled()) {
247
                        break;
248
                    }
249
                    configurator.setPageNumber(pageNumber);
250
                    entities = listEntities(configurator);
251

    
252
                    addToModel(entities, selectedUuid);
253

    
254
                    pageNumber++;
255
                    monitor.worked(pageSize);
256
                    long workedLong = pageSize*pageNumber;
257
                    int loadedCount =  workedLong>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)workedLong;
258
                    monitor.setTaskName(String.format(Messages.AbstractBulkEditorInput_LOADED, loadedCount, totalWork, getName()));
259

    
260
                    //Update selection
261
                    EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
262
                } while (!entities.isEmpty());
263
	            monitor.done();
264
	            EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
265
	        });
266
	        searchJob.schedule();
267
		}
268
	}
269

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

    
286
	public boolean isMergingEnabled() {
287
		return false;
288
	}
289

    
290
	public boolean isConvertingEnabled() {
291
		return false;
292
	}
293

    
294
	public boolean isMarkerTypeEditingEnabled(MarkerType markerType) {
295
		return false;
296
	}
297

    
298

    
299
	/** {@inheritDoc} */
300
	@Override
301
    public boolean merge(T entity, T mergeTarget) {
302
		if (entity instanceof IMergable) {
303
			try {
304
				CdmStore.getCommonService().merge(mergeTarget.getUuid(), entity.getUuid(), (Class<? extends CdmBase>)entity.getClass());
305
			} catch (MergeException e) {
306
				MessagingUtils.errorDialog(Messages.AbstractBulkEditorInput_MERGE_ERROR_TITLE,
307
						this,
308
						String.format(Messages.AbstractBulkEditorInput_MERGE_ERROR_MESSAGE, entity.getClass().getName()),
309
						TaxeditorBulkeditorPlugin.PLUGIN_ID,
310
						e,
311
						true);
312
			}
313
		}
314
		return true;
315
	}
316

    
317
	public void saveModel(){
318
	    saveModel(true);
319
	}
320

    
321
	public void saveModel(boolean resetMerge){
322
	    //delete entities
323
	    for(Entry<T, DeleteConfiguratorBase> entry:toDelete.entrySet()){
324
	        try {
325
                delete(entry.getKey(), entry.getValue());
326
            } catch (ReferencedObjectUndeletableException e) {
327
                e.printStackTrace();
328
            }
329
	    }
330
	    if (!saveCandidates.isEmpty()){
331
	        CdmStore.getService(saveCandidates.iterator().next()).merge(new ArrayList<>(saveCandidates), true);
332
        }
333
	    if(resetMerge){
334
	        //merge entities
335
	        for(T mergeTarget:mergedEntities.keySet()){
336
	            for (T mergeCandidate: mergedEntities.get(mergeTarget)){
337
	                merge(mergeCandidate, mergeTarget);
338
	            }
339
	        }
340
	    }
341
	    toDelete.clear();
342
	    saveCandidates.clear();
343
	    mergedEntities.clear();
344
	}
345

    
346

    
347
	/** {@inheritDoc} */
348
	@Override
349
    public T create(T entity) {
350
		return save(entity);
351
	}
352

    
353
	public IEntityCreator<T> getEntityCreator(){
354
		if(entityCreator == null){
355
			entityCreator = createEntityCreator();
356
		}
357
		return entityCreator;
358
	}
359

    
360
	protected abstract IEntityCreator<T> createEntityCreator();
361

    
362
	/**
363
	 * The default implementation returns an empty list of sort providers.
364
	 * @return
365
	 */
366
	public List<IBulkEditorSortProvider<T>> getSortProviders(){
367
		List<IBulkEditorSortProvider<T>> sortProviders = new ArrayList<IBulkEditorSortProvider<T>>();
368

    
369
		sortProviders.add(new CdmBaseSortProvider<T>());
370

    
371
		return sortProviders;
372
	}
373

    
374
	/**
375
	 * Returns a textual representation given object. The default implementation
376
	 * in the abstract base class returns the simple name of the class, this may
377
	 * be overwritten to something more specific in subclasses.
378
	 *
379
	 * @param entity
380
	 * @return a textual representation given object.
381
	 */
382
	public String getTypeText(Object entity){
383
		return entity.getClass().getSimpleName();
384
	}
385

    
386
	public String getText(T entity) {
387
		if(entity instanceof IdentifiableEntity){
388
			IdentifiableEntity identifiableEntity = (IdentifiableEntity) HibernateProxyHelper.deproxy(entity);
389
			String text = identifiableEntity.getTitleCache();
390
			return text;
391
		}
392

    
393
		return "No text. Implement in subclass"; //$NON-NLS-1$
394
	}
395

    
396
	public BasicEventList<T> getModel() {
397
		return model;
398
	}
399

    
400
	public boolean replaceInModel(T entity) {
401
	    int index = model.indexOf(entity);
402
	    if(index >= 0) {
403
	        model.set(index, entity);
404
	        return true;
405
	    } else {
406
	        return false;
407
	    }
408
	}
409

    
410
    @Override
411
    public List<T> getRootEntities() {
412
        return getModel();
413
    }
414

    
415

    
416
    @Override
417
    public Map<Object, List<String>> getPropertyPathsMap() {
418
        // TODO Auto-generated method stub
419
        return null;
420
    }
421

    
422
	public ConversationHolder getConversation() {
423
		return conversation;
424
	}
425

    
426
	public Set<T> getSaveCandidates() {
427
        return saveCandidates;
428
    }
429

    
430
    public HashMap<T, Set<T>> getMergedEntities() {
431
        return mergedEntities;
432
    }
433

    
434
    public void setMergedEntities(HashMap<T, Set<T>> mergedEntities) {
435
        this.mergedEntities = mergedEntities;
436
    }
437

    
438
}
(1-1/11)