From be97fa41de5eaa4d5d0b2e11c177d7d239e46568 Mon Sep 17 00:00:00 2001 From: "n.hoffmann" Date: Thu, 3 Sep 2009 07:49:49 +0000 Subject: [PATCH] working on duplication detection/arbitration --- .../taxeditor/editor/DuplicateArbitrator.java | 260 +++++++++++++----- .../taxeditor/editor/GroupedComposite.java | 27 -- .../editor/MultiPageTaxonEditor.java | 13 +- ...ltiPageTaxonEditorDataChangeBehaviour.java | 9 +- .../taxeditor/editor/TaxonEditorInput.java | 4 +- 5 files changed, 203 insertions(+), 110 deletions(-) diff --git a/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/DuplicateArbitrator.java b/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/DuplicateArbitrator.java index 02a5f399d..830b3931a 100644 --- a/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/DuplicateArbitrator.java +++ b/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/DuplicateArbitrator.java @@ -10,21 +10,22 @@ package eu.etaxonomy.taxeditor.editor; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import eu.etaxonomy.cdm.api.service.ICommonService; import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper; +import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase; import eu.etaxonomy.cdm.model.name.NonViralName; import eu.etaxonomy.cdm.model.name.TaxonNameBase; import eu.etaxonomy.cdm.model.reference.ReferenceBase; -import eu.etaxonomy.cdm.model.reference.StrictReferenceBase; import eu.etaxonomy.cdm.model.taxon.TaxonBase; +import eu.etaxonomy.cdm.strategy.match.DefaultMatchStrategy; +import eu.etaxonomy.cdm.strategy.match.IMatchStrategy; import eu.etaxonomy.cdm.strategy.match.MatchException; +import eu.etaxonomy.cdm.strategy.match.MatchMode; import eu.etaxonomy.taxeditor.editor.name.NameComposite; import eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor; import eu.etaxonomy.taxeditor.store.CdmStore; @@ -48,14 +49,20 @@ public class DuplicateArbitrator { */ private MultiPageTaxonEditor editor; private TaxonNameEditor nameEditor; - private Set> dirtyNames; + private Set> dirtyNameComposites; - private Map duplicateReferences; + private List duplicateReferences; - private Map duplicateNames; + private List duplicateNames; + private List duplicateCombinationAuthorTeams; + private List duplicateExCombinationAuthorTeams; + private List duplicateBasionymAuthorTeams; + private List duplicateExBasionymAuthorTeams; + private ICommonService commonService; + /** * @param adaptableObject */ @@ -66,8 +73,12 @@ public class DuplicateArbitrator { nameEditor = (TaxonNameEditor) editor.getPage(Page.NAME); - duplicateReferences = new HashMap(); - duplicateNames = new HashMap(); + duplicateReferences = new ArrayList(); + duplicateNames = new ArrayList(); + duplicateCombinationAuthorTeams = new ArrayList(); + duplicateExCombinationAuthorTeams = new ArrayList(); + duplicateBasionymAuthorTeams = new ArrayList(); + duplicateExBasionymAuthorTeams = new ArrayList(); } /** @@ -77,107 +88,214 @@ public class DuplicateArbitrator { // iterate over all names that were edited for(NameComposite composite : nameEditor.getDirtyNames()){ logger.warn("Found " + composite + " with possible duplicates"); + + NonViralName nonViralName = (NonViralName) composite.getParsedName(); - // since we are dealing with NameComposites getData should always return a TaxonBase - // also we do not want to handle viral names at the moment -// NonViralName name = (NonViralName) ((TaxonBase) composite.getData()).getName(); - - TaxonBase taxonBase = HibernateProxyHelper.deproxy(composite.getData(), TaxonBase.class); - - // TODO decide what entities are candidates for duplicate detection - checkDuplicateLatinNames(taxonBase); - checkDuplicateAuthors(taxonBase); - checkDuplicateNomenclaturalReference(taxonBase); + + duplicateNames = findMatchingLatinNames(nonViralName); + duplicateCombinationAuthorTeams = findMatchingAuthors((TeamOrPersonBase) nonViralName.getCombinationAuthorTeam()); + duplicateExCombinationAuthorTeams = findMatchingAuthors((TeamOrPersonBase) nonViralName.getExCombinationAuthorTeam()); + duplicateBasionymAuthorTeams = findMatchingAuthors((TeamOrPersonBase) nonViralName.getBasionymAuthorTeam()); + duplicateExBasionymAuthorTeams = findMatchingAuthors((TeamOrPersonBase) nonViralName.getExBasionymAuthorTeam()); + duplicateReferences = findMatchingNomenclaturalReference(nonViralName.getNomenclaturalReference()); + saveNameElements(composite); + emptyDuplicateSets(); } - - solveDuplicates(); - + } + + /** + * + */ + private void emptyDuplicateSets() { + duplicateReferences.clear(); + duplicateCombinationAuthorTeams.clear(); + duplicateExCombinationAuthorTeams.clear(); + duplicateBasionymAuthorTeams.clear(); + duplicateExBasionymAuthorTeams.clear(); + duplicateNames.clear(); } /** * Provides strategies to solve found duplicates * This can either happen automatically or through user input */ - private void solveDuplicates() { - /* - * TODO first idea that comes to mind would be to present the user - * a dialog with all possible duplicates and let her mark which entities - * she wants to use - * - * If we have sophisticated equals methods that guarantee full equality and - * there is only one match of an entity we might want to resolve the duplication - * automatically. + private void saveNameElements(NameComposite composite) { + + resolveDuplicateNames(composite); + + resolveAllDuplicateAuthors(composite); + + resolveDuplicateReferences(composite); + + // FIXME this is a workaround, because title cache does not get generated correctly in library + // remove once #964 is closed + composite.getTaxon().setTitleCache((composite.getTaxon().generateTitle())); + } + + /** + * @param composite + */ + private void resolveDuplicateNames(NameComposite composite) { + + /* When creating a new taxon editor, a taxon with an empty name is created. + * We have to delete this empty name explicitly from the session, otherwise it + * will be persisted into the database */ + if(composite.getName().getFullTitleCache().length() == 0){ + editor.getConversationHolder().delete(composite.getName()); + } + + if(duplicateNames.size() == 0){ + // No matches were found for the name part. We can safely + // replace the taxons name with the parsed name + + composite.setName(composite.getParsedName()); + }else if (duplicateNames.size() == 1){ + composite.setName(duplicateNames.iterator().next()); + }else{ + + // do stuff + } // debug - for(ReferenceBase reference : duplicateReferences.values()){ - logger.warn(reference); + for(TaxonNameBase name : duplicateNames){ + logger.warn(name.getFullTitleCache()); } - for(TaxonNameBase name : duplicateNames.values()){ - logger.warn(name); + + } + + /** + * @param composite + */ + private void resolveDuplicateReferences(NameComposite composite) { + if(duplicateReferences.size() == 0){ + // nomatches found for the reference we replace the reference with the parsed one + composite.getName().setNomenclaturalReference(composite.getParsedName().getNomenclaturalReference()); + }else if(duplicateReferences.size() == 1){ + // exactly one match. We assume that the user wants this reference + composite.getName().setNomenclaturalReference(duplicateReferences.iterator().next()); + }else{ + // do stuff } + // debug + for(ReferenceBase reference : duplicateReferences){ + logger.info(reference.getTitleCache()); + } } + + /** - * @param name + * @param composite */ - private void checkDuplicateNomenclaturalReference(TaxonBase taxonBase) { + private void resolveAllDuplicateAuthors(NameComposite composite) { + NonViralName name = HibernateProxyHelper.deproxy(composite.getName(), NonViralName.class); + NonViralName parsedName = HibernateProxyHelper.deproxy(composite.getParsedName(), NonViralName.class); - NonViralName name = getName(taxonBase); - try{ - StrictReferenceBase referenceBase = (StrictReferenceBase) name.getNomenclaturalReference(); - - List matches = commonService.findMatching(referenceBase, null); - -// // query datasource for the reference -// List result = CdmStore.getReferenceService().getReferencesByTitle(referenceBase.getTitleCache()); - - // if query delivers results, place possible duplicate in map - for(ReferenceBase duplicateReference : matches){ - duplicateReferences.put(taxonBase, duplicateReference); - } - }catch(NullPointerException e){ - logger.warn("Name has no nomenclatural reference"); - } catch (MatchException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + if(duplicateCombinationAuthorTeams.size() == 0){ + name.setCombinationAuthorTeam(parsedName.getCombinationAuthorTeam()); + }else if(duplicateCombinationAuthorTeams.size() == 1){ + name.setCombinationAuthorTeam(duplicateCombinationAuthorTeams.iterator().next()); + }else{ + // do stuff + } + + if(duplicateExCombinationAuthorTeams.size() == 0){ + name.setExCombinationAuthorTeam(parsedName.getExCombinationAuthorTeam()); + }else if(duplicateExCombinationAuthorTeams.size() == 1){ + name.setExCombinationAuthorTeam(duplicateExCombinationAuthorTeams.iterator().next()); + }else{ + // do stuff + } + + if(duplicateBasionymAuthorTeams.size() == 0){ + name.setBasionymAuthorTeam(parsedName.getBasionymAuthorTeam()); + }else if(duplicateBasionymAuthorTeams.size() == 1){ + name.setBasionymAuthorTeam(duplicateBasionymAuthorTeams.iterator().next()); + }else{ + // do stuff + } + + if(duplicateExBasionymAuthorTeams.size() == 0){ + name.setExBasionymAuthorTeam(parsedName.getExBasionymAuthorTeam()); + }else if(duplicateExBasionymAuthorTeams.size() == 1){ + name.setExBasionymAuthorTeam(duplicateExBasionymAuthorTeams.iterator().next()); + }else{ + // do stuff } + + // debug + for(TeamOrPersonBase authorTeam : duplicateCombinationAuthorTeams){ + logger.info(authorTeam.getTitleCache()); + } + // debug + for(TeamOrPersonBase authorTeam : duplicateExCombinationAuthorTeams){ + logger.info(authorTeam.getTitleCache()); + } + // debug + for(TeamOrPersonBase authorTeam : duplicateBasionymAuthorTeams){ + logger.info(authorTeam.getTitleCache()); + } + // debug + for(TeamOrPersonBase authorTeam : duplicateExBasionymAuthorTeams){ + logger.info(authorTeam.getTitleCache()); + } } + /** * @param name */ - private void checkDuplicateAuthors(TaxonBase taxonBase) { - // see checkDuplicateNomenclaturalReference for implementation + private List findMatchingNomenclaturalReference(ReferenceBase referenceBase) { + if(referenceBase == null) return new ArrayList(); + try{ + return commonService.findMatching(referenceBase, null); + }catch (MatchException e) { + logger.error("Error finding matching references", e); + } + return null; } /** * @param name */ - private void checkDuplicateLatinNames(TaxonBase taxonBase) { - NonViralName name = getName(taxonBase); - - List result = new ArrayList(); //commonService.findMatching(name, null); + private List findMatchingAuthors(TeamOrPersonBase authorTeam) { + + if(authorTeam == null){ + return new ArrayList(); + } - for(Object object: result ){ - TaxonNameBase duplicateName = HibernateProxyHelper.deproxy(object, TaxonNameBase.class); - duplicateNames.put(taxonBase, duplicateName); + try{ + return commonService.findMatching(authorTeam, null); + }catch (MatchException e) { + logger.error("Error finding matching authors", e); } + return null; } - + /** - * Helper method to get the NonViralName of a taxon base. - * - * @param taxonBase - * @return + * @param name */ - private NonViralName getName(TaxonBase taxonBase){ - return HibernateProxyHelper.deproxy(taxonBase.getName(), NonViralName.class); - } + private List findMatchingLatinNames(TaxonNameBase taxonNameBase) { + try { + IMatchStrategy strategy = DefaultMatchStrategy.NewInstance(NonViralName.class); + strategy.setMatchMode("nomenclaturalReference", MatchMode.IGNORE); + strategy.setMatchMode("combinationAuthorTeam", MatchMode.IGNORE); + strategy.setMatchMode("exCombinationAuthorTeam", MatchMode.IGNORE); + strategy.setMatchMode("basionymAuthorTeam", MatchMode.IGNORE); + strategy.setMatchMode("exBasionymAuthorTeam", MatchMode.IGNORE); + + return commonService.findMatching(taxonNameBase, strategy); + + } catch (MatchException e) { + logger.error("Error finding matching names", e); + } + return null; + } } diff --git a/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/GroupedComposite.java b/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/GroupedComposite.java index 7874b41b4..27aeb7295 100644 --- a/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/GroupedComposite.java +++ b/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/GroupedComposite.java @@ -467,33 +467,6 @@ abstract public class GroupedComposite extends Composite implements IHasProperty nonEditableInfoHover.setText(nonEditableHoverText); } - /** - * If the user entering text requires parsing, call this method and override - * the method parse(). - */ - protected void createParser() { - if (textViewer != null) { - parseListener = new ParseListener() { - @Override - public void parse(String text) { - GroupedComposite.this.parse(text); - } - }; - textViewer.getTextWidget().addModifyListener(parseListener); - - } else { - logger.warn("Can't create parser because textViewer has not been initalized."); - } - } - - /** - * Implement in derived classes - * - * @param text - */ - protected abstract void parse(String text); - - /** * If the user hitting carriage return should cause something to happen - * i.e. the creation of a new composite - call this method and override diff --git a/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/MultiPageTaxonEditor.java b/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/MultiPageTaxonEditor.java index b1119125c..09543016b 100644 --- a/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/MultiPageTaxonEditor.java +++ b/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/MultiPageTaxonEditor.java @@ -119,19 +119,18 @@ public class MultiPageTaxonEditor extends FormEditor implements IConversationEna @Override public void doSave(IProgressMonitor monitor) { + if( ! conversation.isBound()){ + conversation.bind(); + } + //handle existing names and authors DuplicateArbitrator duplicateArbitrator = (DuplicateArbitrator) getAdapter(DuplicateArbitrator.class); if(duplicateArbitrator != null){ // disable for now because of transaction problems. -// duplicateArbitrator.arbitrate(); + duplicateArbitrator.arbitrate(); } - - - if( ! conversation.isBound()){ - conversation.bind(); - } - + // commit the conversation and start a new transaction immediately conversation.commit(true); diff --git a/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/MultiPageTaxonEditorDataChangeBehaviour.java b/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/MultiPageTaxonEditorDataChangeBehaviour.java index fb42f6a3d..866d3cb51 100644 --- a/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/MultiPageTaxonEditorDataChangeBehaviour.java +++ b/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/MultiPageTaxonEditorDataChangeBehaviour.java @@ -14,7 +14,7 @@ import java.util.Vector; import org.apache.log4j.Logger; import eu.etaxonomy.cdm.model.name.TaxonNameBase; -import eu.etaxonomy.cdm.model.taxon.Taxon; +import eu.etaxonomy.cdm.model.taxon.TaxonNode; import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeEvent; import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap; import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeEvent.EventType; @@ -67,8 +67,11 @@ public class MultiPageTaxonEditorDataChangeBehaviour extends AbstractDataChangeB public void reactOnDelete(Vector events){ for(CdmDataChangeEvent event : events){ // close open editors - if((event.getEntity() instanceof Taxon) - && input.getTaxon().equals(event.getEntity())){ + if(event.getEntity() instanceof TaxonNode){ + logger.warn("taxon node"); + } + if((event.getEntity() instanceof TaxonNode) + && input.getTaxonNode().equals(event.getEntity())){ EditorUtil.close(source); logger.debug("Closing open editor for deleted taxon."); } diff --git a/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/TaxonEditorInput.java b/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/TaxonEditorInput.java index 56012b8ab..5785c04f8 100644 --- a/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/TaxonEditorInput.java +++ b/taxeditor-editor/src/main/java/eu/etaxonomy/taxeditor/editor/TaxonEditorInput.java @@ -106,8 +106,8 @@ public class TaxonEditorInput implements IEditorInput, IConversationEnabled, IPe // add new taxon as root node TaxonomicTree tree = CdmStore.getTaxonService().getTaxonomicTreeByUuid(parentNodeUuid); - newTaxon = Taxon.NewInstance(name, CdmStore.getDefault().getDefaultSec()); - newTaxonNode = tree.addRoot(newTaxon, null); + newTaxon = Taxon.NewInstance(name, tree.getReference()); + newTaxonNode = tree.addRoot(newTaxon, null, null); }else{ // add new taxon as child of another taxon newTaxon = Taxon.NewInstance(name, parentTaxonNode.getTaxon().getSec()); -- 2.34.1