import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
+import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
-import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
import eu.etaxonomy.cdm.api.service.IAgentService;
import eu.etaxonomy.cdm.api.service.IDescriptionService;
import eu.etaxonomy.cdm.api.service.IEventBaseService;
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
import eu.etaxonomy.cdm.model.term.DefinedTermBase;
import eu.etaxonomy.cdm.model.term.TermBase;
+import eu.etaxonomy.cdm.model.term.TermNode;
+import eu.etaxonomy.cdm.model.term.TermTree;
+import eu.etaxonomy.cdm.model.term.TermVocabulary;
+import eu.etaxonomy.cdm.persistence.dto.AbstractTermDto;
import eu.etaxonomy.cdm.persistence.dto.ReferencingObjectDto;
import eu.etaxonomy.cdm.persistence.dto.TermDto;
+import eu.etaxonomy.cdm.persistence.dto.TermNodeDto;
+import eu.etaxonomy.cdm.persistence.dto.TermTreeDto;
+import eu.etaxonomy.cdm.persistence.dto.TermVocabularyDto;
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
import eu.etaxonomy.taxeditor.bulkeditor.referencingobjects.ReferencingObjectsContentProvider;
import eu.etaxonomy.taxeditor.bulkeditor.referencingobjects.ReferencingObjectsLabelProvider;
import eu.etaxonomy.taxeditor.view.e4.AbstractCdmEditorPartE4;
/**
+ * This view loads and presents referencing objects information asynchronously.
+ *
+ * Most of the task is done in UpdateRefObjectsJob.run()
+ *
* @author pplitzner
+ * @author k.luther
+ * @author a.mueller
* @since Aug 16, 2017
*/
public class ReferencingObjectsViewE4 extends AbstractCdmEditorPartE4 implements IReferencingObjectsView{
public static final String IS_LOADING = "Loading ...";
// public static final String CANCELED = "Canceled ...";
+ private static final List<ReferencingObjectDto> EMPTY_LIST = Arrays.asList(); //immutable empty list
+ private static final RefObjectDtoComparator COMPARATOR = new RefObjectDtoComparator();
+
private Label contentDescription;
- private String description;
-// private String referencedObjectTitleCache;
- private ConversationHolder conversation;
- private UUID actualUuid;
- private List<ReferencingObjectDto> referencingObjects = null;
- private Set<ReferencingObjectDto> referencingObjectsSet = null;
- private IProgressMonitor actualMonitor = null;
- private Job currentJob = null;
+ private volatile UUID actualUuid; //volatile as it used used between threads but only written in the synchronized method
+ private Job currentJob = null; //this variable should only be accessed in synchronized updateToNewItem
@PostConstruct
public void create(Composite parent, EMenuService menuService) {
- if (CdmStore.isActive()){
- if(conversation == null){
- conversation = CdmStore.createConversation();
- }
- }
- else{
+ if (!CdmStore.isActive()){
return;
}
parent.setLayout(new GridLayout());
//create context menu
menuService.registerContextMenu(viewer.getControl(), "eu.etaxonomy.taxeditor.bulkeditor.popupmenu.referencingobjectsview");
-
}
/**
viewer.setColumnProperties(titles);
}
- @Override
- public void updateReferencingObjects(final UUID entityUUID, final Class objectClass) {
- if (actualUuid == entityUUID){
- return ;
- }
- if(actualMonitor!=null && !actualMonitor.isCanceled()){
- while(!actualMonitor.isCanceled()){
- actualMonitor.setCanceled(true);
- }
- }
+ private class ItemDto {
+ String typeName;
+ String itemLabel;
+ UUID itemUuid;
+ Integer itemId;
+ Class<? extends CdmBase> itemClass;
- currentJob = new Job("Update Referencing Objects for " + entityUUID) {
-
- @Override
- protected IStatus run(final IProgressMonitor monitor) {
- monitor.beginTask("Calculating referencing objects", 100);
- actualUuid = entityUUID;
-
- monitor.worked(2);
- ReferencingObjectDto loadDto = new ReferencingObjectDto();
- loadDto.setTitleCache(IS_LOADING);
- referencingObjects = Arrays.asList(loadDto);
- updateView();
- if(monitor.isCanceled()) {
-// actualUuid = null;
- return Status.CANCEL_STATUS;
- }
- if (actualMonitor != null){
- actualMonitor.setCanceled(true);
- }
- actualMonitor = monitor;
- if (entityUUID != null){
- monitor.subTask("Load data from server");
- monitor.worked(1);
- Set<ReferencingObjectDto> refObjectsFromServer = loadReferencingObjects(entityUUID, objectClass);
- if (refObjectsFromServer == null){
- return Status.CANCEL_STATUS; //TODO is this correct?, null can happen e.g. if server call throws exception
- }
- monitor.worked(25);
- if(monitor.isCanceled()) {
-// actualUuid = null;
- return Status.CANCEL_STATUS;
- }
- referencingObjectsSet = refObjectsFromServer;
+ private String bestLabel(){
+ return (StringUtils.isNotBlank(this.typeName)? this.typeName + " " : "")
+ + "'"+(StringUtils.isNotBlank(this.itemLabel)? this.itemLabel : this.itemUuid.toString()) +"'"
+ + (this.itemId != null? " (id=" + this.itemId+")" : "");
+ }
+ }
- monitor.subTask("Show without description");
- monitor.worked(1);//maybe this helps to update the subtask label
+ private class UpdateRefObjectsJob extends Job{
- //TODO have a status line instead
- int count = referencingObjectsSet == null? 0: referencingObjectsSet.size();
- updateDescriptionLabel(CdmUtils.Nz(description) + "(n="+count+")"); //
+ final private ItemDto item;
+ public UpdateRefObjectsJob(String name, ItemDto item) {
+ super(name);
+ this.item = item;
+ if (item.itemUuid == null){
+ throw new RuntimeException("Item uuid must always exist at this point");
+ }
+ }
- List<ReferencingObjectDto> localDtos = sortToList(referencingObjectsSet, monitor);
- monitor.worked(10);
- if(monitor.isCanceled()) {
-// actualUuid = null;
- return Status.CANCEL_STATUS;
- }
- referencingObjects = localDtos;
- updateView();
-
- monitor.subTask("Initialize");
- monitor.worked(1);//maybe this helps to update the subtask label
- initializeDtos(localDtos, monitor, 50);
- if (monitor.isCanceled()){
-// actualUuid = null;
- //TODO use status line instead
-// referencingObjects = Arrays.asList(CANCELED);
- updateView();
-// monitor.done();
- return Status.CANCEL_STATUS;
- }
+ @Override
+ protected IStatus run(final IProgressMonitor monitor) {
- if(monitor.isCanceled()) {
- return Status.CANCEL_STATUS;
- }
- monitor.subTask("Update View");
+ monitor.beginTask("Calculating referencing objects for " + item.itemLabel, 100);
+
+ //set to loading
+ monitor.subTask("Load empty");
+ monitor.worked(1);
+ updateView("Loading " + item.bestLabel(), EMPTY_LIST, item.itemUuid);
+ if(monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
- updateView();
- monitor.worked(5);
- }
- monitor.done();
- actualMonitor = null;
+ //handle transient instance
+ if (item.itemId != null && item.itemId.equals(0)){
+ updateView("Not yet persisted: " + item.bestLabel(), EMPTY_LIST, item.itemUuid);
+ monitor.done();
+ return Status.OK_STATUS;
+ }
+ monitor.worked(1); //sum = 2
+
+ //load uninitialized DTOs from server
+ monitor.subTask("Load base data from server");
+ Set<ReferencingObjectDto> refObjectsFromServer = loadReferencingObjects(item.itemUuid, item.itemClass);
+ if (refObjectsFromServer == null){
+ updateView("An error occurred when loading " + item.bestLabel(), EMPTY_LIST, item.itemUuid);
+ return Status.CANCEL_STATUS; //TODO is this correct?, null can happen e.g. if server call throws exception
+ }
+ if(monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+ monitor.worked(10); //sum = 12
+
+ //show count
+ monitor.subTask("Show without description");
+ int count = refObjectsFromServer.size();
+ updateView("Loading " + count + " items for " + item.bestLabel(), EMPTY_LIST, item.itemUuid);
+ monitor.worked(1); //sum = 13
+
+ //sort
+ List<ReferencingObjectDto> localDtos = sortToList(refObjectsFromServer);
+ updateView("0/" + count + " items for " + item.bestLabel(), localDtos, item.itemUuid);
+ if(monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+ monitor.worked(2); //sum = 15
- return Status.OK_STATUS;
- }
+ //initialize
+ monitor.subTask("Initialize");
+ initializeDtos(localDtos, item, monitor, 83); //is calling updateView itself; sum = 95
+ if(monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+ monitor.worked(2); //sum = 100 (just in case)
+ monitor.done();
- };
- currentJob.setUser(true);
+ return Status.OK_STATUS;
+ }
+ }
+ private synchronized void updateToNewItem(final ItemDto item) {
+ if (currentJob != null){
+ currentJob.cancel();
+ }
+ Job newJob = new UpdateRefObjectsJob("Update Referencing Objects for " + item.bestLabel(), item);
+ newJob.setUser(true);
+ actualUuid = item.itemUuid;
+ currentJob = newJob;
currentJob.schedule();
}
- private List<ReferencingObjectDto> sortToList(Set<ReferencingObjectDto> referencingObjectsSet, IProgressMonitor monitor) {
+ private List<ReferencingObjectDto> sortToList(Set<ReferencingObjectDto> referencingObjectsSet) {
List<ReferencingObjectDto> result = new ArrayList<>(referencingObjectsSet);
- //TODO make singleton
- Collections.sort(result, new RefObjectDtoComparator());
+ Collections.sort(result, COMPARATOR);
return result;
}
- protected boolean initializeDtos(List<ReferencingObjectDto> localDtos, IProgressMonitor monitor, int work) {
+ protected boolean initializeDtos(List<ReferencingObjectDto> localDtos, ItemDto item, IProgressMonitor monitor, int work) {
+
IProgressMonitor subMonitor = AbstractUtility.getSubProgressMonitor(monitor, work);
subMonitor.beginTask("Initialize DTOs", localDtos.size());
-
int i = 100 - 20; //the first run should only include 20 records
+ int initCount = 0;
Set<ReferencingObjectDto> toInitialize = new HashSet<>();
for (ReferencingObjectDto dto : localDtos){
+ initCount++;
toInitialize.add(dto);
-
-// dto.setTitleCache(ReferencingObjectFormatter.format(dto.getReferencedEntity(), CdmStore.getDefaultLanguage() ));
- if (monitor.isCanceled()){
- return false;
- }
subMonitor.worked(1);
if (++i == 100){
- initBulk(toInitialize);
- updateView();
+ if (monitor.isCanceled()){
+ return false;
+ }
+ initBulk(toInitialize, initCount, localDtos, item);
+ //reset
toInitialize = new HashSet<>();
i = 0;
}
}
- initBulk(toInitialize);
+ //final bulk
+ if (monitor.isCanceled()){
+ return false;
+ }
+ initBulk(toInitialize, initCount, localDtos, item);
return true;
}
- private void initBulk(Set<ReferencingObjectDto> toInitialize) {
+ private void initBulk(Set<ReferencingObjectDto> toInitialize, int initCount,
+ List<ReferencingObjectDto> localDtos, ItemDto item) {
Set<ReferencingObjectDto> initialized = CdmStore.getCommonService().initializeReferencingObjectDtos(toInitialize, true, true, true, CdmStore.getDefaultLanguage());
Map<UUID,ReferencingObjectDto> map = new HashMap<>();
initialized.forEach(i->map.put(i.getUuid(), i));
- toInitialize.forEach(dto->merge(dto, map.get(dto.getUuid())));
- updateView();
-
+ toInitialize.forEach(dto->mergeInitializedData(dto, map.get(dto.getUuid())));
+ String initStr = initCount < localDtos.size()? initCount + "/" + localDtos.size() + " items": "Items";
+ updateView(initStr + " for " + item.bestLabel(), localDtos, item.itemUuid);
}
- private void merge(ReferencingObjectDto to, ReferencingObjectDto from) {
+ private void mergeInitializedData(ReferencingObjectDto to, ReferencingObjectDto from) {
to.setTitleCache(from.getTitleCache());
to.setOpenInTarget(from.getOpenInTarget());
to.setReferencedEntity(from.getReferencedEntity());
else if(CdmBase.class.isAssignableFrom(objectClass)){
referencedObject = CdmStore.getCommonService().find(objectClass, entity);
}
- //referencedObject =(CdmBase) CdmStore.getService(IIdentifiableEntityService.class).load(referencedObject.getUuid());
Set<ReferencingObjectDto> setOfReferencingObjects = null;
if (referencedObject != null){
-// if(referencedObject.isInstanceOf(IdentifiableEntity.class)){
-// referencedObjectTitleCache = (HibernateProxyHelper.deproxy(referencedObject, IdentifiableEntity.class)).getTitleCache();
-// }
-// else if(referencedObject.isInstanceOf(DescriptionElementBase.class)){
-// referencedObjectTitleCache = DescriptionHelper.getLabel(referencedObject);
-// }
-// else if (referencedObject.isInstanceOf(User.class)){
-// referencedObjectTitleCache = ((User)referencedObject).getUsername();
-// }else{
-// referencedObjectTitleCache = DescriptionHelper.getLabel(referencedObject);
-// }
setOfReferencingObjects = CdmStore.getCommonService().getReferencingObjectDtos(referencedObject);
return setOfReferencingObjects;
}
return null;
}
- class RefObjectDtoComparator implements Comparator<ReferencingObjectDto>{
+ /**
+ * Compares the referencing object by type and id. Using "description" for
+ * comparation has been given up to avoid initialization before comparison.
+ */
+ static class RefObjectDtoComparator implements Comparator<ReferencingObjectDto>{
@Override
public int compare(ReferencingObjectDto dto1, ReferencingObjectDto dto2) {
}
}
- private void updateView() {
- Display.getDefault().asyncExec(()->{
- if (viewer != null && !viewer.getControl().isDisposed()){
- try{
- viewer.setInput(referencingObjects);
- viewer.refresh();
-
- //enable/disable table
- viewer.getControl().setEnabled(referencingObjects!=null);
- }catch(Exception e){
- e.printStackTrace();
- logger.debug(e.getStackTrace());
- updateDescriptionLabel("The referencing objects view could not be updated completely. Some Problems occurred: " + e.getMessage());
- }
- }
- });
+ //not sure if it needs to be synchronized, only the local variable actualUuid is read
+ private void updateView(final String label,
+ final List<ReferencingObjectDto> referencingObjects, UUID itemUuid) {
+
+ if (this.actualUuid == itemUuid){
+ Display.getDefault().asyncExec(()->{
+ if (contentDescription != null && !contentDescription.isDisposed()){
+ contentDescription.setText(label);
+ }
+ if (viewer != null && !viewer.getControl().isDisposed()){
+ try{
+ viewer.setInput(referencingObjects);
+ viewer.refresh();
+
+ //enable/disable table
+ viewer.getControl().setEnabled(referencingObjects!=null);
+ }catch(Exception e){
+ e.printStackTrace();
+ logger.debug(e.getStackTrace());
+ updateDescriptionLabel("The referencing objects view could not be updated completely. Some Problems occurred: " + e.getMessage());
+ }
+ }
+ });
+ }
}
);
}
+ @Override
+ protected boolean showEmptyIfNoActiveEditor(){
+ return false;
+ }
+
@Override
public void selectionChanged_internal(Object selection, MPart activePart, MPart thisPart) {
if(activePart==thisPart){
IStructuredSelection structuredSelection = createSelection(selection);
if(structuredSelection!=null){
- //referencedObjectTitleCache = null;
showViewer(structuredSelection, activePart, viewer);
}
}
@Override
public void showViewer(IStructuredSelection selection, MPart activePart, Viewer viewer){
- // this.part = part;
-
- Object firstElement = selection.getFirstElement();
- if (firstElement instanceof TermDto){
- TermDto termDto = (TermDto) firstElement;
- description = "'"+termDto.getRepresentation_L10n() + "' is referenced by:";
- updateDescriptionLabel(description);
- updateReferencingObjects(termDto.getUuid(), TermBase.class );
- return;
- }
- if(firstElement instanceof TreeNode){
- firstElement = ((TreeNode) firstElement).getValue();
- }
- if (firstElement instanceof TaxonNode && !((TaxonNode)firstElement).hasTaxon()){
- firstElement = ((TaxonNode)firstElement).getClassification();
+ handleNewSelection(selection.getFirstElement());
+ }
+
+ //Note AM: this can probably be done better together with base class methods
+ // As I am not so familiar with this structure I only adapt it this way to be on the safe side
+ @Override
+ public void handleNewSelection(Object firstElement){
+ ItemDto dto = makeItemDto(firstElement);
+ if (dto.itemUuid == null || dto.itemClass == null || dto.itemUuid.equals(this.actualUuid)){
+ return;
}
- if(firstElement instanceof CdmBase){
- firstElement= CdmBase.deproxy(firstElement, CdmBase.class);
- CdmBase referencedCdmObject = (CdmBase)firstElement;
- if (referencedCdmObject.getUuid() == actualUuid){
- return;
- }
- String referencedObjectTitleCache = null;
- if(referencedCdmObject.isInstanceOf(IdentifiableEntity.class)){
- referencedObjectTitleCache = (HibernateProxyHelper.deproxy(referencedCdmObject, IdentifiableEntity.class)).getTitleCache();
+ updateToNewItem(dto);
+ }
+
+ /**
+ * Transforms the selection into uniform format (ItemDto).
+ * This method must be fast / should never require a server call
+ * as it takes place before the update Job is started and therefore is not
+ * asynchronous.
+ */
+ public ItemDto makeItemDto(Object firstElement) {
+
+ ItemDto dto = new ItemDto();
+ if(firstElement instanceof TreeNode){
+ firstElement = ((TreeNode) firstElement).getValue();
+ }
+ if (firstElement instanceof TaxonNode && !((TaxonNode)firstElement).hasTaxon()){
+ firstElement = ((TaxonNode)firstElement).getClassification();
+ }
+
+ if (firstElement instanceof AbstractTermDto){
+ AbstractTermDto termDto = (AbstractTermDto) firstElement;
+ dto.itemLabel = termDto.getTitleCache();
+ dto.itemUuid= termDto.getUuid();
+ dto.typeName = termDto.getTermType().getLabel();
+ dto.itemId = null; // id does not yet exist in TermDto
+ if (termDto instanceof TermDto){
+ dto.itemClass = TermBase.class;
+ }else if(termDto instanceof TermTreeDto){
+ dto.itemClass = TermTree.class;
+ dto.typeName = dto.typeName + " Tree";
+ }else if(termDto instanceof TermVocabularyDto){
+ dto.itemClass = TermVocabulary.class;
+ dto.typeName = dto.typeName + " Vocabulary";
+ }else{
+ //make it an unhandled selection
+ dto.itemUuid = null;
+ }
+ }else if (firstElement instanceof TermNodeDto){
+ TermNodeDto termNodeDto = (TermNodeDto) firstElement;
+ dto.itemLabel = (termNodeDto.getTerm() != null? termNodeDto.getTerm().getTitleCache() : termNodeDto.getTreeIndex());
+ dto.itemUuid= termNodeDto.getUuid();
+ dto.typeName = termNodeDto.getType().getLabel() + " Node";
+ dto.itemId = null; //does not yet exist in TermDto
+ dto.itemClass = TermNode.class;
+ }else if(firstElement instanceof CdmBase){
+ CdmBase cdmBase = CdmBase.deproxy(firstElement, CdmBase.class);
+ String label = null;
+ if(cdmBase instanceof IdentifiableEntity){
+ label = (HibernateProxyHelper.deproxy(cdmBase, IdentifiableEntity.class)).getTitleCache();
}
- else if(referencedCdmObject.isInstanceOf(DescriptionElementBase.class)){
- referencedObjectTitleCache = DescriptionHelper.getLabel(referencedCdmObject);
+ else if(cdmBase instanceof DescriptionElementBase){
+ label = DescriptionHelper.getLabel(cdmBase);
}
- else if (referencedCdmObject.isInstanceOf(User.class)){
- referencedObjectTitleCache = ((User)referencedCdmObject).getUsername();
- }else if (referencedCdmObject.isInstanceOf(TaxonNode.class)){
- referencedObjectTitleCache = "TaxonNode of "+(HibernateProxyHelper.deproxy(referencedCdmObject, TaxonNode.class)).getTaxon().getTitleCache();
+ else if (cdmBase instanceof User){
+ label = ((User)cdmBase).getUsername();
+ }else if (cdmBase instanceof TaxonNode){
+ label = ((TaxonNode)cdmBase).getTaxon().getTitleCache();
}
- if (CdmUtils.isBlank(referencedObjectTitleCache)){
- referencedObjectTitleCache = "#"+referencedCdmObject.getId();
+ if (CdmUtils.isBlank(label)){
+ label = "#"+cdmBase.getId();
}
- description = referencedCdmObject.getUserFriendlyTypeName() + " '" + referencedObjectTitleCache + "' is referenced by:";
- updateDescriptionLabel(description);
-
- updateReferencingObjects(referencedCdmObject.getUuid(),firstElement.getClass() );
-
+ dto.typeName = cdmBase.getUserFriendlyTypeName();
+ dto.itemLabel = label;
+ dto.itemUuid= cdmBase.getUuid();
+ dto.itemClass = cdmBase.getClass();
+ dto.itemId = cdmBase.getId();
}else if (firstElement instanceof UuidAndTitleCache<?>){
- UuidAndTitleCache<?> element = (UuidAndTitleCache<?>) firstElement;
- description = element.getType().getSimpleName() + " '" + element.getTitleCache() + "' is referenced by:";
- updateDescriptionLabel(description);
- updateReferencingObjects(element.getUuid(),element.getType());
- }
- else if (firstElement != null){
- updateView();
- description = "undefined:";
- updateDescriptionLabel(description);
+ UuidAndTitleCache<? extends CdmBase> element = (UuidAndTitleCache<? extends CdmBase>) firstElement;
+ dto.typeName = CdmUtils.userFriendlyClassName(element.getType());
+ dto.itemLabel = element.getTitleCache();
+ if (CdmUtils.isBlank(dto.itemLabel)){
+ dto.itemLabel = "id=" + element.getId();
+ }
+ dto.itemUuid= element.getUuid();
+ dto.itemClass = element.getType();
+ dto.itemId = element.getId();
+ }else if (firstElement instanceof String){
+ dto.typeName = "String";
+ dto.itemLabel = firstElement.toString();
+ dto.itemUuid= null;
+ dto.itemClass = null;
+ dto.itemId = null;
+ }else if (firstElement != null){
+ dto.typeName = CdmUtils.userFriendlyClassName(firstElement.getClass());
+ dto.itemLabel = firstElement.toString();
+ dto.itemUuid= null;
+ dto.itemClass = null;
+ dto.itemId = null;
+ }else{
+ dto.typeName = null;
+ dto.itemLabel = "no selection";
+ dto.itemUuid= null;
+ dto.itemClass = null;
+ dto.itemId = null;
}
- }
+ return dto;
+ }
@PreDestroy
public void dispose() {
- if(conversation!=null){
- conversation.close();
- conversation = null;
- }
+ //no conversation in this view
}
@Override