Project

General

Profile

Download (33.1 KB) Statistics
| Branch: | Revision:
1
/**
2
* Copyright (C) 2020 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.cdm.io.casearia;
10

    
11
import java.util.ArrayList;
12
import java.util.HashMap;
13
import java.util.HashSet;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.Set;
17
import java.util.UUID;
18

    
19
import org.apache.logging.log4j.LogManager;
20
import org.apache.logging.log4j.Logger;
21
import org.springframework.stereotype.Component;
22

    
23
import eu.etaxonomy.cdm.api.service.config.SynonymDeletionConfigurator;
24
import eu.etaxonomy.cdm.common.CdmUtils;
25
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
26
import eu.etaxonomy.cdm.io.mexico.SimpleExcelTaxonImport;
27
import eu.etaxonomy.cdm.io.mexico.SimpleExcelTaxonImportState;
28
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
29
import eu.etaxonomy.cdm.model.common.CdmBase;
30
import eu.etaxonomy.cdm.model.common.Language;
31
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
32
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
33
import eu.etaxonomy.cdm.model.name.Rank;
34
import eu.etaxonomy.cdm.model.name.RankClass;
35
import eu.etaxonomy.cdm.model.name.TaxonName;
36
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
37
import eu.etaxonomy.cdm.model.reference.Reference;
38
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
39
import eu.etaxonomy.cdm.model.reference.ReferenceType;
40
import eu.etaxonomy.cdm.model.taxon.Classification;
41
import eu.etaxonomy.cdm.model.taxon.Synonym;
42
import eu.etaxonomy.cdm.model.taxon.SynonymType;
43
import eu.etaxonomy.cdm.model.taxon.Taxon;
44
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
45
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
46
import eu.etaxonomy.cdm.model.term.DefinedTerm;
47
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
48

    
49
/**
50
 * Taxon import for Casearia from Kew world checklist of plants.
51
 *
52
 * @author a.mueller
53
 * @since 12.05.2020
54
 */
