package eu.etaxonomy.taxeditor.bulkeditor.input;
import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.UUID;
+import java.util.stream.Collectors;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.ui.IEditorInput;
-import org.eclipse.ui.IPersistableElement;
+import org.eclipse.core.runtime.ICoreRunnable;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import ca.odell.glazedlists.BasicEventList;
import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
import eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator;
import eu.etaxonomy.cdm.model.common.CdmBase;
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
import eu.etaxonomy.cdm.model.common.MarkerType;
+import eu.etaxonomy.cdm.persistence.dto.MergeResult;
import eu.etaxonomy.cdm.strategy.merge.IMergable;
import eu.etaxonomy.cdm.strategy.merge.MergeException;
import eu.etaxonomy.taxeditor.annotatedlineeditor.IEntityCreator;
import eu.etaxonomy.taxeditor.bulkeditor.input.sortprovider.TitleCacheComparator;
import eu.etaxonomy.taxeditor.bulkeditor.internal.TaxeditorBulkeditorPlugin;
import eu.etaxonomy.taxeditor.editor.CdmEntitySessionInput;
+import eu.etaxonomy.taxeditor.event.EventUtility;
+import eu.etaxonomy.taxeditor.event.WorkbenchEventConstants;
+import eu.etaxonomy.taxeditor.l10n.Messages;
import eu.etaxonomy.taxeditor.model.MessagingUtils;
import eu.etaxonomy.taxeditor.store.CdmStore;
/**
* @author p.ciardelli
* @created 25.06.2009
- * @version 1.0
- * @param <T>
*/
-public abstract class AbstractBulkEditorInput<T extends CdmBase> extends CdmEntitySessionInput implements IEditorInput ,
- IEntityPersistenceService<T> {
+public abstract class AbstractBulkEditorInput<T extends CdmBase>
+ extends CdmEntitySessionInput
+ implements IEntityPersistenceService<T> {
+
+
+ private static final String PROPERTY_PROTECTED_TITLECACHE = "Protect TitleCache";
+ private static final String TYPE_PROPERTY = Messages.BulkEditorE4_TYPE;
+ private static final String ID_PROPERTY = "Id"; //$NON-NLS-1$
+ private static final String UUID_PROPERTY = "Uuid"; //$NON-NLS-1$
private UUID entityUuid;
- private List<T> model = new ArrayList<>();
+ private BasicEventList<T> model = new BasicEventList<>();
private Map<T, DeleteConfiguratorBase> toDelete = new HashMap<>();
+ public Map<T, DeleteConfiguratorBase> getToDelete() {
+ return toDelete;
+ }
+
+ private Set<T> saveCandidates = new HashSet<>();
+
- private List<T> mergeCandidates = new ArrayList<>();
- private T mergeTarget = null;
+ private Set<T> markedMergeCandidates = new HashSet<>();
+ private T markedMergeTarget = null;
+
+ private HashMap<T, Set<T>> mergedEntities = new HashMap<>();
private IEntityCreator<T> entityCreator;
private final ConversationHolder conversation;
+ private Job searchJob;
+
public AbstractBulkEditorInput() {
super(true);
this.conversation = CdmStore.createConversation();
BulkEditorInputType inputType = BulkEditorInputType.getByType(entity.getClass());
- AbstractBulkEditorInput editorInput = NewInstance(inputType);
+ AbstractBulkEditorInput<?> editorInput = NewInstance(inputType);
editorInput.setEntityUuid(entity.getUuid());
return editorInput;
}
+ public static AbstractBulkEditorInput NewInstance(Class clazz, UUID uuid) {
+
+ BulkEditorInputType inputType = BulkEditorInputType.getByType(clazz);
+
+ AbstractBulkEditorInput<?> editorInput = NewInstance(inputType);
+
+ editorInput.setEntityUuid(uuid);
+
+ return editorInput;
+ }
+
+ public abstract String getName();
+
+ public String getEditorName(){
+ return getName();
+ }
+
+ protected int getPageSize(){
+ return 100;
+ }
+
protected abstract List<T> listEntities(IIdentifiableEntityServiceConfigurator configurator);
+ protected abstract long countEntities(IIdentifiableEntityServiceConfigurator configurator);
+
protected abstract T loadEntity(UUID entityUuid);
+ public List<String> getPropertyKeys(){
+ List<String> properties = new ArrayList<>();
+ properties.add(getName());
+ properties.add(PROPERTY_PROTECTED_TITLECACHE);
+ properties.addAll(getPropertyKeys_internal());
+ properties.add(TYPE_PROPERTY);
+ properties.add(ID_PROPERTY);
+ properties.add(UUID_PROPERTY);
+ return properties;
+ }
+
+ protected abstract List<String> getPropertyKeys_internal();
+
+
+ public Object getPropertyValue(T cdmBase, String property){
+ if(property.equals(getName())){
+ return getText(cdmBase);
+ }
+ else if(property.equals(PROPERTY_PROTECTED_TITLECACHE)
+ &&cdmBase.isInstanceOf(IdentifiableEntity.class)){
+ return HibernateProxyHelper.deproxy(cdmBase, IdentifiableEntity.class).isProtectedTitleCache();
+ }
+ else if(property.equals(TYPE_PROPERTY)){
+ return getTypeText(cdmBase);
+ }
+ else if(property.equals(UUID_PROPERTY)){
+ return cdmBase.getUuid();
+ }
+ else if(property.equals(ID_PROPERTY)){
+ return cdmBase.getId();
+ }
+ return null;
+ }
+
+ public boolean isBooleanProperty(String property) {
+ if(property.equals(PROPERTY_PROTECTED_TITLECACHE)){
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isCacheProperty(String property) {
+ if(property.equals(PROPERTY_PROTECTED_TITLECACHE)){
+ return true;
+ }
+ return false;
+ }
+
+ public <T extends IdentifiableEntity> Comparator<T> getTitleComparator(){
+ return new TitleCacheComparator<T>();
+ }
+
public void setMergeTarget(T t){
- mergeTarget = t;
+ markedMergeTarget = t;
}
- public List<T> getMergeCandidates() {
- return mergeCandidates;
+ public Set<T> getMergeCandidates() {
+ return markedMergeCandidates;
}
public T getMergeTarget() {
- return mergeTarget;
+ return markedMergeTarget;
}
public void removeMergeTarget(){
- mergeTarget = null;
+ markedMergeTarget = null;
}
public void addMergeCandidate(T t){
- mergeCandidates.add(t);
+ markedMergeCandidates.add(t);
}
public void removeMergeCandidate(T t){
- mergeCandidates.remove(t);
+ markedMergeCandidates.remove(t);
}
public void addToDelete(T t, DeleteConfiguratorBase config) {
toDelete.put(t, config);
}
-
- private void setEntityUuid(UUID entityUuid){
+ public void addSaveCandidate(T t){
+ saveCandidates.add(t);
+ }
+ public void setEntityUuid(UUID entityUuid){
this.entityUuid = entityUuid;
}
return entityUuid;
}
- @Override
- public boolean exists() {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public ImageDescriptor getImageDescriptor() {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public IPersistableElement getPersistable() {
- return null;
- }
-
- /** {@inheritDoc} */
- @Override
- @SuppressWarnings("unchecked")
- public Object getAdapter(Class adapter) {
- return null;
- }
-
- public void performSearch(final BulkEditorQuery bulkEditorQuery) {
-
- List<T> entityList = new ArrayList<T>();
+ public void performSearch(final BulkEditorQuery bulkEditorQuery, IStructuredSelection selection) {
+ //cancel previous search job
+ if(searchJob!=null && searchJob.getState()!=Job.NONE){
+ boolean isCanceled = searchJob.cancel();
+ if (!isCanceled){
+ while (!isCanceled){
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ }
+ isCanceled = searchJob.cancel();
+ }
+ }
+ searchJob = null;
+// /*
+// * wait for a little while for the job to finish
+// * to avoid asynchronously loaded results of the
+// * previous search being shown in the next search
+// * (not critical but explicitly waiting for the job to finish
+// * could run into an endless loop by mistake)
+// */
+// try {
+// Thread.sleep(500);
+// } catch (InterruptedException e) {
+// }
+ }
+ model.clear();
+ markedMergeCandidates.clear();
+ markedMergeTarget = null;
if(getEntityUuid() != null){
-
T entity = loadEntity(getEntityUuid());
- entityList.add(entity);
- model = entityList;
+ model.add(entity);
}
else if(bulkEditorQuery != null){
-
- IIdentifiableEntityServiceConfigurator configurator = bulkEditorQuery.getSearchConfigurator();
- Comparator queryComparator = (bulkEditorQuery.getComparator() != null) ? bulkEditorQuery.getComparator() : new TitleCacheComparator();
-
- entityList = listEntities(configurator);
-
- Collections.sort(entityList, queryComparator);
-
-
+ IIdentifiableEntityServiceConfigurator<?> configurator = bulkEditorQuery.getSearchConfigurator();
+
+ // check for UUID search
+ String titleSearchString = configurator.getTitleSearchString();
+ try {
+ UUID uuid = UUID.fromString(titleSearchString);
+ T entity = loadEntity(uuid);
+ //UUID search found -> add entity to list and return
+ model.add(entity);
+ return;
+ } catch (IllegalArgumentException e) {
+ // search string was no UUID
+ }
+ //-> continue with standard search
+
+ int pageSize = configurator.getPageSize()!=null?configurator.getPageSize():getPageSize();
+ configurator.setPageSize(pageSize);
+ long count = countEntities(configurator);
+ int totalWork = count>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)count;
+ String jobLabel = String.format(Messages.AbstractBulkEditorInput_LOADING, getName(), bulkEditorQuery.getSearchString());
+ searchJob = Job.create(jobLabel, (ICoreRunnable) monitor -> {
+ monitor.beginTask(jobLabel, totalWork);
+ int pageNumber = 0;
+ List<T> entities;
+
+ //load previously selected element
+ UUID selectedUuid = null;
+ if(selection!=null && selection.getFirstElement() instanceof CdmBase){
+ selectedUuid = ((CdmBase) selection.getFirstElement()).getUuid();
+ T entity = loadEntity(selectedUuid);
+ model.add(entity);
+ }
+
+ do {
+ if (monitor.isCanceled()) {
+ break;
+ }
+ configurator.setPageNumber(pageNumber);
+ entities = listEntities(configurator);
+
+ addToModel(entities, selectedUuid);
+
+ pageNumber++;
+ monitor.worked(pageSize);
+ long workedLong = pageSize*pageNumber;
+ int loadedCount = workedLong>Integer.MAX_VALUE?Integer.MAX_VALUE:(int)workedLong;
+ monitor.setTaskName(String.format(Messages.AbstractBulkEditorInput_LOADED, loadedCount, totalWork, getName()));
+
+ //Update selection
+ EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
+ } while (!entities.isEmpty());
+ monitor.done();
+ EventUtility.postAsyncEvent(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED, selection);
+ });
+ searchJob.schedule();
}
+ }
- model = entityList;
+ private void addToModel(List<T> entities, UUID selectedUuid){
+ //filter pre-loaded previously selected element
+ if(selectedUuid!=null){
+ entities = entities.stream().filter(entity->!entity.getUuid().equals(selectedUuid)).collect(Collectors.toList());
+ }
+ /*
+ * IMPORTANT!
+ * Entities have to be loaded into the main session because they are
+ * loaded in a parallel asynchronous thread
+ */
+ if(getCdmEntitySession()!=null){//is null when closing the bulk editor during loading
+ getCdmEntitySession().load(entities, true);
+ }
+ model.addAll(entities);
}
public boolean isMergingEnabled() {
return false;
}
-
- /** {@inheritDoc} */
@Override
public boolean merge(T entity, T mergeTarget) {
if (entity instanceof IMergable) {
try {
CdmStore.getCommonService().merge(mergeTarget.getUuid(), entity.getUuid(), (Class<? extends CdmBase>)entity.getClass());
} catch (MergeException e) {
- MessagingUtils.errorDialog("Bulk Editor Merge Error",
+ MessagingUtils.errorDialog(Messages.AbstractBulkEditorInput_MERGE_ERROR_TITLE,
this,
- "Could not merge chosen objects of type " + entity.getClass().getName(),
+ String.format(Messages.AbstractBulkEditorInput_MERGE_ERROR_MESSAGE, entity.getClass().getName()),
TaxeditorBulkeditorPlugin.PLUGIN_ID,
e,
true);
}
public void saveModel(){
+ saveModel(true);
+ }
+
+ public void saveModel(boolean resetMerge){
//delete entities
for(Entry<T, DeleteConfiguratorBase> entry:toDelete.entrySet()){
try {
e.printStackTrace();
}
}
- toDelete.clear();
- //merge entities
- for(T mergeCandidate:mergeCandidates){
- merge(mergeCandidate, mergeTarget);
+ if (!saveCandidates.isEmpty()){
+ List<MergeResult<T>> results = CdmStore.getService(saveCandidates.iterator().next()).merge(new ArrayList<>(saveCandidates), true);
+ for (MergeResult<T> result: results){
+ if (result.getMergedEntity() != null){
+ T entity = result.getMergedEntity();
+ }
+ }
+ }
+ if(resetMerge){
+ //merge entities
+ for(T mergeTarget:mergedEntities.keySet()){
+ for (T mergeCandidate: mergedEntities.get(mergeTarget)){
+ merge(mergeCandidate, mergeTarget);
+ }
+ }
}
- mergeCandidates.clear();
- mergeTarget = null;
+ toDelete.clear();
+ saveCandidates.clear();
+ mergedEntities.clear();
}
-
- /** {@inheritDoc} */
@Override
public T create(T entity) {
return save(entity);
* @return
*/
public List<IBulkEditorSortProvider<T>> getSortProviders(){
- List<IBulkEditorSortProvider<T>> sortProviders = new ArrayList<IBulkEditorSortProvider<T>>();
+ List<IBulkEditorSortProvider<T>> sortProviders = new ArrayList<>();
- sortProviders.add(new CdmBaseSortProvider<T>());
+ sortProviders.add(new CdmBaseSortProvider<>());
return sortProviders;
}
}
public String getText(T entity) {
- if(entity instanceof IdentifiableEntity){
- IdentifiableEntity identifiableEntity = (IdentifiableEntity) HibernateProxyHelper.deproxy(entity);
-
- return identifiableEntity.getTitleCache();
+ if(entity.isInstanceOf(IdentifiableEntity.class)){
+ IdentifiableEntity<?> identifiableEntity = HibernateProxyHelper.deproxy(entity, IdentifiableEntity.class);
+ String text = identifiableEntity.getTitleCache();
+ return text;
}
- return "No text. Implement in subclass";
+ return "No text. Implement in subclass"; //$NON-NLS-1$
}
- public List<T> getModel() {
+ public BasicEventList<T> getModel() {
return model;
}
- protected boolean replaceInModel(T entity) {
+ public boolean replaceInModel(T entity) {
int index = model.indexOf(entity);
if(index >= 0) {
model.set(index, entity);
}
@Override
- public List<T> getRootEntities() {
+ public Collection<T> getRootEntities() {
return getModel();
}
public ConversationHolder getConversation() {
return conversation;
}
+
+ public Set<T> getSaveCandidates() {
+ return saveCandidates;
+ }
+
+ public HashMap<T, Set<T>> getMergedEntities() {
+ return mergedEntities;
+ }
+
+ public void setMergedEntities(HashMap<T, Set<T>> mergedEntities) {
+ this.mergedEntities = mergedEntities;
+ }
+
}