ref #7505 re-enable parallel loading for bulk editor
[taxeditor.git] / eu.etaxonomy.taxeditor.bulkeditor / src / main / java / eu / etaxonomy / taxeditor / bulkeditor / input / AbstractBulkEditorInput.java
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
21 import org.eclipse.core.runtime.ICoreRunnable;
22 import org.eclipse.core.runtime.jobs.Job;
23 import org.eclipse.jface.viewers.IStructuredSelection;
24
25 import ca.odell.glazedlists.BasicEventList;
26 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
27 import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
28 import eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator;
29 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
30 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
31 import eu.etaxonomy.cdm.model.common.CdmBase;
32 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
33 import eu.etaxonomy.cdm.model.common.MarkerType;
34 import eu.etaxonomy.cdm.strategy.merge.IMergable;
35 import eu.etaxonomy.cdm.strategy.merge.MergeException;
36 import eu.etaxonomy.taxeditor.annotatedlineeditor.IEntityCreator;
37 import eu.etaxonomy.taxeditor.annotatedlineeditor.IEntityPersistenceService;
38 import eu.etaxonomy.taxeditor.bulkeditor.BulkEditorQuery;
39 import eu.etaxonomy.taxeditor.bulkeditor.IBulkEditorSortProvider;
40 import eu.etaxonomy.taxeditor.bulkeditor.input.sortprovider.CdmBaseSortProvider;
41 import eu.etaxonomy.taxeditor.bulkeditor.input.sortprovider.TitleCacheComparator;
42 import eu.etaxonomy.taxeditor.bulkeditor.internal.TaxeditorBulkeditorPlugin;
43 import eu.etaxonomy.taxeditor.editor.CdmEntitySessionInput;
44 import eu.etaxonomy.taxeditor.event.EventUtility;
45 import eu.etaxonomy.taxeditor.event.WorkbenchEventConstants;
46 import eu.etaxonomy.taxeditor.l10n.Messages;
47 import eu.etaxonomy.taxeditor.model.MessagingUtils;
48 import eu.etaxonomy.taxeditor.store.CdmStore;
49
50 /**
51 * @author p.ciardelli
52 * @created 25.06.2009
53 * @version 1.0
54 * @param <T>
55 */
56 public abstract class AbstractBulkEditorInput<T extends CdmBase> extends CdmEntitySessionInput implements
57 IEntityPersistenceService<T> {
58
59 private UUID entityUuid;
60
61 private BasicEventList<T> model = new BasicEventList<>();
62
63 private Map<T, DeleteConfiguratorBase> toDelete = new HashMap<>();
64 private Set<T> saveCandidates = new HashSet<>();
65
66
67 private Set<T> markedMergeCandidates = new HashSet<>();
68 private T markedMergeTarget = null;
69
70 private HashMap<T, Set<T>> mergedEntities = new HashMap<>();
71
72 private IEntityCreator<T> entityCreator;
73 private final ConversationHolder conversation;
74
75 private Job searchJob;
76
77 public AbstractBulkEditorInput() {
78 super(true);
79 this.conversation = CdmStore.createConversation();
80 }
81
82 static public AbstractBulkEditorInput NewInstance(BulkEditorInputType inputType) {
83
84 return BulkEditorInputType.getInput(inputType);
85 }
86
87 public static AbstractBulkEditorInput NewInstance(IdentifiableEntity entity) {
88
89 BulkEditorInputType inputType = BulkEditorInputType.getByType(entity.getClass());
90
91 AbstractBulkEditorInput editorInput = NewInstance(inputType);
92
93 editorInput.setEntityUuid(entity.getUuid());
94
95 return editorInput;
96 }
97
98 public abstract String getName();
99
100 public String getEditorName(){
101 return getName();
102 }
103
104 protected int getPageSize(){
105 return 100;
106 }
107
108 protected abstract List<T> listEntities(IIdentifiableEntityServiceConfigurator configurator);
109
110 protected abstract long countEntities(IIdentifiableEntityServiceConfigurator configurator);
111
112 protected abstract T loadEntity(UUID entityUuid);
113
114 public Comparator<T> getTitleComparator(){
115 return new TitleCacheComparator();
116 }
117
118 public void setMergeTarget(T t){
119 markedMergeTarget = t;
120 }
121
122 public Set<T> getMergeCandidates() {
123 return markedMergeCandidates;
124 }
125
126 public T getMergeTarget() {
127 return markedMergeTarget;
128 }
129
130 public void removeMergeTarget(){
131 markedMergeTarget = null;
132 }
133
134 public void addMergeCandidate(T t){
135 markedMergeCandidates.add(t);
136 }
137
138 public void removeMergeCandidate(T t){
139 markedMergeCandidates.remove(t);
140 }
141
142 public void addToDelete(T t, DeleteConfiguratorBase config) {
143 toDelete.put(t, config);
144 }
145 public void addSaveCandidate(T t){
146 saveCandidates.add(t);
147 }
148 private void setEntityUuid(UUID entityUuid){
149 this.entityUuid = entityUuid;
150 }
151
152 public UUID getEntityUuid() {
153 return entityUuid;
154 }
155
156 public void performSearch(final BulkEditorQuery bulkEditorQuery, IStructuredSelection selection) {
157 //cancel previous search job
158 if(searchJob!=null && searchJob.getState()!=Job.NONE){
159 searchJob.cancel();
160 searchJob = null;
161 }
162 model.clear();
163
164 if(getEntityUuid() != null){
165
166 T entity = loadEntity(getEntityUuid());
167 model.add(entity);
168 }
169 else if(bulkEditorQuery != null){
170
171 IIdentifiableEntityServiceConfigurator configurator = bulkEditorQuery.getSearchConfigurator();
172
173 int pageSize = configurator.getPageSize()!=null?configurator.getPageSize():getPageSize();
174 configurator.setPageSize(pageSize);
175 long count = countEntities(configurator);
176 int totalWork = count>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)count;
177 String jobLabel = String.format(Messages.AbstractBulkEditorInput_LOADING, getName(), bulkEditorQuery.getSearchString());
178 searchJob = Job.create(jobLabel, (ICoreRunnable) monitor -> {
179 monitor.beginTask(jobLabel, totalWork);
180 int pageNumber = 0;
181 List<T> entities;
182 do {
183 if (monitor.isCanceled()) {
184 break;
185 }
186 configurator.setPageNumber(pageNumber);
187 entities = listEntities(configurator);
188 model.addAll(entities);
189 //select if entity is loaded
190 if(selection!=null && model.containsAll(selection.toList())){
191 EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
192 }
193 pageNumber++;
194 monitor.worked(pageSize);
195 long workedLong = pageSize*pageNumber;
196 int loadedCount = workedLong>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)workedLong;
197 monitor.setTaskName(String.format(Messages.AbstractBulkEditorInput_LOADED, loadedCount, totalWork, getName()));
198 } while (!entities.isEmpty());
199 monitor.done();
200 EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
201 });
202 searchJob.schedule();
203 }
204 }
205
206 public boolean isMergingEnabled() {
207 return false;
208 }
209
210 public boolean isConvertingEnabled() {
211 return false;
212 }
213
214 public boolean isMarkerTypeEditingEnabled(MarkerType markerType) {
215 return false;
216 }
217
218
219 /** {@inheritDoc} */
220 @Override
221 public boolean merge(T entity, T mergeTarget) {
222 if (entity instanceof IMergable) {
223 try {
224 CdmStore.getCommonService().merge(mergeTarget.getUuid(), entity.getUuid(), (Class<? extends CdmBase>)entity.getClass());
225 } catch (MergeException e) {
226 MessagingUtils.errorDialog(Messages.AbstractBulkEditorInput_MERGE_ERROR_TITLE,
227 this,
228 String.format(Messages.AbstractBulkEditorInput_MERGE_ERROR_MESSAGE, entity.getClass().getName()),
229 TaxeditorBulkeditorPlugin.PLUGIN_ID,
230 e,
231 true);
232 }
233 }
234 return true;
235 }
236
237 public void saveModel(){
238 saveModel(true);
239 }
240
241 public void saveModel(boolean resetMerge){
242 //delete entities
243 for(Entry<T, DeleteConfiguratorBase> entry:toDelete.entrySet()){
244 try {
245 delete(entry.getKey(), entry.getValue());
246 } catch (ReferencedObjectUndeletableException e) {
247 e.printStackTrace();
248 }
249 }
250 if (!saveCandidates.isEmpty()){
251 CdmStore.getService(saveCandidates.iterator().next()).merge(new ArrayList<>(saveCandidates), true);
252 }
253 if(resetMerge){
254 //merge entities
255 for(T mergeTarget:mergedEntities.keySet()){
256 for (T mergeCandidate: mergedEntities.get(mergeTarget)){
257 merge(mergeCandidate, mergeTarget);
258 }
259 }
260 }
261 toDelete.clear();
262 saveCandidates.clear();
263 mergedEntities.clear();
264 }
265
266
267 /** {@inheritDoc} */
268 @Override
269 public T create(T entity) {
270 return save(entity);
271 }
272
273 public IEntityCreator<T> getEntityCreator(){
274 if(entityCreator == null){
275 entityCreator = createEntityCreator();
276 }
277 return entityCreator;
278 }
279
280 protected abstract IEntityCreator<T> createEntityCreator();
281
282 /**
283 * The default implementation returns an empty list of sort providers.
284 * @return
285 */
286 public List<IBulkEditorSortProvider<T>> getSortProviders(){
287 List<IBulkEditorSortProvider<T>> sortProviders = new ArrayList<IBulkEditorSortProvider<T>>();
288
289 sortProviders.add(new CdmBaseSortProvider<T>());
290
291 return sortProviders;
292 }
293
294 /**
295 * Returns a textual representation given object. The default implementation
296 * in the abstract base class returns the simple name of the class, this may
297 * be overwritten to something more specific in subclasses.
298 *
299 * @param entity
300 * @return a textual representation given object.
301 */
302 public String getTypeText(Object entity){
303 return entity.getClass().getSimpleName();
304 }
305
306 public String getText(T entity) {
307 if(entity instanceof IdentifiableEntity){
308 IdentifiableEntity identifiableEntity = (IdentifiableEntity) HibernateProxyHelper.deproxy(entity);
309 String text = identifiableEntity.getTitleCache();
310 return text;
311 }
312
313 return "No text. Implement in subclass"; //$NON-NLS-1$
314 }
315
316 public BasicEventList<T> getModel() {
317 return model;
318 }
319
320 public boolean replaceInModel(T entity) {
321 int index = model.indexOf(entity);
322 if(index >= 0) {
323 model.set(index, entity);
324 return true;
325 } else {
326 return false;
327 }
328 }
329
330 @Override
331 public List<T> getRootEntities() {
332 return getModel();
333 }
334
335
336 @Override
337 public Map<Object, List<String>> getPropertyPathsMap() {
338 // TODO Auto-generated method stub
339 return null;
340 }
341
342 public ConversationHolder getConversation() {
343 return conversation;
344 }
345
346 public Set<T> getSaveCandidates() {
347 return saveCandidates;
348 }
349
350 public HashMap<T, Set<T>> getMergedEntities() {
351 return mergedEntities;
352 }
353
354 public void setMergedEntities(HashMap<T, Set<T>> mergedEntities) {
355 this.mergedEntities = mergedEntities;
356 }
357 }