55
@Component
56
public class CaseariaTaxonImport extends SimpleExcelTaxonImport<CaseariaImportConfigurator>{
57

    
58
    private static final long serialVersionUID = 7686154384296707819L;
59
    private static final Logger logger = LogManager.getLogger();
60

    
61
    protected static final String TAXON_MAPPING = "TaxonMapping";
62
    private static final String NAME_CIT = "NameCit";
63
    private static final String IPNI_ID = "ipni_id";
64
    private static final String PLANT_NAME_ID = "plant_name_id";
65
    private static final String TAXON_RANK = "taxon_rank";
66
    private static final String TAXON_STATUS = "taxon_status";
67
    private static final String NOMENCLATURAL_REMARKS = "nomenclatural_remarks";
68
    private static final String ACCEPTED_PLANT_NAME_ID = "accepted_plant_name_id";
69
    private static final String TAXON_NAME = "taxon_name";
70
    private static final String TAXON_AUTHORS = "taxon_authors";
71
    private static final String FAMILY = "family";
72
    private static final String FIRST_PUBLISHED = "first_published";
73
    private static final String PUB_TYPE = "PubType";
74
    private static final String VOLUME_AND_PAGE = "volume_and_page";
75
    private static final String PLACE_OF_PUBLICATION = "place_of_publication";
76
    private static final String PRIMARY_AUTHOR = "primary_author";
77
    private static final String PARENTHETICAL_AUTHOR = "parenthetical_author";
78
    private static final String INFRASPECIFIC_RANK = "infraspecific_rank";
79
    private static final String INFRASPECIES = "infraspecies";
80
    private static final String SPECIES = "species";
81
    private static final String GENUS = "genus";
82

    
83
    private static final int RECORDS_PER_TRANSACTION = 500;
84
    private static boolean logMissingIpniId = false;
85

    
86
    private Map<String, UUID> taxonMapping = new HashMap<>();
87
    private Reference secRef = null;
88
    private Set<UUID> createdNames = new HashSet<>();
89

    
90
    private SimpleExcelTaxonImportState<CaseariaImportConfigurator> state;
91
    private NonViralNameParserImpl parser = new NonViralNameParserImpl();
92

    
93

    
94
    @Override
95
    protected void firstPass(SimpleExcelTaxonImportState<CaseariaImportConfigurator> state) {
96
        int line = state.getCurrentLine();
97
        if ((line % RECORDS_PER_TRANSACTION) == 0){
98
            newTransaction(state);
99
            System.out.println(line);
100
        }
101

    
102
        this.state = state;
103
        Map<String, String> record = state.getOriginalRecord();
104

    
105
        String fullCitation = getValue(record, NAME_CIT);
106
        String ipniId = getValue(record, IPNI_ID);
107
        String sourceId = getValue(record, PLANT_NAME_ID);
108
        String rankStr = getValue(record, TAXON_RANK);
109
        String status = getValue(record, TAXON_STATUS);
110
        String nomenclaturalRemarks = getValue(record, NOMENCLATURAL_REMARKS);
111
        String accId = getValue(record, ACCEPTED_PLANT_NAME_ID);
112
        String taxonNameStr = getValue(record, TAXON_NAME);
113
        String taxonAuthors = getValue(record, TAXON_AUTHORS);
114

    
115
        String fullNameStr = CdmUtils.concat(" ", taxonNameStr,taxonAuthors);
116
        String row = String.valueOf(line) + "("+fullNameStr+"): ";
117

    
118
        boolean isNewName = true;
119

    
120
        try {
121

    
122
            List<NomenclaturalStatusType> statusTypes = new ArrayList<>();
123
            Class<? extends CdmBase> taxonClazz = makeStatus(status, sourceId, accId, row, statusTypes);
124

    
125

    
126
            Rank rank = state.getTransformer().getRankByKey(rankStr);
127

    
128
            TaxonName name = parser.parseReferencedName(fullCitation, state.getConfig().getNomenclaturalCode(), rank);
129
            if (name.isProtectedFullTitleCache() || name.isProtectedTitleCache() || name.isProtectedNameCache()
130
                    || name.isProtectedAuthorshipCache()){
131
                logger.warn(row + "Name not parsable: " + fullCitation);
132
                name.setTitleCache(fullNameStr, true);
133
                name.setNameCache(taxonNameStr, true);
134
            }else{
135
                testParsedName(state, name, row, null);
136
            }
137
            name.addImportSource(sourceId, PLANT_NAME_ID, getSourceReference(state), "line " + state.getCurrentLine());
138
            name = dedupliateNameParts(name);
139
            getNameService().saveOrUpdate(name);
140
            createdNames.add(name.getUuid());
141

    
142
            handleNomenclRemarkAndNameStatus(nomenclaturalRemarks, row, isNewName, name, statusTypes);
143

    
144
            TaxonBase<?> taxonBase;
145
            if (taxonClazz == Taxon.class){
146
                taxonBase = Taxon.NewInstance(name, getSecRef());
147
            }else{
148
                taxonBase = Synonym.NewInstance(name, getSecRef());
149
            }
150
            taxonBase.addImportSource(sourceId, PLANT_NAME_ID, getSourceReference(state), "line " + state.getCurrentLine());
151
            getTaxonService().saveOrUpdate(taxonBase);
152

    
153
            if (!isBlank(ipniId)){
154
                DefinedTerm ipniIdIdentifierType = DefinedTerm.IDENTIFIER_NAME_IPNI();
155
                name.addIdentifier(ipniId, ipniIdIdentifierType);
156
            }else{
157
                if(logMissingIpniId){
158
                    logger.warn(row + "IPNI id is missing.");
159
                }
160
            }
161

    
162
            UUID uuid = taxonMapping.put(sourceId, taxonBase.getUuid());{
163
                if (uuid != null){
164
                    logger.warn(row + "sourceId already existed in taxonMapping: " + sourceId);
165
                }
166
            }
167
            if(taxonBase.isInstanceOf(Taxon.class)){
168
                UUID existingUuid = taxonMapping.put(name.getNameCache(), taxonBase.getUuid());
169
                if (existingUuid != null){
170
                    logger.warn(row + name.getNameCache() + " has multiple instances in file");
171
                }
172
            }
173
        } catch (UndefinedTransformerMethodException e) {
174
            e.printStackTrace();
175
        }
176
    }
177

    
178
    private TaxonName dedupliateNameParts(TaxonName name) {
179
        if (state.getConfig().isDoDeduplicate()){
180
            state.getDeduplicationHelper().replaceAuthorNamesAndNomRef(name);
181
        }
182
        return name;
183
    }
184

    
185
    private Class<? extends CdmBase> makeStatus(String status, String sourceId,
186
            String accId, String row, List<NomenclaturalStatusType> statusTypes) {
187

    
188
        Class<? extends CdmBase> clazz;
189
        if ("Accepted".equals(status) || "Unplaced".equals(status) || "Misapplied".equals(status)){
190
            clazz = Taxon.class;
191
        }else if ("Synonym".equals(status)){
192
            clazz = (accId == null)? Taxon.class : Synonym.class;
193
        }else if("Illegitimate".equals(status)){
194
            clazz = getIllegInvalidStatus(sourceId, accId);
195
            statusTypes.add(NomenclaturalStatusType.ILLEGITIMATE());
196
        }else if ("Invalid".equals(status)){
197
            clazz = getIllegInvalidStatus(sourceId, accId);
198
            statusTypes.add(NomenclaturalStatusType.INVALID());
199
        }else{
200
            logger.warn(row + "Unhandled status: " + status);
201
            clazz = Taxon.class;  //to do something
202
        }
203
        return clazz;
204
    }
205

    
206
    private void handleNomenclRemarkAndNameStatus(String nomenclaturalRemarks, String row, boolean isNewName, TaxonName name,
207
            List<NomenclaturalStatusType> statusTypes) {
208

    
209
        NomenclaturalStatusType remarkType = null;
210
        NomenclaturalStatusType statusType = statusTypes.isEmpty()? null: statusTypes.iterator().next();
211
        if (nomenclaturalRemarks == null){
212
           //nothing to do
213
        }else if (", nom. illeg.".equals(nomenclaturalRemarks)){
214
            remarkType = NomenclaturalStatusType.ILLEGITIMATE();
215
        }else if (", nom. cons.".equals(nomenclaturalRemarks)){
216
            remarkType = NomenclaturalStatusType.CONSERVED();
217
        }else if (", nom. nud.".equals(nomenclaturalRemarks)){
218
            remarkType = NomenclaturalStatusType.NUDUM();
219
        }else if (", nom. provis.".equals(nomenclaturalRemarks)){
220
            remarkType = NomenclaturalStatusType.PROVISIONAL();
221
        }else if (", nom. rej.".equals(nomenclaturalRemarks)){
222
            remarkType = NomenclaturalStatusType.REJECTED();
223
        }else if (", nom. subnud.".equals(nomenclaturalRemarks)){
224
            remarkType = NomenclaturalStatusType.SUBNUDUM();
225
        }else if (", nom. superfl.".equals(nomenclaturalRemarks)){
226
            remarkType = NomenclaturalStatusType.SUPERFLUOUS();
227
        }else if (", not validly publ.".equals(nomenclaturalRemarks)){
228
            statusTypes.add(NomenclaturalStatusType.INVALID());
229
        }else if (", opus utique oppr.".equals(nomenclaturalRemarks)){
230
            statusTypes.add(NomenclaturalStatusType.OPUS_UTIQUE_OPPR());
231
        }else {
232
            logger.warn(row + "Unhandled nomenclatural remark: " + nomenclaturalRemarks);
233
        }
234

    
235
        NomenclaturalStatusType kewType = remarkType != null? remarkType : statusType;
236
        if (isNewName){
237
            if(remarkType != null && statusType != null && !remarkType.equals(statusType)){
238
                logger.warn(row + "Kew suggests 2 different nom. status. types for new name. The status from nomenclatural_remarks was taken.");
239
            }
240
            if (kewType != null){
241
                name.addStatus(kewType, getSecRef(), null);
242
            }
243
        }else{
244
            NomenclaturalStatusType existingType = null;
245
            if (!name.getStatus().isEmpty()){
246
                existingType = name.getStatus().iterator().next().getType();
247
            }
248
            if (existingType != null && kewType != null){
249
                if (!existingType.equals(kewType)){
250
                    logger.warn(row + "Existing name status "+existingType.getTitleCache()+" differs from Kew status " + kewType.getTitleCache() + ". Key status ignored");
251
                }
252
            }else if (existingType != null && kewType == null){
253
                logger.warn(row + "Info: Existing name has a name status "+existingType.getTitleCache()+" but Kew name has no status. Existing status kept.");
254
            }else if (existingType == null && kewType != null){
255
                if(remarkType != null && statusType != null && !remarkType.equals(statusType)){
256
                    logger.warn(row + "Existing name has no status while Kew name suggests a status (but 2 different status form status and nomenclatural_remarks field).");
257
                }else{
258
                    logger.warn(row + "Existing name has no status while Kew name suggests a status ("+kewType.getTitleCache()+"). Kew status ignored.");
259
                }
260
            }
261
        }
262
    }
263

    
264
    private void newTransaction(SimpleExcelTaxonImportState<CaseariaImportConfigurator> state) {
265
        commitTransaction(state.getTransactionStatus());
266
        secRef = null;
267
        state.getDeduplicationHelper().reset();
268
        state.setSourceReference(null);
269
        System.gc();
270
        state.setTransactionStatus(startTransaction());
271
    }
272

    
273
    private Reference getSecRef() {
274
        if (secRef == null){
275
            secRef = getReferenceService().find(state.getConfig().getSecUuid());
276
            if (secRef == null){
277
                secRef = ReferenceFactory.newDatabase();
278
                secRef.setTitle("Casearia Database");
279
            }
280
        }
281
        return secRef;
282
    }
283

    
284
    private Class<? extends CdmBase> getIllegInvalidStatus(String sourceId, String accId) {
285
        if (sourceId.equals(accId)){
286
            return Taxon.class;
287
        }else if(accId != null){
288
            return Synonym.class;
289
        }
290
        return null;
291
    }
292

    
293

    
294
    private void testParsedName(SimpleExcelTaxonImportState<CaseariaImportConfigurator> state, TaxonName name,
295
            String row, String fullCitation) throws UndefinedTransformerMethodException {
296

    
297
        Map<String, String> record = state.getOriginalRecord();
298

    
299
//      publication_author
300

    
301
        String rankStr = getValue(record, TAXON_RANK);
302
        String nameCache = getValue(record, TAXON_NAME);
303
        String authorshipCache = getValue(record, TAXON_AUTHORS);
304
        String genus = getValue(record, GENUS);
305
        String species = getValue(record, SPECIES);
306
        String infraspecies = getValue(record, INFRASPECIES);
307
        String infraSpecRank = getValue(record, INFRASPECIFIC_RANK);
308
        String basionymAuthor = getValue(record, PARENTHETICAL_AUTHOR);
309
        String combinationAuthor = getValue(record, PRIMARY_AUTHOR);
310
        String place_of_publication = getValue(record, PLACE_OF_PUBLICATION);
311
        String volume_and_page = getValue(record, VOLUME_AND_PAGE);
312
        String pubType = getValue(record, PUB_TYPE);
313
        String yearPublished = getValue(record, FIRST_PUBLISHED);
314

    
315
        String fullName = CdmUtils.concat(" ", nameCache, authorshipCache);
316

    
317
        if (!CdmUtils.nullSafeEqual(name.getNameCache(), nameCache)){
318
            logger.warn(row + "Unexpected nameCache: " + nameCache);
319
        }
320
        if (!CdmUtils.nullSafeEqual(name.getTitleCache(), fullName)){
321
            logger.warn(row + "Unexpected titleCache: <->" + name.getTitleCache());
322
        }
323
        if (!CdmUtils.nullSafeEqual(name.getGenusOrUninomial(),genus)){
324
            logger.warn(row + "Unexpected genus: " + genus);
325
        }
326
        if (!CdmUtils.nullSafeEqual(name.getSpecificEpithet(), species)){
327
            logger.warn(row + "Unexpected species epithet: " + name.getSpecificEpithet() +"<->"+ species);
328
        }
329
        if (!CdmUtils.nullSafeEqual(name.getInfraSpecificEpithet(), infraspecies)){
330
            logger.warn(row + "Unexpected infraspecific epithet: " + name.getInfraSpecificEpithet() +"<->"+ infraspecies);
331
        }
332
        if (!CdmUtils.nullSafeEqual(name.getAuthorshipCache(), authorshipCache)){
333
            logger.warn(row + "Unexpected authors: " + name.getAuthorshipCache() +"<->"+ authorshipCache);
334
        }
335
        String combinationAndExAuthor = authorTitle(name.getCombinationAuthorship(), name.getExCombinationAuthorship());
336
        if (!CdmUtils.nullSafeEqual(combinationAndExAuthor, combinationAuthor)){
337
            logger.warn(row + "Unexpected combination author: " + combinationAndExAuthor +"<->"+ combinationAuthor);
338
        }
339
        String basionymAndExAuthor = authorTitle(name.getBasionymAuthorship(), name.getExBasionymAuthorship());
340
        if (!CdmUtils.nullSafeEqual(basionymAndExAuthor, basionymAuthor)){
341
            logger.warn(row + "Unexpected basionym author: " + basionymAndExAuthor +"<->"+ basionymAuthor);
342
        }
343
        Rank rank = state.getTransformer().getRankByKey(rankStr);
344
        if (!rank.equals(name.getRank())){
345
            logger.warn(row + "Unexpected rank: " + rankStr);
346
        }
347

    
348
        Reference nomRef = name.getNomenclaturalReference();
349
        if (nomRef == null){
350
            if (fullCitation != null){
351
                NonViralNameParserImpl parser = new NonViralNameParserImpl();
352
                TaxonName parsedName = parser.parseReferencedName(fullCitation, NomenclaturalCode.ICNAFP, rank);
353
                if (parsedName.getNomenclaturalReference() != null){
354
                    name.setNomenclaturalReference(parsedName.getNomenclaturalReference());
355
                    logger.warn(row + "Nom.ref. was missing. Taken from Kew");
356
                }else{
357
                    logger.warn(row + "Nom. ref. is missing or can not be parsed");
358
                }
359
            }else{
360
                logger.warn(row + "NomRef is missing.");
361
            }
362
        }else{
363
            if ("A".equals(pubType) && nomRef.getType() != ReferenceType.Article){
364
                logger.warn(row + "Unexpected nomref type: " + pubType + "<->" + nomRef.getType().toString());
365
            }
366
            if ("B".equals(pubType) && nomRef.getType() != ReferenceType.Book){
367
                logger.warn(row + "Unexpected nomref type: " + pubType + "<->" + nomRef.getType().toString());
368
            }
369
            String year = normalizeYear(yearPublished);
370
            if (!CdmUtils.nullSafeEqual(year, nomRef.getDatePublishedString())){
371
                logger.warn(row + "Unexpected year: " + year + "<->" + nomRef.getDatePublishedString());
372
            }
373
            if (volume_and_page != null && !name.getFullTitleCache().contains(volume_and_page)){
374
                logger.warn(row + "volume_and_page not found in fullTitleCache: " + name.getFullTitleCache() +"<->"+ volume_and_page);
375
            }
376
            if (place_of_publication != null && !name.getFullTitleCache().contains(place_of_publication)){
377
                logger.warn(row + "place_of_publication not found in fullTitleCache: " + name.getFullTitleCache() +"<->"+ place_of_publication);
378
            }
379
        }
380
        if (isBlank(infraSpecRank)){
381
            if (rank.isLowerThan(RankClass.Species)){
382
                logger.warn(row +  "No infraspce marker given but rank is lower than species");
383
            }
384
        }else if ("subsp.".equals(infraSpecRank)){
385
            if(!rank.equals(Rank.SUBSPECIES())){
386
                logger.warn(row + "Unexpected infraspec marker: " + infraSpecRank);
387
            }
388
        }else if ("var.".equals(infraSpecRank)){
389
            if (!rank.equals(Rank.VARIETY())){
390
                logger.warn(row + "Unexpected infraspec marker: " + infraSpecRank);
391
            }
392
        }else if ("subvar.".equals(infraSpecRank)){
393
            if (!rank.equals(Rank.SUBVARIETY())){
394
                logger.warn(row + "Unexpected infraspec marker: " + infraSpecRank);
395
            }
396
        }else if ("f.".equals(infraSpecRank)){
397
            if (!rank.equals(Rank.FORM())){
398
                logger.warn(row + "Unexpected infraspec marker: " + infraSpecRank);
399
            }
400
        }else{
401
            logger.warn(row + "Unhandled infraspec marker: " + infraSpecRank);
402
        }
403
    }
404

    
405
    private String authorTitle(TeamOrPersonBase<?> author, TeamOrPersonBase<?> exAuthor) {
406
        String authorStr = author == null? null: author.getNomenclaturalTitleCache();
407
        String exAuthorStr = exAuthor == null? null: exAuthor.getNomenclaturalTitleCache();
408
        return CdmUtils.concat(" ex ", exAuthorStr, authorStr);
409
    }
410

    
411
    private String normalizeYear(String year) {
412
        if (year == null){
413
            return null;
414
        }else{
415
            year = year.substring(1, year.length() - 1);
416
        }
417
        if (year.contains("\" [")){
418
            String[] split = year.split("\" \\[");
419
            year = split[1].replace("]","") + " [" + split[0]+"\"]";
420
        }else if ("?".equals(year)){
421
            return null;
422
        }
423
        return year;
424
    }
425

    
426
    @Override
427
    protected void secondPass(SimpleExcelTaxonImportState<CaseariaImportConfigurator> state) {
428
        state.putStatusItem(TAXON_MAPPING, taxonMapping);
429

    
430

    
431
        Map<String, String> record = state.getOriginalRecord();
432
        int line = state.getCurrentLine();
433
//        String fullName = getValue(record, KEW_F_NAME4CDM_LINK);
434
        String status = getValue(record, TAXON_STATUS);
435
        String sourceId = getValue(record, PLANT_NAME_ID);
436
        String accId = getValue(record, ACCEPTED_PLANT_NAME_ID);
437
        String family = getValue(record, FAMILY);
438

    
439
        String accName = getValue(record, "AcceptedName");
440
        String basionymId = getValue(record, "basionym_plant_name_id");
441
        String homotypicSynonym = getValue(record, "homotypic_synonym");
442

    
443
//      AcceptedName, Basionym, taxon_name_hybcorr, genus_hybrid, species_hybrid, homotypic_synonym,
444
//      basionym_plant_name_id
445

    
446
        String taxonNameStr = getValue(record, TAXON_NAME);
447
        String taxonAuthors = getValue(record, TAXON_AUTHORS);
448
        String fullNameStr = CdmUtils.concat(" ", taxonNameStr,taxonAuthors);
449
        String row = String.valueOf(line) + "("+fullNameStr+"): ";
450

    
451
        try {
452
            if ((line % RECORDS_PER_TRANSACTION) == 0){
453
                newTransaction(state);
454
                System.out.println(line);
455
            }
456

    
457
            UUID uuid = taxonMapping.get(sourceId);
458
            TaxonBase<?> taxonBase = getTaxonService().find(uuid);
459
            if (taxonBase == null){
460
                logger.warn(row + "taxonBase not found: " + sourceId);
461
                return;
462
            }
463

    
464
            UUID accUuid = taxonMapping.get(accId);
465
            boolean hasAccepted = !sourceId.equals(accId);
466

    
467
            Taxon accTaxon = null;
468
            TaxonNode parent = null;
469
            Taxon child = null;
470
            Synonym syn = null;
471
            boolean isSynonymAccepted = false;
472

    
473
            if(accId == null){
474
                logger.info(row + "accID is null");
475
                child = CdmBase.deproxy(taxonBase, Taxon.class);
476
            //synonyms
477
            }else if(hasAccepted){
478
                TaxonBase<?> accTaxonBase = getTaxonService().find(accUuid);
479
                if (accTaxonBase == null){
480
//                    logger.warn(row + "acctaxon not found: " + accId + "; " + accName);
481
                }else if(!accTaxonBase.isInstanceOf(Taxon.class)){
482
                    logger.warn(row + "acctaxon is synonym: " + accId + "; " + accName);
483
                    isSynonymAccepted = true;
484
                }else{
485
                    accTaxon = CdmBase.deproxy(accTaxonBase, Taxon.class);
486
                    if (!accTaxon.getName().getTitleCache().equals(accName)){
487
                        logger.warn(row + "Accepted name differs: " + accName +" <-> "+ accTaxon.getName().getTitleCache());
488
                    }
489
                }
490
            //accepted taxa
491
            }else if (sourceId.equals(accId)){
492
                if (!taxonBase.isInstanceOf(Taxon.class)){
493
                    logger.warn(row + "child not of class Taxon: " + sourceId);
494
                }else{
495
                    Rank rank = taxonBase.getName().getRank();
496
                    child = CdmBase.deproxy(taxonBase, Taxon.class);
497
                    if(rank.equals(Rank.GENUS())){
498
                        parent = getFamily(row, family);
499
                    }else if (rank.equals(Rank.SPECIES())){
500
                        String genus = child.getName().getGenusOrUninomial();
501
                        UUID parentUuid = taxonMapping.get(genus);
502
                        parent = getParent(parentUuid, row);
503
                    }else if (rank.isLowerThan(RankClass.Species)){
504
                        String speciesName = child.getName().getGenusOrUninomial() + " " + child.getName().getSpecificEpithet();
505
                        UUID parentUuid = taxonMapping.get(speciesName);
506
                        parent = getParent(parentUuid, row);
507
                    }
508
                }
509
            }
510

    
511
            if (taxonBase.isInstanceOf(Synonym.class)){
512
                syn = CdmBase.deproxy(taxonBase, Synonym.class);
513
            }
514

    
515
            if ("Accepted".equals(status)){
516
                if (parent == null){
517
                    logger.warn(row + "Parent is missing. Taxon is moved to 'unresolved' instead'");
518
                    parent = unresolvedParent();
519
                }
520
                if (child == null){
521
                    logger.warn(row + "Child is missing. Taxon not imported.");
522
                }else{
523
                    if (!child.getTaxonNodes().isEmpty()){
524
                        if(!child.getName().getRank().equals(Rank.GENUS())){
525
                            logger.warn(row + "Taxon already has a parent. Taxon not attached to any further parent taxon.");
526
                        }
527
                    }else{
528
                        addChild(parent, child, row);
529
                    }
530
                }
531
            }else if ("Synonym".equals(status)){
532
                if(accTaxon == null){
533
                    if(isSynonymAccepted){
534
                        logger.warn(row +  "Synonym added to 'unresolved' as accepted taxon is synonym itself.");
535
                    }else if (accId != null){
536
                        logger.warn(row +  "Accepted taxon "+accName+" for synonym unexpectedly does not exist. Synonym was moved to 'unresolved'");
537
                    }else{
538
                        logger.warn(row +  "No accepted taxon given for synonym. Therefore taxon was moved to 'unresolved'");
539
                    }
540
                    if(accId != null){
541
                        child = Taxon.NewInstance(syn.getName(), syn.getSec());
542
                        taxonMapping.put(sourceId, child.getUuid());
543
                        child.addImportSource(sourceId, PLANT_NAME_ID, getSourceReference(state), "line " + state.getCurrentLine());
544
                    }
545
                    addChild(unresolvedParent(), child, row);
546
                    getTaxonService().deleteSynonym(syn, new SynonymDeletionConfigurator());
547
                }else{
548
                    accTaxon.addSynonym(syn, SynonymType.SYNONYM_OF);
549
                }
550
            }else if ("Misapplied".equals(status)){
551
                Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
552
                if(accTaxon == null){
553
                    if(isSynonymAccepted){
554
                        logger.warn(row +  "Misapplication added to 'unresolved' as accepted taxon is synonym itself.");
555
                    }else if (accId != null){
556
                        logger.warn(row +  "Accepted taxon "+accName+" for misapplication unexpectedly does not exist. Misapplication was moved to 'unresolved'");
557
                    }else{
558
                        logger.warn(row +  "No accepted taxon given for misapplication. Therefore taxon was moved to 'unresolved'");
559
                    }
560
                    addChild(unresolvedParent(), taxon, row);
561
                }else{
562
                    accTaxon.addMisappliedName(taxon, null, null);
563
                }
564
            }else if ("Unplaced".equals(status)){
565
                parent = unresolvedParent();
566
                addChild(parent, child, row);
567
            }else if("Illegitimate".equals(status) || "Invalid".equals(status)){
568
                if (hasAccepted){
569
                    if(accTaxon == null){
570
                        logger.warn(row + "accepted taxon for illegitimate or invalid taxon not found. Illeg/inval taxon was moved to 'unresolved'");
571
                        child = Taxon.NewInstance(syn.getName(), syn.getSec());
572
                        addChild(unresolvedParent(), child, row);
573
                    }else{
574
                        accTaxon.addSynonym(syn, SynonymType.SYNONYM_OF);
575
                    }
576
                }else{
577
                    addChild(unresolvedParent(), child, row);
578
                }
579
            }else{
580
                logger.warn(row + "Unhandled status: " +  status);
581
            }
582

    
583
            if (basionymId != null && false){
584
                UUID basionymUuid = taxonMapping.get(basionymId);
585
                TaxonBase<?> basionymTaxon = getTaxonService().find(basionymUuid);
586
                if (basionymTaxon != null){
587
                    if (hasSameAcceptedTaxon(taxonBase, basionymTaxon)){
588
                        if (taxonBase.getName().getBasionym() == null){
589
                            taxonBase.getName().addBasionym(basionymTaxon.getName());
590
                        }
591
                    }else{
592
                        logger.warn(row + "Basionym has not same accepted taxon and therefore was ignored.");
593
                    }
594
                }else{
595
                    logger.warn(row + "Basionym "+basionymId+" not found.");
596
                }
597
            }
598
        } catch (Exception e) {
599
            logger.error(row + "Error.");
600
            e.printStackTrace();
601
        }
602
    }
603

    
604
    private boolean hasSameAcceptedTaxon(TaxonBase<?> taxonBase, TaxonBase<?> basionymTaxon) {
605
        if (taxonBase.isInstanceOf(Synonym.class)){
606
            taxonBase = CdmBase.deproxy(taxonBase, Synonym.class).getAcceptedTaxon();
607
        }
608
        if (basionymTaxon.isInstanceOf(Synonym.class)){
609
            basionymTaxon = CdmBase.deproxy(basionymTaxon, Synonym.class).getAcceptedTaxon();
610
        }
611
        return taxonBase != null && basionymTaxon != null && taxonBase.equals(basionymTaxon);
612
    }
613

    
614
    private TaxonNode getParent(UUID parentUuid, String row) {
615
        if(parentUuid == null){
616
            logger.warn(row + "Parent uuid is null. No parent found.");
617
            return null;
618
        }
619
        TaxonBase<?> pTaxon = getTaxonService().find(parentUuid);
620
        if (pTaxon == null){
621
            logger.warn(row + "No parent found for parent UUID. This should not happen.");
622
            return null;
623
        }
624
        if (pTaxon.isInstanceOf(Synonym.class)){
625
            logger.warn(row + "Parent is synonym");
626
            return null;
627
        }else{
628
            Taxon ptax = CdmBase.deproxy(pTaxon, Taxon.class);
629
            if(ptax.getTaxonNodes().isEmpty()){
630
                logger.info(row + "Parent has no node yet");
631
                TaxonNode newParent = getClassification().addChildTaxon(ptax, null, null);
632
                getTaxonNodeService().saveOrUpdate(newParent);
633
                return newParent;
634
            }else {
635
                if(ptax.getTaxonNodes().size()>1){
636
                    logger.warn("Parent has >1 nodes. Take arbitrary one");
637
                }
638
                return ptax.getTaxonNodes().iterator().next();
639
            }
640
        }
641
    }
642

    
643
    private void addChild(TaxonNode parent, Taxon child, String row) {
644
        if (parent == null){
645
            logger.warn(row + "Parent is null");
646
        }else if (child == null){
647
            logger.warn(row + "Child is null");
648
        }else{
649
            if (!child.getTaxonNodes().isEmpty()){
650
                TaxonNode childNode = child.getTaxonNodes().iterator().next();
651
                if (childNode.getParent() != null && childNode.getParent().equals(parent)){
652
                    logger.info(row + "Parent-child relation exists already.");
653
                }else{
654
                    logger.warn(row + "Child already has different parent. Parent-child relation not added.");
655
                }
656
            }else{
657
                TaxonNode node = parent.addChildTaxon(child, null, null);
658
                getTaxonNodeService().saveOrUpdate(node);
659
            }
660
        }
661
    }
662

    
663
    private TaxonNode getFamily(String line, String family){
664
        UUID uuid;
665
        if ("Salicaceae".equals(family)){
666
            uuid = UUID.fromString("5432a4eb-2fbe-4494-925d-d01743ed435f");
667
//        }else if ("Meliaceae".equals(family)){
668
//            //Note: not needed, genus with family Meliaceae is synonym
669
//            uuid = UUID.fromString("c8694910-bfec-45a1-8901-2a0a2a6f12b1");
670
        }else{
671
            logger.warn(line + "Family not yet handled: " + family);
672
            return null;
673
        }
674
        TaxonNode familyNode = getTaxonNodeService().find(uuid);
675
        if (familyNode == null){
676
            familyNode = createFamily(family, uuid);
677
        }
678
        return familyNode;
679
    }
680

    
681
    private TaxonNode createFamily(String family, UUID uuid) {
682
        Classification classification = getClassification();
683
        TaxonName name = TaxonNameFactory.NewBotanicalInstance(Rank.FAMILY());
684
        name.setGenusOrUninomial(family);
685
        Taxon taxon = Taxon.NewInstance(name, getSecRef());
686
        TaxonNode result = classification.addChildTaxon(taxon, null, null);
687
        result.setUuid(uuid);
688
        getTaxonNodeService().saveOrUpdate(result);
689
        return result;
690
    }
691

    
692
    private Classification getClassification() {
693
        Classification classification = getClassificationService().find(state.getConfig().getClassificationUuid());
694
        if (classification == null){
695
            classification = Classification.NewInstance(
696
                    state.getConfig().getClassificationName(), getSecRef(), Language.LATIN());
697
            classification.setUuid(state.getConfig().getClassificationUuid());
698
            getClassificationService().save(classification);
699
        }
700
        return classification;
701
    }
702

    
703
    private TaxonNode unresolvedParent(){
704
        UUID uuid = UUID.fromString("1c48b8d3-077d-4aef-9e41-d4d3e0abd4c7");
705
        TaxonNode unresolvedParent = getTaxonNodeService().find(uuid);
706
        if (unresolvedParent == null){
707
            unresolvedParent = createFamily("Unresolved", uuid);
708
        }
709
        return unresolvedParent;
710
    }
711
}
(3-3/4)