3 * Copyright (C) 2017 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
10 package eu
.etaxonomy
.cdm
.strategy
.homotypicgroup
;
12 import java
.util
.ArrayList
;
13 import java
.util
.List
;
15 import java
.util
.UUID
;
17 import org
.apache
.log4j
.Logger
;
19 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
20 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
21 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
22 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
23 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymType
;
24 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
25 import eu
.etaxonomy
.cdm
.strategy
.StrategyBase
;
28 * This class tries to guess all basionym relationships for the synonyms of a given taxon
29 * by evaluating the name parts including authors.
30 * It adds all {@link TaxonName taxon names} that seem to belong to the same
31 * basionym to the homotypic group of this basionym and creates the basionym relationship
32 * if not yet added/created.<BR>
33 * Also it changes the {@link SynonymType synonym type} of the synonyms
34 * that are homotypic to the accepted taxon to
35 * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() homotypic synonym of}.
37 * NOTE: It is still unclear where to put this kind of operations.
38 * The base class, package and even the module may change in future.
44 public class BasionymRelationCreator
extends StrategyBase
{
46 private static final long serialVersionUID
= -4711438819176248413L;
47 @SuppressWarnings("unused")
48 private static final Logger logger
= Logger
.getLogger(BasionymRelationCreator
.class);
50 private UUID uuid
= UUID
.fromString("e9e1d1f5-e398-4ba7-81a6-92875573d7cb");
56 protected UUID
getUuid() {
60 public void invoke (Taxon taxon
){
61 Set
<Synonym
> synonyms
= taxon
.getSynonyms();
63 //compare accepted against synonyms
64 for (Synonym synonym
: synonyms
){
65 TaxonName basionym
= compareHomotypic(taxon
.getName(), synonym
.getName());
66 if (basionym
!= null){
67 synonym
.setType(SynonymType
.HOMOTYPIC_SYNONYM_OF());
68 adaptHomotypicGroup(basionym
, taxon
.getName(), synonym
.getName());
71 List
<Synonym
> synonymList
= new ArrayList
<>(synonyms
);
73 //compareEachSynonymAgainstEachOther;
74 for (int i
= 0; i
< synonymList
.size()-1; i
++){
75 for (int j
= i
+ 1; j
< synonymList
.size(); j
++){
76 Synonym syn1
= synonymList
.get(i
);
77 Synonym syn2
= synonymList
.get(j
);
78 TaxonName basionym
= compareHomotypic(syn1
.getName(), syn2
.getName());
79 if (basionym
!= null){
80 adaptHomotypicGroup(basionym
, syn1
.getName(), syn2
.getName());
81 if (taxon
.getName().getBasionyms().contains(basionym
)){
82 syn1
.setType(SynonymType
.HOMOTYPIC_SYNONYM_OF());
83 syn2
.setType(SynonymType
.HOMOTYPIC_SYNONYM_OF());
95 private void adaptHomotypicGroup(TaxonName basionym
,
96 TaxonName name1
, TaxonName name2
) {
97 if (basionym
.equals(name1
)){
98 if (!name2
.getBasionyms().contains(name1
)){
99 name2
.addBasionym(name1
);
101 }else if (basionym
.equals(name2
)){
102 if (!name1
.getBasionyms().contains(name2
)){
103 name1
.addBasionym(name2
);
112 private TaxonName
compareHomotypic(TaxonName name1
, TaxonName name2
) {
113 if (name1
== null || name2
== null){
116 TaxonName basionymCandidate
= checkAuthors(name1
, name2
);
117 if (basionymCandidate
== null){
120 TaxonName newCombinationCandidate
121 = basionymCandidate
== name1? name2
: name1
;
122 boolean isBasionym
= compareNameParts(basionymCandidate
, newCombinationCandidate
);
124 return basionymCandidate
;
132 * @param basionymCandiate
133 * @param newCombinationCandidate
135 private boolean compareNameParts(TaxonName basionymCandidate
,
136 TaxonName newCombinationCandidate
) {
137 if (basionymCandidate
.isGenusOrSupraGeneric() || newCombinationCandidate
.isGenusOrSupraGeneric()){
139 }else if (matchLastNamePart(basionymCandidate
, newCombinationCandidate
)){
150 private TaxonName
checkAuthors(TaxonName name1
, TaxonName name2
) {
151 if (hasBasionymAuthorOf(name1
, name2
)){
153 }else if (hasBasionymAuthorOf(name2
, name1
)){
165 private boolean hasBasionymAuthorOf(TaxonName name1
, TaxonName name2
) {
166 TeamOrPersonBase
<?
> basAuthor2
= name2
.getBasionymAuthorship();
167 TeamOrPersonBase
<?
> combinationAuthor
= name1
.getCombinationAuthorship();
168 TeamOrPersonBase
<?
> basAuthor1
= name1
.getBasionymAuthorship();
169 if (basAuthor2
!= null && basAuthor1
== null){
170 if (matches(basAuthor2
, combinationAuthor
)){
179 * @param combinationAuthor
182 private boolean matches(TeamOrPersonBase
<?
> basAuthor
, TeamOrPersonBase
<?
> combinationAuthor
) {
183 //TODO better do with a CDM matcher that also compares other fields and
184 //returns false if other fields are contradictory
185 if (basAuthor
== null || combinationAuthor
== null){
187 }else if (basAuthor
== combinationAuthor
|| basAuthor
.equals(combinationAuthor
)){
189 }else if (CdmUtils
.nonEmptyEquals(basAuthor
.getNomenclaturalTitle(), combinationAuthor
.getNomenclaturalTitle())){
197 * @param basionymName
198 * @param newCombination
201 public static boolean matchLastNamePart(TaxonName name1
, TaxonName name2
) {
202 String familyNamePart1
= name1
.getLastNamePart();
203 String familyNamePart2
= name2
.getLastNamePart();
204 if (familyNamePart1
!= null && familyNamePart2
!= null){
205 familyNamePart1
= normalizeBasionymNamePart(familyNamePart1
);
206 familyNamePart2
= normalizeBasionymNamePart(familyNamePart2
);
207 return (familyNamePart1
.equals(familyNamePart2
));
214 * @param familyNamePart
217 private static String
normalizeBasionymNamePart(String familyNamePart
) {
218 String namePart
= familyNamePart
.toLowerCase()
219 .replaceAll("(um|us|a|is|e|os|on|or)$", "")
220 .replaceAll("er$", "r") //e.g. ruber <-> rubra
221 .replaceAll("ese$", "s"); //e.g. cayanensis <-> cayanenese
222 //TODO tampensis / tampense