From: Andreas Kohlbecker Date: Wed, 29 Jun 2016 13:56:15 +0000 (+0200) Subject: #4366 Transmissionengine aggregation of source references X-Git-Tag: 4.2.0^2~39 X-Git-Url: https://dev.e-taxonomy.eu/gitweb/cdmlib.git/commitdiff_plain/f5295f3f9a9eecfa5ab1d0c9e82ab8ce95332897 #4366 Transmissionengine aggregation of source references --- diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/OriginalSourceBase.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/OriginalSourceBase.java index 68ea146045..27921149bc 100644 --- a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/OriginalSourceBase.java +++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/OriginalSourceBase.java @@ -150,4 +150,24 @@ public abstract class OriginalSourceBase extends Referenc } } +//*********************************** EQUALS *********************************************************/ + + /** + * {@inheritDoc} + */ + @Override + public boolean equalsByShallowCompare(ReferencedEntityBase other) { + + if(!super.equalsByShallowCompare(other)) { + return false; + } + OriginalSourceBase theOther = (OriginalSourceBase)other; + if(!StringUtils.equals(this.getIdInSource(), theOther.getIdInSource()) + || !StringUtils.equals(this.getIdNamespace(), theOther.getIdNamespace())) { + return false; + } + + return true; + } + } \ No newline at end of file diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/ReferencedEntityBase.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/ReferencedEntityBase.java index e08b2905c8..11b9a74c5e 100644 --- a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/ReferencedEntityBase.java +++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/ReferencedEntityBase.java @@ -21,6 +21,7 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSchemaType; import javax.xml.bind.annotation.XmlType; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.hibernate.annotations.Cascade; import org.hibernate.annotations.CascadeType; @@ -118,4 +119,28 @@ public abstract class ReferencedEntityBase extends AnnotatableEntity implements return result; } +//*********************************** EQUALS *********************************************************/ + + /** + * Indicates whether some other object is "equal to" this one. + * + * Uses a content based compare strategy which avoids bean initialization. This is achieved by + * comparing the cdm entity ids. + * + * @param other + * @return + */ + public boolean equalsByShallowCompare(ReferencedEntityBase other) { + + if(this.getCitation().getId() != other.getCitation().getId() + || !StringUtils.equals(this.getCitationMicroReference(), other.getCitationMicroReference()) + || !StringUtils.equals(this.getOriginalNameString(), other.getOriginalNameString()) + ){ + return false; + } + + return true; + } + + } \ No newline at end of file diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/description/DescriptionElementSource.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/description/DescriptionElementSource.java index 4bbba68bf5..a201db9f1c 100644 --- a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/description/DescriptionElementSource.java +++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/description/DescriptionElementSource.java @@ -25,6 +25,7 @@ import org.hibernate.envers.Audited; import eu.etaxonomy.cdm.model.common.OriginalSourceBase; import eu.etaxonomy.cdm.model.common.OriginalSourceType; +import eu.etaxonomy.cdm.model.common.ReferencedEntityBase; import eu.etaxonomy.cdm.model.name.TaxonNameBase; import eu.etaxonomy.cdm.model.reference.Reference; @@ -163,6 +164,29 @@ public class DescriptionElementSource extends OriginalSourceBaseb is preferred over a these Set of sources will be added to the sources of b * @return */ - private StatusAndSources choosePreferred(StatusAndSources a, StatusAndSources b){ + private StatusAndSources choosePreferred(StatusAndSources a, StatusAndSources b, Set sourcesForWinnerB){ if (statusPriorityMap == null) { initializeStatusPriorityMap(); @@ -268,9 +269,12 @@ public class TransmissionEngineDistribution { //TODO extends IoBase? return a; } if(statusPriorityMap.get(a.status) < statusPriorityMap.get(b.status)){ + if(sourcesForWinnerB != null) { + b.addSources(sourcesForWinnerB); + } return b; } else if (statusPriorityMap.get(a.status) == statusPriorityMap.get(b.status)){ - a.sources.addAll(b.sources); + a.addSources(b.sources); return a; } else { return a; @@ -335,7 +339,7 @@ public class TransmissionEngineDistribution { //TODO extends IoBase? // only for debugging: - logger.setLevel(Level.DEBUG); // TRACE will slow down a lot since it forces loading all term representations + //logger.setLevel(Level.TRACE); // TRACE will slow down a lot since it forces loading all term representations //Logger.getLogger("org.hibernate.SQL").setLevel(Level.DEBUG); logger.info("Hibernate JDBC Batch size: " @@ -481,7 +485,7 @@ public class TransmissionEngineDistribution { //TODO extends IoBase? continue; } StatusAndSources subStatusAndSources = new StatusAndSources(status, distribution.getSources()); - accumulatedStatusAndSources = choosePreferred(accumulatedStatusAndSources, subStatusAndSources); + accumulatedStatusAndSources = choosePreferred(accumulatedStatusAndSources, subStatusAndSources, null); } } } // next sub area @@ -491,6 +495,7 @@ public class TransmissionEngineDistribution { //TODO extends IoBase? } // store new distribution element for superArea in taxon description Distribution newDistribitionElement = Distribution.NewInstance(superArea, accumulatedStatusAndSources.status); + newDistribitionElement.getSources().addAll(accumulatedStatusAndSources.sources); newDistribitionElement.addMarker(Marker.NewInstance(MarkerType.COMPUTED(), true)); description.addElement(newDistribitionElement); } @@ -520,7 +525,7 @@ public class TransmissionEngineDistribution { //TODO extends IoBase? } /** - * Step 2: Accumulate by ranks staring from lower rank to upper rank, the status of all children + * Step 2: Accumulate by ranks starting from lower rank to upper rank, the status of all children * are accumulated on each rank starting from lower rank to upper rank. *
    *
  • aggregate distribution of included taxa of the next lower rank for any rank level starting from the lower rank (e.g. sub species) @@ -566,7 +571,7 @@ public class TransmissionEngineDistribution { //TODO extends IoBase? while (taxonIdIterator.hasNext()) { if(txStatus == null) { - // transaction has been comitted at the end of this batch, start a new one + // transaction has been committed at the end of this batch, start a new one txStatus = startTransaction(false); } @@ -624,17 +629,22 @@ public class TransmissionEngineDistribution { //TODO extends IoBase? } StatusAndSources subStatusAndSources = new StatusAndSources(status, distribution.getSources()); - accumulatedStatusMap.put(area, choosePreferred(accumulatedStatusMap.get(area), subStatusAndSources)); + accumulatedStatusMap.put(area, choosePreferred(accumulatedStatusMap.get(area), subStatusAndSources, null)); } } if(accumulatedStatusMap.size() > 0) { TaxonDescription description = findComputedDescription(taxon, doClearDescriptions); for (NamedArea area : accumulatedStatusMap.keySet()) { - // store new distribution element in new Description - Distribution newDistribitionElement = Distribution.NewInstance(area, accumulatedStatusMap.get(area).status); - newDistribitionElement.addMarker(Marker.NewInstance(MarkerType.COMPUTED(), true)); - description.addElement(newDistribitionElement); + Distribution distribition = findDistribution(description, area, accumulatedStatusMap.get(area).status); + if(distribition == null) { + // create a new distribution element + distribition = Distribution.NewInstance(area, accumulatedStatusMap.get(area).status); + distribition.addMarker(Marker.NewInstance(MarkerType.COMPUTED(), true)); + } + addSourcesDeduplicated(distribition.getSources(), accumulatedStatusMap.get(area).sources); + + description.addElement(distribition); } taxonService.saveOrUpdate(taxon); descriptionService.saveOrUpdate(description); @@ -670,6 +680,25 @@ public class TransmissionEngineDistribution { //TODO extends IoBase? subMonitor.done(); } +/** + * @param description + * @param area + * @param status + * @return + */ +private Distribution findDistribution(TaxonDescription description, NamedArea area, PresenceAbsenceTerm status) { + for(DescriptionElementBase item : description.getElements()) { + if(!(item instanceof Distribution)) { + continue; + } + Distribution distribution = ((Distribution)item); + if(distribution.getArea().equals(area) && distribution.getStatus().equals(status)) { + return distribution; + } + } + return null; +} + /** * @param lowerRank * @param upperRank @@ -927,6 +956,26 @@ private List rankInterval(Rank lowerRank, Rank upperRank) { commitTransaction(txStatus); } + public static void addSourcesDeduplicated(Set target, Set sources) { + for(DescriptionElementSource source : sources) { + boolean contained = false; + for(DescriptionElementSource existingSource: target) { + if(existingSource.equalsByShallowCompare(source)) { + contained = true; + break; + } + } + if(!contained) { + try { + target.add((DescriptionElementSource)source.clone()); + } catch (CloneNotSupportedException e) { + // should never happen + throw new RuntimeException(e); + } + } + } + } + public enum AggregationMode { byAreas, byRanks, @@ -936,13 +985,21 @@ private List rankInterval(Rank lowerRank, Rank upperRank) { private class StatusAndSources { - PresenceAbsenceTerm status; + private final PresenceAbsenceTerm status; - Set sources = new HashSet<>(); + private final Set sources = new HashSet<>(); public StatusAndSources(PresenceAbsenceTerm status, Set sources) { this.status = status; - this.sources = sources; + addSourcesDeduplicated(this.sources, sources); } + + /** + * @param sources + */ + public void addSources(Set sources) { + addSourcesDeduplicated(this.sources, sources); + } + } } diff --git a/cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/TransmissionEngineDistributionTest.java b/cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/TransmissionEngineDistributionTest.java index 27df05abd3..c3c341938c 100644 --- a/cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/TransmissionEngineDistributionTest.java +++ b/cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/TransmissionEngineDistributionTest.java @@ -12,9 +12,13 @@ package eu.etaxonomy.cdm.api.service; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.io.FileNotFoundException; import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.UUID; @@ -53,7 +57,6 @@ import eu.etaxonomy.cdm.test.unitils.CleanSweepInsertLoadStrategy; */ public class TransmissionEngineDistributionTest extends CdmTransactionalIntegrationTest { - @SuppressWarnings("unused") private static Logger logger = Logger.getLogger(TransmissionEngineDistributionTest.class); private static final UUID T_LAPSANA_UUID = UUID.fromString("f65d47bd-4f49-4ab1-bc4a-bc4551eaa1a8"); @@ -105,6 +108,9 @@ public class TransmissionEngineDistributionTest extends CdmTransactionalIntegrat private Classification classification; + private Reference book_a = null; + private Reference book_b = null; + @Before public void setUp() { @@ -124,6 +130,11 @@ public class TransmissionEngineDistributionTest extends CdmTransactionalIntegrat yug_ma = termService.getAreaByTdwgAbbreviation("YUG-MA"); yug_mn = termService.getAreaByTdwgAbbreviation("YUG-MN"); + book_a = ReferenceFactory.newBook(); + book_a.setTitle("book_a"); + book_b = ReferenceFactory.newBook(); + book_b.setTitle("book_a"); + engine.updatePriorities(); } @@ -146,7 +157,7 @@ public class TransmissionEngineDistributionTest extends CdmTransactionalIntegrat addDistributions( T_LAPSANA_COMMUNIS_ALPINA_UUID, - new Distribution[] { + Arrays.asList(new Distribution[] { // should succeed during area aggregation be ignored by rank aggregation // => yug will get status ENDEMIC_FOR_THE_RELEVANT_AREA // but only for LAPSANA_COMMUNIS_ALPINA @@ -154,7 +165,7 @@ public class TransmissionEngineDistributionTest extends CdmTransactionalIntegrat // should be ignored by area aggregation // => LAPSANA_COMMUNIS will wave distribution with yug_ko and INTRODUCED_FORMERLY_INTRODUCED Distribution.NewInstance(yug_ko, PresenceAbsenceTerm.INTRODUCED_FORMERLY_INTRODUCED()), - } + }) ); engine.accumulate(AggregationMode.byAreasAndRanks, superAreas, lowerRank, upperRank, null, null); @@ -186,31 +197,16 @@ public class TransmissionEngineDistributionTest extends CdmTransactionalIntegrat }) public void testArea_area() { - Distribution[] distributions = new Distribution[4]; - - Reference book_LCA_yug_mn = ReferenceFactory.newBook(); - book_LCA_yug_mn.setTitle("LCA_yug_mn"); - DescriptionElementSource.NewPrimarySourceInstance(book_LCA_yug_mn, "1"); - distributions[0] = Distribution.NewInstance(yug_mn, PresenceAbsenceTerm.CULTIVATED()); + Set distributions_LCA = new HashSet<>(); - Reference book_LCA_yug_ko = ReferenceFactory.newBook(); - book_LCA_yug_mn.setTitle("LCA_yug_ko"); - DescriptionElementSource.NewPrimarySourceInstance(book_LCA_yug_ko, "2"); - distributions[1] = Distribution.NewInstance(yug_ko, PresenceAbsenceTerm.NATIVE()); // NATIVE should succeed - - Reference book_LCA_yug_bh = ReferenceFactory.newBook(); - book_LCA_yug_mn.setTitle("LCA_yug_bh"); - DescriptionElementSource.NewPrimarySourceInstance(book_LCA_yug_bh, "3"); - distributions[2] = Distribution.NewInstance(yug_bh, PresenceAbsenceTerm.INTRODUCED()); - - Reference book_LCA_yug_ma = ReferenceFactory.newBook(); - book_LCA_yug_mn.setTitle("LCA_yug_ma"); - DescriptionElementSource.NewPrimarySourceInstance(book_LCA_yug_ma, "4"); - distributions[3] = Distribution.NewInstance(yug_ma, PresenceAbsenceTerm.NATIVE()); // NATIVE should succeed + distributions_LCA.add(newDistribution(book_a, yug_mn, PresenceAbsenceTerm.CULTIVATED(), "1")); + distributions_LCA.add(newDistribution(book_a, yug_ko, PresenceAbsenceTerm.NATIVE(), "2")); // NATIVE should succeed + distributions_LCA.add(newDistribution(book_a, yug_bh, PresenceAbsenceTerm.INTRODUCED(), "3")); + distributions_LCA.add(newDistribution(book_a, yug_ma, PresenceAbsenceTerm.NATIVE(), "4")); // NATIVE should succeed addDistributions( T_LAPSANA_COMMUNIS_ALPINA_UUID, - distributions + distributions_LCA ); Taxon lapsana_communis_alpina = (Taxon) taxonService.load(T_LAPSANA_COMMUNIS_ALPINA_UUID); @@ -225,45 +221,53 @@ public class TransmissionEngineDistributionTest extends CdmTransactionalIntegrat for (TaxonDescription description : lapsana_communis_alpina.getDescriptions()) { if(description.hasMarker(MarkerType.COMPUTED(), true)) { assertNull("only one computed Distribution should exists", accumulatedDistribution); - assertEquals("the computed Decription should have only one element", 1, description.getElements().size()); + assertEquals("the computed Decsription should have only one element", 1, description.getElements().size()); accumulatedDistribution = (Distribution) description.getElements().iterator().next(); assertEquals("Expecting area to be YUG", yug, accumulatedDistribution.getArea()); assertEquals("Expecting status to be NATIVE", PresenceAbsenceTerm.NATIVE().getLabel(), accumulatedDistribution.getStatus().getLabel()); } } assertNotNull("The area YUG should have been found", accumulatedDistribution); -// assertEquals("Expecting two source references", accumulatedDistribution.getSources().size()); -// assertTrue(accumulatedDistribution.getSources().contains(book_LCA_yug_ko)); -// assertTrue(accumulatedDistribution.getSources().contains(book_LCA_yug_ma)); + assertEquals("Expecting two source references", 2, accumulatedDistribution.getSources().size()); + Iterator sourceIt = accumulatedDistribution.getSources().iterator(); + // should contain source_LCA_yug_ma and source_LCA_yug_ko, testing the microreference which is unique in the tests + assertTrue(" 2 4 ".contains(" " + sourceIt.next().getCitationMicroReference() + " ")); + assertTrue(" 2 4 ".contains(" " + sourceIt.next().getCitationMicroReference() + " ")); } @Test @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class) - public void testArea_rank_and_area() { + public void testArea_rank_and_area_1() { + + Set distributions_LCA = new HashSet<>(); + distributions_LCA.add(newDistribution(book_a, yug_mn, PresenceAbsenceTerm.CULTIVATED(), "1")); + distributions_LCA.add(newDistribution(book_a, yug_ko, PresenceAbsenceTerm.NATIVE(), "2")); // NATIVE should succeed addDistributions( T_LAPSANA_COMMUNIS_ALPINA_UUID, - new Distribution[] { - Distribution.NewInstance(yug_mn, PresenceAbsenceTerm.CULTIVATED()), - Distribution.NewInstance(yug_ko, PresenceAbsenceTerm.NATIVE()), // should succeed - } + distributions_LCA ); + + Set distributions_LC = new HashSet<>(); + distributions_LC.add(newDistribution(book_a, yug_mn, PresenceAbsenceTerm.CULTIVATED(), "3")); + distributions_LC.add(newDistribution(book_a, yug_ko, PresenceAbsenceTerm.NATIVE(), "4")); // NATIVE should succeed + + commitAndStartNewTransaction(null); + addDistributions( T_LAPSANA_COMMUNIS_UUID, - new Distribution[] { - Distribution.NewInstance(yug_mn, PresenceAbsenceTerm.INTRODUCED_UNCERTAIN_DEGREE_OF_NATURALISATION()), - Distribution.NewInstance(yug_ko, PresenceAbsenceTerm.CULTIVATED()), - } + distributions_LC ); engine.accumulate(AggregationMode.byAreasAndRanks, superAreas, lowerRank, upperRank, null, null); Taxon lapsana_communis = (Taxon) taxonService.load(T_LAPSANA_COMMUNIS_UUID); - assertEquals(2, lapsana_communis.getDescriptions().size()); + assertEquals("Lapsana communis alpina must only have 2 Descriptions", 2, lapsana_communis.getDescriptions().size()); Taxon lapsana = (Taxon) taxonService.load(T_LAPSANA_UUID); - assertEquals(1, lapsana.getDescriptions().size()); + assertEquals("Lapsana communis must only have 1 Description", 1, lapsana.getDescriptions().size()); TaxonDescription description = lapsana.getDescriptions().iterator().next(); + assertTrue(description.hasMarker(MarkerType.COMPUTED(), true)); assertEquals(3, description.getElements().size()); int numExpectedFound = 0; for (DescriptionElementBase element : description.getElements()){ @@ -271,25 +275,209 @@ public class TransmissionEngineDistributionTest extends CdmTransactionalIntegrat if(distribution.getArea().equals(yug)){ numExpectedFound++; assertEquals("aggregated status of area YUG is wrong", PresenceAbsenceTerm.NATIVE().getLabel(), distribution.getStatus().getLabel()); + assertEquals(2, distribution.getSources().size()); + Iterator sourceIt = distribution.getSources().iterator(); + // should contain source_LCA_yug_ma and source_LCA_yug_ko, testing the microreference which is unique in the tests + assertTrue(" 2 4 ".contains(" " + sourceIt.next().getCitationMicroReference() + " ")); + assertTrue(" 2 4 ".contains(" " + sourceIt.next().getCitationMicroReference() + " ")); } if(distribution.getArea().equals(yug_mn)){ numExpectedFound++; assertEquals("aggregated status of area YUG-MN is wrong", PresenceAbsenceTerm.CULTIVATED().getLabel(), distribution.getStatus().getLabel()); + assertEquals(2, distribution.getSources().size()); + Iterator sourceIt = distribution.getSources().iterator(); + // should contain source_LCA_yug_ma and source_LCA_yug_ko, testing the microreference which is unique in the tests + assertTrue(" 1 3 ".contains(" " + sourceIt.next().getCitationMicroReference() + " ")); + assertTrue(" 1 3 ".contains(" " + sourceIt.next().getCitationMicroReference() + " ")); } if(distribution.getArea().equals(yug_ko)){ numExpectedFound++; assertEquals("aggregated status of area YUG-KO wrong", PresenceAbsenceTerm.NATIVE().getLabel(), distribution.getStatus().getLabel()); + assertEquals(2, distribution.getSources().size()); + Iterator sourceIt = distribution.getSources().iterator(); + // should contain source_LCA_yug_ma and source_LCA_yug_ko, testing the microreference which is unique in the tests + assertTrue(" 2 4 ".contains(" " + sourceIt.next().getCitationMicroReference() + " ")); + assertTrue(" 2 4 ".contains(" " + sourceIt.next().getCitationMicroReference() + " ")); } } assertEquals("All three expected areas should have been found before", numExpectedFound, 3); } + /** + * Variant of {@link #testArea_rank_and_area_1()} with alternate source references to check the + * suppression of duplicates. + * + * This test relies on {@link #testArea_rank_and_area_1()} + * an makes assertions only on the alternative source references + */ + @Test + @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class) + public void testArea_rank_and_area_2() { + + Set distributions_LCA = new HashSet(); + distributions_LCA.add(newDistribution(book_a, yug_ko, PresenceAbsenceTerm.NATIVE(), "1")); + distributions_LCA.add(newDistribution(book_b, yug_ko, PresenceAbsenceTerm.NATIVE(), "2")); + + addDistributions( + T_LAPSANA_COMMUNIS_ALPINA_UUID, + distributions_LCA + ); + + + engine.accumulate(AggregationMode.byAreasAndRanks, superAreas, lowerRank, upperRank, null, null); + + Taxon lapsana_communis = (Taxon) taxonService.load(T_LAPSANA_COMMUNIS_UUID); + int computedDescriptionsCnt = 0; + for(TaxonDescription description : lapsana_communis.getDescriptions()) { + if(description.hasMarker(MarkerType.COMPUTED(), true)) { + computedDescriptionsCnt++; + assertEquals(2, description.getElements().size()); // yug, yug_ko + for(DescriptionElementBase distribution : description.getElements()) { + logger.debug(((Distribution)distribution).getArea() + " " + sourcesToString(distribution)); + if(((Distribution)distribution).getArea().equals(yug_ko)){ + assertEquals(2, distribution.getSources().size()); + } + if(((Distribution)distribution).getArea().equals(yug)){ + assertEquals(2, distribution.getSources().size()); + } + } + } + } + assertEquals(1, computedDescriptionsCnt); + } + + + /** + * Variant of {@link #testArea_rank_and_area_1()} with alternate source references to check the + * suppression of duplicates. + * + * This test relies on {@link #testArea_rank_and_area_1()} + * an makes assertions only on the alternative source references + */ + @Test + @DataSets({ + @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"), + @DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"), + @DataSet(value="TransmissionEngineDistributionTest.xml"), + }) + public void testArea_rank_and_area_3() { + + Set distributions_LCA = new HashSet(); + distributions_LCA.add(newDistribution(book_a, yug_ko, PresenceAbsenceTerm.NATIVE(), "1")); + distributions_LCA.add(newDistribution(book_a, yug_ko, PresenceAbsenceTerm.NATIVE(), "3")); + + addDistributions( + T_LAPSANA_COMMUNIS_ALPINA_UUID, + distributions_LCA + ); + + Set distributions_LC = new HashSet<>(); + distributions_LC.add(newDistribution(book_a, yug_ko, PresenceAbsenceTerm.NATIVE(), "1")); + distributions_LC.add(newDistribution(book_b, yug_ko, PresenceAbsenceTerm.NATIVE(), "2")); + + commitAndStartNewTransaction(null); + + addDistributions( + T_LAPSANA_COMMUNIS_UUID, + distributions_LC + ); + + engine.accumulate(AggregationMode.byAreasAndRanks, superAreas, lowerRank, upperRank, null, null); + + Taxon lapsana_communis = (Taxon) taxonService.load(T_LAPSANA_COMMUNIS_UUID); + int computedDescriptionsCnt = 0; + for(TaxonDescription description : lapsana_communis.getDescriptions()) { + if(description.hasMarker(MarkerType.COMPUTED(), true)) { + computedDescriptionsCnt++; + assertEquals(2, description.getElements().size()); + for(DescriptionElementBase distribution : description.getElements()) { + logger.debug(((Distribution)distribution).getArea() + " " + sourcesToString(distribution)); + if(((Distribution)distribution).getArea().equals(yug_ko)){ + assertEquals(2, distribution.getSources().size()); + } + if(((Distribution)distribution).getArea().equals(yug)){ + assertEquals(3, distribution.getSources().size()); + } + } + } + } + assertEquals(1, computedDescriptionsCnt); + } + + /** + * Variant of {@link #testArea_rank_and_area_1()} with alternate source references to + * check the handling of the case where the target taxon already has the distribution which is the + * result of the aggregation (see http://dev.e-taxonomy.eu/trac/ticket/4366#comment:12) + * + * This test relies on {@link #testArea_rank_and_area_1()} + * an makes assertions only on the alternative source references + */ + @Test + @DataSets({ + @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"), + @DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"), + @DataSet(value="TransmissionEngineDistributionTest.xml"), + }) + public void testArea_rank_and_area_4() { + + Set distributions_LCA = new HashSet<>(); + distributions_LCA.add(newDistribution(book_a, yug_ko, PresenceAbsenceTerm.NATIVE(), "1")); + + addDistributions( + T_LAPSANA_COMMUNIS_ALPINA_UUID, + distributions_LCA + ); + + Set distributions_LC = new HashSet<>(); + distributions_LC.add(newDistribution(book_a, yug, PresenceAbsenceTerm.NATIVE(), "2")); // should succeed + + commitAndStartNewTransaction(null); + + addDistributions( + T_LAPSANA_COMMUNIS_UUID, + distributions_LC + ); + + engine.accumulate(AggregationMode.byAreasAndRanks, superAreas, lowerRank, upperRank, null, null); + + Taxon lapsana_communis = (Taxon) taxonService.load(T_LAPSANA_COMMUNIS_UUID); + int computedDescriptionsCnt = 0; + for(TaxonDescription description : lapsana_communis.getDescriptions()) { + if(description.hasMarker(MarkerType.COMPUTED(), true)) { + computedDescriptionsCnt++; + assertEquals(2, description.getElements().size()); + Distribution distribution = (Distribution)description.getElements().iterator().next(); + if(distribution.getArea().equals(yug_ko)){ + assertEquals(2, distribution.getSources().size()); + DescriptionElementSource source = distribution.getSources().iterator().next(); + assertEquals("2", source.getCitationMicroReference()); + } + } + } + assertEquals(1, computedDescriptionsCnt); + } + + /** + * @param referenceTitle + * @param area + * @param status + * @param microCitation + * @return + */ + private Distribution newDistribution(Reference reference, NamedArea area, PresenceAbsenceTerm status, + String microCitation) { + DescriptionElementSource source = DescriptionElementSource.NewPrimarySourceInstance(reference, microCitation); + Distribution distribution = Distribution.NewInstance(area, status); + distribution.getSources().add(source); + return distribution; + } + /** * creates a new description for the taxon identified by the UUIDs * @param taxonUuid * @param distributions */ - private void addDistributions(UUID taxonUuid, Distribution[] distributions) { + private void addDistributions(UUID taxonUuid, Collection distributions) { Taxon taxon = (Taxon) taxonService.load(taxonUuid); if(taxon == null) { throw new NullPointerException("No taxon found for " + taxonUuid); @@ -304,6 +492,14 @@ public class TransmissionEngineDistributionTest extends CdmTransactionalIntegrat commitAndStartNewTransaction(null); } + private String sourcesToString(DescriptionElementBase deb) { + StringBuffer out = new StringBuffer(); + for ( DescriptionElementSource source : deb.getSources()) { + out.append(source.getCitation().getTitle() + " : " + source.getCitationMicroReference() + ", "); + } + return out.toString(); + } + //@Test // uncomment to create test data file// @Override @@ -393,6 +589,7 @@ public class TransmissionEngineDistributionTest extends CdmTransactionalIntegrat "REFERENCE", "DESCRIPTIONELEMENTBASE", "DESCRIPTIONBASE", "AGENTBASE", "CLASSIFICATION", "TAXONNODE", "HOMOTYPICALGROUP", "LANGUAGESTRING", + "HIBERNATE_SEQUENCES" }); } diff --git a/cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/TransmissionEngineDistributionTest.xml b/cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/TransmissionEngineDistributionTest.xml index f6296a6895..afa7deece6 100644 --- a/cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/TransmissionEngineDistributionTest.xml +++ b/cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/TransmissionEngineDistributionTest.xml @@ -1,28 +1,41 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +