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
.io
.bogota
;
13 import java
.util
.UUID
;
15 import org
.apache
.log4j
.Logger
;
16 import org
.springframework
.stereotype
.Component
;
17 import org
.springframework
.transaction
.TransactionStatus
;
19 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
20 import eu
.etaxonomy
.cdm
.io
.common
.utils
.ImportDeduplicationHelper
;
21 import eu
.etaxonomy
.cdm
.io
.mexico
.SimpleExcelTaxonImport
;
22 import eu
.etaxonomy
.cdm
.io
.mexico
.SimpleExcelTaxonImportState
;
23 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
24 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
25 import eu
.etaxonomy
.cdm
.model
.name
.IBotanicalName
;
26 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
27 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameFactory
;
28 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
29 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
30 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
31 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
32 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymType
;
33 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
34 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
35 import eu
.etaxonomy
.cdm
.strategy
.homotypicgroup
.BasionymRelationCreator
;
36 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImpl
;
44 public class BogotaChecklistTaxonImport
<CONFIG
extends BogotaChecklistImportConfigurator
>
45 extends SimpleExcelTaxonImport
<CONFIG
> {
47 private static final long serialVersionUID
= -884838817884874228L;
48 private static final Logger logger
= Logger
.getLogger(BogotaChecklistTaxonImport
.class);
50 private static final String ID_COL
= "#";
51 private static final String AUTHOR
= "Autor";
52 private static final String NAME
= "Nombre";
53 private static final String GENUS
= "GĂ©nero";
54 private static final String FAMILIA
= "Familia";
55 private static final String INFRASPECIFIC
= "Taxones infraespecĂficos";
56 private static final String SINONIMOS
= "Sinonimos";
58 private static UUID rootUuid
= UUID
.fromString("d66eda18-4c11-4472-bfe8-f6cd5ed95c9f");
59 private static UUID plantaeUuid
= UUID
.fromString("032fc183-eb4f-4f19-a290-28597a849096");
61 @SuppressWarnings("unchecked")
62 private ImportDeduplicationHelper
<SimpleExcelTaxonImportState
<?
>> deduplicationHelper
63 = (ImportDeduplicationHelper
<SimpleExcelTaxonImportState
<?
>>)ImportDeduplicationHelper
.NewStandaloneInstance();
65 private String lastGenus
;
66 private NonViralNameParserImpl parser
= NonViralNameParserImpl
.NewInstance();
67 private BasionymRelationCreator basionymCreator
= new BasionymRelationCreator();
71 protected String
getWorksheetName(CONFIG config
) {
72 return "Resultados Busqueda Avanzada";
75 private boolean isFirst
= true;
76 private TransactionStatus tx
= null;
82 protected void firstPass(SimpleExcelTaxonImportState
<CONFIG
> state
) {
84 tx
= this.startTransaction();
88 String line
= state
.getCurrentLine() + ": ";
89 Map
<String
, String
> record
= state
.getOriginalRecord();
91 String noStr
= getValue(record
, ID_COL
);
94 TaxonNode taxonNode
= makeTaxon(state
, line
, record
, noStr
);
96 if (taxonNode
!= null){
98 makeSynonyms(state
, record
, line
, taxonNode
.getTaxon(), noStr
);
101 makeInfraSpecific(state
, record
, line
, taxonNode
, noStr
);
103 logger
.warn(line
+ "No taxon node given");
109 protected void secondPass(SimpleExcelTaxonImportState
<CONFIG
> state
) {
111 this.commitTransaction(tx
);
122 private void makeSynonyms(SimpleExcelTaxonImportState
<CONFIG
> state
, Map
<String
, String
> record
, String line
,
123 Taxon taxon
, String noStr
) {
125 String synonymsStr
= getValue(record
, SINONIMOS
);
126 if (synonymsStr
!= null){
127 String
[] splits
= synonymsStr
.split(",");
128 for(String split
: splits
){
129 split
= split
.trim();
130 boolean isMisapplied
= split
.contains("auct.") || split
.contains(" sensu ");
131 if (split
.endsWith(" None")){
132 split
= split
.replace(" None", "").trim();
135 handleSingleMisapplied(state
, split
, line
, taxon
, noStr
);
137 handleSingleSynonym(state
, split
, line
, taxon
, noStr
);
141 basionymCreator
.invoke(taxon
);
151 private void handleSingleMisapplied(SimpleExcelTaxonImportState
<CONFIG
> state
, String nameStr
, String line
,
152 Taxon taxon
, String noStr
) {
153 Rank rank
= Rank
.SPECIES();
154 String AUCT_NON
= "auct. non ";
155 String auctStr
= nameStr
.contains(AUCT_NON
)? AUCT_NON
: nameStr
.endsWith("auct.")?
"auct.": null;
156 boolean auctRequired
= false;
157 if (auctStr
== null){
159 if (nameStr
.endsWith("auct.colomb.")){
160 nameStr
= nameStr
.replace(" auct.colomb.", "");
161 auctStr
= "auct.colomb.";
162 }else if (nameStr
.endsWith(" [auct.mult.non Sw.]")){
163 nameStr
= nameStr
.replace(" [auct.mult.non Sw.]", "");
164 auctStr
= "[auct.mult.non Sw.]";
165 }else if (nameStr
.endsWith(" auct.pr.p.")){
166 nameStr
= nameStr
.replace(" auct.pr.p.", "");
167 auctStr
= "auct.pr.p.";
168 }else if (nameStr
.contains(" sensu ")){
169 logger
.warn(line
+ "sensu not yet handled correctly:" + nameStr
);
170 auctRequired
= false;
172 auctRequired
= false;
173 logger
.warn(line
+ "auct. not recognized: " + nameStr
);
177 nameStr
= nameStr
.replace(auctStr
, "").trim();
179 IBotanicalName name
= (IBotanicalName
)parser
.parseFullName(nameStr
, state
.getConfig().getNomenclaturalCode(), rank
);
180 name
.addImportSource(noStr
, getNamespace(state
.getConfig()), getSourceCitation(state
), null);
181 name
= deduplicationHelper
.getExistingName(state
, name
);
182 if (name
.isProtectedTitleCache()){
183 logger
.warn(line
+ "Misapplied name could not be parsed: " + nameStr
);
185 deduplicationHelper
.replaceAuthorNamesAndNomRef(state
, name
);
187 Taxon misApp
= Taxon
.NewInstance(name
, null);
189 misApp
.setAppendedPhrase(auctStr
);
191 misApp
.addImportSource(noStr
, getNamespace(state
.getConfig()),
192 getSourceCitation(state
), null);
193 taxon
.addMisappliedName(misApp
, state
.getConfig().getSecReference(), null);
201 private String
getNamespace(CONFIG config
) {
202 return getWorksheetName(config
)+"."+ ID_COL
;
213 private void handleSingleSynonym(SimpleExcelTaxonImportState
<CONFIG
> state
, String nameStr
,
214 String line
, Taxon taxon
, String noStr
) {
215 Rank rank
= Rank
.SPECIES();
216 IBotanicalName name
= (IBotanicalName
)parser
.parseFullName(nameStr
, state
.getConfig().getNomenclaturalCode(), rank
);
217 name
.addImportSource(noStr
, getNamespace(state
.getConfig()), getSourceCitation(state
), null);
218 name
= deduplicationHelper
.getExistingName(state
, name
);
219 if (name
.isProtectedTitleCache()){
220 logger
.warn(line
+ "Synonym could not be parsed: " + nameStr
);
222 deduplicationHelper
.replaceAuthorNamesAndNomRef(state
, name
);
224 Synonym synonym
= Synonym
.NewInstance(name
, getSecReference(state
));
225 synonym
.addImportSource(noStr
, getNamespace(state
.getConfig()), getSourceCitation(state
), null);
226 taxon
.addSynonym(synonym
, SynonymType
.SYNONYM_OF());
237 private void makeInfraSpecific(SimpleExcelTaxonImportState
<CONFIG
> state
, Map
<String
, String
> record
, String line
,
238 TaxonNode speciesNode
, String noStr
) {
239 String subSpeciesStr
= getValue(record
, INFRASPECIFIC
);
240 if (subSpeciesStr
!= null){
241 String
[] splits
= subSpeciesStr
.split(",");
242 for(String split
: splits
){
243 if (split
.endsWith(" None")){
244 split
= split
.replace(" None", "").trim();
246 Rank rank
= Rank
.SUBSPECIES();
247 IBotanicalName name
= (IBotanicalName
)parser
.parseFullName(split
.trim(), state
.getConfig().getNomenclaturalCode(), rank
);
248 name
.addImportSource(noStr
, getNamespace(state
.getConfig()), getSourceCitation(state
), null);
249 name
= deduplicationHelper
.getExistingName(state
, name
);
250 if (name
.isProtectedTitleCache()){
251 logger
.warn(line
+ "Infraspecific taxon could not be parsed: " + split
.trim());
253 deduplicationHelper
.replaceAuthorNamesAndNomRef(state
, name
);
255 Taxon subSpecies
= Taxon
.NewInstance(name
, getSecReference(state
));
256 subSpecies
.addImportSource(noStr
, getNamespace(state
.getConfig()), getSourceCitation(state
), null);
257 TaxonNode subSpeciesNode
= speciesNode
.addChildTaxon(subSpecies
, getSecReference(state
), null);
258 getTaxonNodeService().save(subSpeciesNode
);
270 private TaxonNode
makeTaxon(SimpleExcelTaxonImportState
<CONFIG
> state
, String line
, Map
<String
, String
> record
,
273 TaxonNode familyTaxon
= getFamilyTaxon(record
, state
);
274 if (familyTaxon
== null){
275 logger
.warn(line
+ "Family not created: " + record
.get(FAMILIA
));
278 String genusStr
= getValue(record
, GENUS
);
279 String nameStr
= getValue(record
, NAME
);
280 String speciesAuthorStr
= getValue(record
, AUTHOR
);
282 nameStr
= CdmUtils
.concat(" ", nameStr
, speciesAuthorStr
);
283 Rank rank
= Rank
.SPECIES();
284 IBotanicalName name
= (IBotanicalName
)parser
.parseFullName(nameStr
, state
.getConfig().getNomenclaturalCode(), rank
);
285 name
.addImportSource(noStr
, getNamespace(state
.getConfig()), getSourceCitation(state
), null);
286 name
= deduplicationHelper
.getExistingName(state
, name
);
287 if (name
.isProtectedTitleCache()){
288 logger
.warn(line
+ "Name could not be parsed: " + nameStr
);
290 deduplicationHelper
.replaceAuthorNamesAndNomRef(state
, name
);
292 Taxon taxon
= Taxon
.NewInstance(name
, getSecReference(state
));
294 taxon
.addImportSource(noStr
, getNamespace(state
.getConfig()), getSourceCitation(state
), null);
296 String parentStr
= genusStr
;
297 boolean genusAsBefore
= genusStr
.equals(lastGenus
);
298 TaxonNode parent
= getParent(state
, parentStr
);
302 //everything as expected
303 newNode
= parent
.addChildTaxon(taxon
, getSecReference(state
), null);
304 getTaxonNodeService().save(newNode
);
306 logger
.warn(line
+ "Unexpected non-missing parent");
311 logger
.warn(line
+ "Unexpected missing genus parent");
314 parent
= makeGenusNode(state
, record
, genusStr
);
315 newNode
= parent
.addChildTaxon(taxon
, getSecReference(state
), null);
316 getTaxonNodeService().save(newNode
);
320 this.lastGenus
= genusStr
;
329 private TaxonNode
getFamilyTaxon(Map
<String
, String
> record
, SimpleExcelTaxonImportState
<CONFIG
> state
) {
330 String familyStr
= getValue(record
, FAMILIA
);
331 if (familyStr
== null){
334 familyStr
= familyStr
.trim();
336 Taxon family
= state
.getHigherTaxon(familyStr
);
337 TaxonNode familyNode
;
339 familyNode
= family
.getTaxonNodes().iterator().next();
341 IBotanicalName name
= makeFamilyName(state
, familyStr
);
342 Reference sec
= getSecReference(state
);
343 family
= Taxon
.NewInstance(name
, sec
);
345 ITaxonTreeNode classificationNode
= getClassification(state
);
346 familyNode
= classificationNode
.addChildTaxon(family
, sec
, null);
347 state
.putHigherTaxon(familyStr
, family
);
348 getTaxonNodeService().save(familyNode
);
355 private TaxonNode rootNode
;
356 private TaxonNode
getClassification(SimpleExcelTaxonImportState
<CONFIG
> state
) {
357 if (rootNode
== null){
358 Reference sec
= getSecReference(state
);
359 String classificationName
= state
.getConfig().getClassificationName();
360 Language language
= Language
.DEFAULT();
361 Classification classification
= Classification
.NewInstance(classificationName
, sec
, language
);
362 classification
.setUuid(state
.getConfig().getClassificationUuid());
363 classification
.getRootNode().setUuid(rootUuid
);
365 IBotanicalName plantaeName
= TaxonNameFactory
.NewBotanicalInstance(Rank
.KINGDOM());
366 plantaeName
.setGenusOrUninomial("Plantae");
367 Taxon plantae
= Taxon
.NewInstance(plantaeName
, sec
);
368 TaxonNode plantaeNode
= classification
.addChildTaxon(plantae
, null, null);
369 plantaeNode
.setUuid(plantaeUuid
);
370 getClassificationService().save(classification
);
372 rootNode
= plantaeNode
;
378 protected IBotanicalName
makeFamilyName(SimpleExcelTaxonImportState
<CONFIG
> state
, String famStr
) {
379 IBotanicalName name
= TaxonNameFactory
.NewBotanicalInstance(Rank
.FAMILY());
380 famStr
= decapitalize(famStr
);
381 name
.setGenusOrUninomial(famStr
);
382 name
.addSource(makeOriginalSource(state
));
391 protected IdentifiableSource
makeOriginalSource(SimpleExcelTaxonImportState
<CONFIG
> state
) {
392 return IdentifiableSource
.NewDataImportInstance(getValue(state
.getOriginalRecord(),ID_COL
),
393 getNamespace(state
.getConfig()), state
.getConfig().getSourceReference());
400 private String
decapitalize(String famStr
) {
401 String result
= famStr
.substring(0,1) + famStr
.substring(1).toLowerCase();
406 protected Reference
getSecReference(SimpleExcelTaxonImportState
<CONFIG
> state
) {
407 return state
.getConfig().getSecReference();
414 protected Reference
getSourceCitation(SimpleExcelTaxonImportState
<CONFIG
> state
) {
415 return state
.getConfig().getSourceReference();
423 private TaxonNode
getParent(SimpleExcelTaxonImportState
<CONFIG
> state
, String parentStr
) {
424 Taxon taxon
= state
.getHigherTaxon(parentStr
);
426 return taxon
== null ?
null : taxon
.getTaxonNodes().iterator().next();
435 private TaxonNode
makeGenusNode(SimpleExcelTaxonImportState
<CONFIG
> state
,
436 Map
<String
, String
> record
, String genusStr
) {
437 IBotanicalName name
= TaxonNameFactory
.NewBotanicalInstance(Rank
.GENUS());
438 name
.setGenusOrUninomial(genusStr
);
439 Taxon genus
= Taxon
.NewInstance(name
, getSecReference(state
));
440 TaxonNode family
= getFamilyTaxon(record
, state
);
441 TaxonNode genusNode
= family
.addChildTaxon(genus
, getSecReference(state
), null);
442 state
.putHigherTaxon(genusStr
, genus
);
443 genus
.addSource(makeOriginalSource(state
));
444 getTaxonNodeService().save(genusNode
);