-// $Id$
/**
* Copyright (C) 2007 EDIT
* European Distributed Institute of Taxonomy
package eu.etaxonomy.taxeditor.bulkeditor.input;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
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.api.service.exception.ReferencedObjectUndeletableException;
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
import eu.etaxonomy.cdm.model.common.CdmBase;
-import eu.etaxonomy.cdm.model.common.ICdmBase;
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
import eu.etaxonomy.cdm.model.common.MarkerType;
import eu.etaxonomy.cdm.strategy.merge.IMergable;
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;
/**
- * <p>Abstract AbstractBulkEditorInput class.</p>
- *
* @author p.ciardelli
* @created 25.06.2009
* @version 1.0
* @param <T>
*/
-public abstract class AbstractBulkEditorInput<T extends ICdmBase> extends CdmEntitySessionInput implements IEditorInput ,
+public abstract class AbstractBulkEditorInput<T extends CdmBase> extends CdmEntitySessionInput implements
IEntityPersistenceService<T> {
+
+ 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;
+ private BasicEventList<T> model = new BasicEventList<>();
+
+ private Map<T, DeleteConfiguratorBase> toDelete = new HashMap<>();
+ private Set<T> saveCandidates = new HashSet<>();
+
+
+ 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 static Class serviceClass;
+ private Job searchJob;
public AbstractBulkEditorInput() {
-
+ super(true);
+ this.conversation = CdmStore.createConversation();
}
- /**
- * <p>NewInstance</p>
- *
- * @param inputType a {@link eu.etaxonomy.taxeditor.bulkeditor.input.BulkEditorInputTypeValues.BulkEditorInputType} object.
- * @return a {@link eu.etaxonomy.taxeditor.bulkeditor.input.AbstractBulkEditorInput} object.
- */
static public AbstractBulkEditorInput NewInstance(BulkEditorInputType inputType) {
return BulkEditorInputType.getInput(inputType);
}
- /**
- * <p>NewInstance</p>
- *
- * @param entity a {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity} object.
- * @return a {@link eu.etaxonomy.taxeditor.bulkeditor.input.AbstractBulkEditorInput} object.
- */
public static AbstractBulkEditorInput NewInstance(IdentifiableEntity entity) {
-
BulkEditorInputType inputType = BulkEditorInputType.getByType(entity.getClass());
AbstractBulkEditorInput editorInput = NewInstance(inputType);
return editorInput;
}
- /**
- * <p>listEntities</p>
- *
- * @param configurator a {@link eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator} object.
- * @return a {@link java.util.List} object.
- */
+ public abstract String getName();
+
+ public String getEditorName(){
+ return getName();
+ }
+
+ protected int getPageSize(){
+ return 100;
+ }
+
protected abstract List<T> listEntities(IIdentifiableEntityServiceConfigurator configurator);
- /**
- * <p>loadEntity</p>
- *
- * @param entityUuid a {@link java.util.UUID} object.
- * @return a T object.
- */
- protected T loadEntity(UUID entityUuid) {
- List<String> propertyPaths = Arrays.asList(new String[]{});
- return (T) CdmStore.getService(serviceClass).load(entityUuid, propertyPaths);
- }
+ protected abstract long countEntities(IIdentifiableEntityServiceConfigurator configurator);
- private void setEntityUuid(UUID entityUuid){
- this.entityUuid = entityUuid;
+ protected abstract T loadEntity(UUID entityUuid);
+
+ public List<String> getPropertyKeys(){
+ List<String> properties = new ArrayList<>();
+ properties.add(getName());
+ properties.add(TYPE_PROPERTY);
+ properties.addAll(getPropertyKeys_internal());
+ 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(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 Comparator<T> getTitleComparator(){
+ return new TitleCacheComparator();
}
- /**
- * <p>Getter for the field <code>entityUuid</code>.</p>
- *
- * @return a {@link java.util.UUID} object.
- */
- public UUID getEntityUuid() {
- return entityUuid;
+ public void setMergeTarget(T t){
+ markedMergeTarget = t;
}
+ public Set<T> getMergeCandidates() {
+ return markedMergeCandidates;
+ }
- /* (non-Javadoc)
- * @see org.eclipse.ui.IEditorInput#exists()
- */
- /**
- * <p>exists</p>
- *
- * @return a boolean.
- */
- @Override
- public boolean exists() {
- // TODO Auto-generated method stub
- return false;
+ public T getMergeTarget() {
+ return markedMergeTarget;
+ }
+
+ public void removeMergeTarget(){
+ markedMergeTarget = null;
}
- /* (non-Javadoc)
- * @see org.eclipse.ui.IEditorInput#getImageDescriptor()
- */
- /**
- * <p>getImageDescriptor</p>
- *
- * @return a {@link org.eclipse.jface.resource.ImageDescriptor} object.
- */
- @Override
- public ImageDescriptor getImageDescriptor() {
- // TODO Auto-generated method stub
- return null;
+ public void addMergeCandidate(T t){
+ markedMergeCandidates.add(t);
}
- /* (non-Javadoc)
- * @see org.eclipse.ui.IEditorInput#getPersistable()
- */
- /**
- * <p>getPersistable</p>
- *
- * @return a {@link org.eclipse.ui.IPersistableElement} object.
- */
- @Override
- public IPersistableElement getPersistable() {
- return null;
+ public void removeMergeCandidate(T t){
+ markedMergeCandidates.remove(t);
}
- /* (non-Javadoc)
- * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
- */
- /** {@inheritDoc} */
- @Override
- @SuppressWarnings("unchecked")
- public Object getAdapter(Class adapter) {
- return null;
+ public void addToDelete(T t, DeleteConfiguratorBase config) {
+ toDelete.put(t, config);
+ }
+ public void addSaveCandidate(T t){
+ saveCandidates.add(t);
+ }
+ private void setEntityUuid(UUID entityUuid){
+ this.entityUuid = entityUuid;
}
- /**
- * <p>Setter for the field <code>query</code>.</p>
- *
- * @param bulkEditorQuery a {@link eu.etaxonomy.taxeditor.bulkeditor.IBulkEditorQuery} object.
- */
- public void performSearch(final BulkEditorQuery bulkEditorQuery) {
+ public UUID getEntityUuid() {
+ return entityUuid;
+ }
- 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){
+ 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();
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);
}
- /**
- * <p>isMergingEnabled</p>
- *
- * @return a boolean.
- */
public boolean isMergingEnabled() {
return false;
}
- /**
- * <p>isMergingEnabled</p>
- *
- * @return a boolean.
- */
public boolean isConvertingEnabled() {
return false;
}
- /**
- * <p>isMarkerTypeEditingEnabled</p>
- *
- * @param markerType a {@link eu.etaxonomy.cdm.model.common.MarkerType} object.
- * @return a boolean.
- */
+
public boolean isMarkerTypeEditingEnabled(MarkerType markerType) {
return false;
}
public boolean merge(T entity, T mergeTarget) {
if (entity instanceof IMergable) {
try {
- CdmStore.getCommonService().merge(mergeTarget.getId(), entity.getId(), (Class<? extends CdmBase>)entity.getClass());
+ 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);
return true;
}
+ public void saveModel(){
+ saveModel(true);
+ }
+
+ public void saveModel(boolean resetMerge){
+ //delete entities
+ for(Entry<T, DeleteConfiguratorBase> entry:toDelete.entrySet()){
+ try {
+ delete(entry.getKey(), entry.getValue());
+ } catch (ReferencedObjectUndeletableException e) {
+ e.printStackTrace();
+ }
+ }
+ if (!saveCandidates.isEmpty()){
+ CdmStore.getService(saveCandidates.iterator().next()).merge(new ArrayList<>(saveCandidates), true);
+ }
+ if(resetMerge){
+ //merge entities
+ for(T mergeTarget:mergedEntities.keySet()){
+ for (T mergeCandidate: mergedEntities.get(mergeTarget)){
+ merge(mergeCandidate, mergeTarget);
+ }
+ }
+ }
+ toDelete.clear();
+ saveCandidates.clear();
+ mergedEntities.clear();
+ }
+
/** {@inheritDoc} */
@Override
return entityCreator;
}
- /**
- * @return
- */
protected abstract IEntityCreator<T> createEntityCreator();
/**
return entity.getClass().getSimpleName();
}
- /**
- * @param entity
- * @return
- */
public String getText(T entity) {
if(entity instanceof IdentifiableEntity){
IdentifiableEntity identifiableEntity = (IdentifiableEntity) HibernateProxyHelper.deproxy(entity);
-
- return identifiableEntity.getTitleCache();
+ String text = identifiableEntity.getTitleCache();
+ return text;
}
- return "No text. Implement in subclass";
+ return "No text. Implement in subclass"; //$NON-NLS-1$
}
- /**
- * @return
- */
- 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);
}
}
- /* (non-Javadoc)
- * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled#getRootEntities()
- */
@Override
- public List<T> getRootEntities() {
+ public List<T> getRootEntities() {
return getModel();
}
- /* (non-Javadoc)
- * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled#getPropertyPathsMap()
- */
@Override
public Map<Object, List<String>> getPropertyPathsMap() {
// TODO Auto-generated method stub
return null;
}
+
+ 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;
+ }
+
}