Project

General

Profile

Download (39.5 KB) Statistics
| Branch: | Tag: | Revision:
1
 /**
2
 * Copyright (C) 2007 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

    
10
package eu.etaxonomy.cdm.io.excel.taxa;
11

    
12
import java.net.MalformedURLException;
13
import java.net.URISyntaxException;
14
import java.util.Arrays;
15
import java.util.HashMap;
16
import java.util.HashSet;
17
import java.util.List;
18
import java.util.Map;
19
import java.util.Set;
20
import java.util.UUID;
21

    
22
import org.apache.commons.lang.StringUtils;
23
import org.apache.log4j.Logger;
24
import org.springframework.stereotype.Component;
25

    
26
import eu.etaxonomy.cdm.common.CdmUtils;
27
import eu.etaxonomy.cdm.common.URI;
28
import eu.etaxonomy.cdm.io.common.TdwgAreaProvider;
29
import eu.etaxonomy.cdm.io.excel.common.ExcelRowBase.SourceDataHolder;
30
import eu.etaxonomy.cdm.io.tcsrdf.TcsRdfTransformer;
31
import eu.etaxonomy.cdm.model.agent.Team;
32
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
33
import eu.etaxonomy.cdm.model.common.CdmBase;
34
import eu.etaxonomy.cdm.model.common.Extension;
35
import eu.etaxonomy.cdm.model.common.ExtensionType;
36
import eu.etaxonomy.cdm.model.common.Identifier;
37
import eu.etaxonomy.cdm.model.common.Language;
38
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
39
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
40
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
41
import eu.etaxonomy.cdm.model.description.Distribution;
42
import eu.etaxonomy.cdm.model.description.Feature;
43
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
44
import eu.etaxonomy.cdm.model.description.TaxonDescription;
45
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
46
import eu.etaxonomy.cdm.model.description.TextData;
47
import eu.etaxonomy.cdm.model.location.NamedArea;
48
import eu.etaxonomy.cdm.model.media.ExternalLinkType;
49
import eu.etaxonomy.cdm.model.media.Media;
50
import eu.etaxonomy.cdm.model.name.INonViralName;
51
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
52
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
53
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
54
import eu.etaxonomy.cdm.model.name.Rank;
55
import eu.etaxonomy.cdm.model.name.TaxonName;
56
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
57
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
58
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
59
import eu.etaxonomy.cdm.model.reference.Reference;
60
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
61
import eu.etaxonomy.cdm.model.reference.ReferenceType;
62
import eu.etaxonomy.cdm.model.taxon.Classification;
63
import eu.etaxonomy.cdm.model.taxon.Synonym;
64
import eu.etaxonomy.cdm.model.taxon.SynonymType;
65
import eu.etaxonomy.cdm.model.taxon.Taxon;
66
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
67
import eu.etaxonomy.cdm.model.term.DefinedTerm;
68
import eu.etaxonomy.cdm.persistence.dto.MergeResult;
69
import eu.etaxonomy.cdm.strategy.exceptions.StringNotParsableException;
70
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
71
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
72
import eu.etaxonomy.cdm.strategy.parser.TimePeriodParser;
73

    
74
/**
75
 * @author a.babadshanjan
76
 * @since 08.01.2009
77
 */
78

    
79
@Component
80
public class NormalExplicitImport extends TaxonExcelImportBase {
81
    private static final long serialVersionUID = 3642423349766191160L;
82

    
83
    private static final Logger logger = Logger.getLogger(NormalExplicitImport.class);
84

    
85
	public static Set<String> validMarkers = new HashSet<>(Arrays.asList(new String[]{"", "valid", "accepted", "a", "v", "t", "!"}));
86
	public static Set<String> synonymMarkers = new HashSet<>(Arrays.asList(new String[]{"**","invalid", "synonym", "s", "i"}));
87
	public static Set<String> nameStatusMarkers = new HashSet<>(Arrays.asList(new String[]{"illegitimate", "nom. rej.", "nom. cons."}));
88
	public static final UUID uuidRefExtension = UUID.fromString("a46533df-7a78-448f-9b80-36d087fbdf2a");
89

    
90
    private static final Object NOM_ILLEG = "illegitimate";
91
    private static final Object NOM_REJ = "nom. rej.";
92
    private static final Object NOM_CONS = "nom. cons.";
93

    
94

    
95
	@Override
96
	protected void analyzeSingleValue(KeyValue keyValue, TaxonExcelImportState state) {
97

    
98
		NormalExplicitRow normalExplicitRow = (NormalExplicitRow)state.getCurrentRow();
99
		String key = keyValue.key;
100
		String value = keyValue.value;
101
		Integer index = keyValue.index;
102
		if (((NormalExplicitImportConfigurator)state.getConfig()).getParentUUID() != null){
103
            normalExplicitRow.setParentId("0");
104
		}
105
//  Infraspecific rank  Infraspecific epithet   Authorship  Taxonomic status in TPL Nomenclatural status from original data source  Confidence level    Source  Source id   IPNI id Publication Collation   Page    Date    Accepted ID
106

    
107
		key = key.replace(" ","");
108
		if (key.equalsIgnoreCase(ID_COLUMN)) {
109
			//String ivalue = floatString2IntValue(value);
110
			normalExplicitRow.setId(value);
111

    
112
		} else if(key.equalsIgnoreCase(PARENT_ID_COLUMN) ) {
113
			//int ivalue = floatString2IntValue(value);
114
			normalExplicitRow.setParentId(value);
115

    
116
		} else if(key.equalsIgnoreCase(RANK_COLUMN)) {
117
			normalExplicitRow.setRank(value);
118

    
119
		} else if(key.trim().equalsIgnoreCase(SCIENTIFIC_NAME_COLUMN) || key.trim().equalsIgnoreCase(FULLNAME_COLUMN)) {
120
			normalExplicitRow.setScientificName(value);
121

    
122
		} else if(key.equalsIgnoreCase(AUTHOR_COLUMN)|| key.equalsIgnoreCase(AUTHORS_COLUMN) || key.equals(AUTHORSHIP_COLUMN)) {
123
			normalExplicitRow.setAuthor(value);
124
		}else if(key.equalsIgnoreCase(PUBLISHING_AUTHOR_COLUMN)) {
125
            normalExplicitRow.setPublishingAuthor(value);
126
		}else if(key.equalsIgnoreCase(BASIONYM_AUTHOR_COLUMN)) {
127
            normalExplicitRow.setBasionymAuthor(value);
128

    
129
		}else if(key.equalsIgnoreCase(BASIONYM_COLUMN)) {
130
            normalExplicitRow.setBasionym(value);
131
		}else if(key.trim().equalsIgnoreCase(NOMENCLATURAL_SYNONYM_COLUMN)) {
132
            normalExplicitRow.setSynonym(value);
133
		} else if(key.equalsIgnoreCase(REFERENCE_COLUMN) || key.equalsIgnoreCase(PUBLICATION_COLUMN)) {
134
			normalExplicitRow.setReference(value);
135

    
136
		} else if(key.equalsIgnoreCase(COLLATION_COLUMN)) {
137
            normalExplicitRow.setCollation(value);
138

    
139
        }else if(key.equalsIgnoreCase(PAGE_COLUMN)) {
140
            normalExplicitRow.setPage(value);
141

    
142
        }else if(key.equalsIgnoreCase(NAMESTATUS_COLUMN) || key.trim().startsWith(NOMENCLATURAL_STATUS_COLUMN)) {
143
			normalExplicitRow.setNameStatus(value);
144

    
145
		} else if(key.equalsIgnoreCase(VERNACULAR_NAME_COLUMN)) {
146
			normalExplicitRow.setCommonName(value);
147

    
148
		} else if(key.equalsIgnoreCase(LANGUAGE_COLUMN)) {
149
			normalExplicitRow.setLanguage(value);
150

    
151
		} else if(key.equalsIgnoreCase(TDWG_COLUMN) ) {
152
			//TODO replace still necessary?
153
			value = value.replace(".0", "");
154
			normalExplicitRow.putDistribution(index, value);
155

    
156
		} else if(key.equalsIgnoreCase(PROTOLOGUE_COLUMN)) {
157
			normalExplicitRow.putProtologue(index, value);
158

    
159
		} else if(key.equalsIgnoreCase(IMAGE_COLUMN)) {
160
			normalExplicitRow.putImage(index, value);
161

    
162
		} else if(key.equalsIgnoreCase(DATE_COLUMN) || key.equalsIgnoreCase(YEAR_COLUMN)|| key.equalsIgnoreCase(PUBLICATION_YEAR_COLUMN)) {
163
            normalExplicitRow.setDate(value);
164

    
165
        } else if(key.equalsIgnoreCase(FAMILY_COLUMN)) {
166
            normalExplicitRow.setFamily(value);
167
        } else if(key.equalsIgnoreCase(INFRA_FAMILY_COLUMN)) {
168
            normalExplicitRow.setInfraFamily(value);
169
        }else if(key.equalsIgnoreCase(GENUS_COLUMN)) {
170
            normalExplicitRow.setGenus(value);
171
        }else if(key.trim().equalsIgnoreCase(INFRA_GENUS_COLUMN.trim())) {
172
            normalExplicitRow.setInfraGenus(value);
173
        }else if(key.equalsIgnoreCase(SPECIES_COLUMN)) {
174
            normalExplicitRow.setSpecies(value);
175
        }else if(key.equalsIgnoreCase(INFRA_SPECIES_COLUMN) || key.equalsIgnoreCase(INFRA_SPECIES_EPITHET_COLUMN)) {
176
            normalExplicitRow.setInfraSpecies(value);
177
        }else if(key.equalsIgnoreCase(INFRA_SPECIES_RANK_COLUMN)) {
178
            normalExplicitRow.setInfraSpecies_Rank(value);
179
        } else if (key.equalsIgnoreCase(VERSION_COLUMN)){
180
            normalExplicitRow.setVersion(value);
181
        }else if (key.equalsIgnoreCase(ACCEPTED_ID_COLUMN)){
182
            normalExplicitRow.setAccepted_id(value);
183
        }else if (key.equalsIgnoreCase(TAXONOMIC_STATUS)){
184
            normalExplicitRow.setTaxonomicStatus(value);
185
        }else if (key.equalsIgnoreCase(IPNI_ID_COLUMN)){
186
            normalExplicitRow.setIpni_id(value);
187
        }else if (key.equalsIgnoreCase(SOURCE_COLUMN)){
188
            normalExplicitRow.setSource(value);
189
        } else if (key.equalsIgnoreCase(SOURCE_ID_COLUMN)){
190
            normalExplicitRow.setSource_Id(value);
191
        }
192

    
193

    
194
        else if(key.equalsIgnoreCase("!")) {
195
            //! = Legitimate, * = Illegitimate, ** = Invalid, *** = nom. rej., !! = nom. cons.
196
            if (value.equals("!")){
197
                normalExplicitRow.setNameStatus("accepted");
198
            } else if (value.equals("*")){
199
                normalExplicitRow.setNameStatus("illegitimate");
200
            } else if (value.equals("**")){
201
                normalExplicitRow.setNameStatus("invalid");
202
            } else if (value.equals("***")){
203
                normalExplicitRow.setNameStatus("nom. rej.");
204
            } else if (value.equals("!!")){
205
                normalExplicitRow.setNameStatus("nom. cons.");
206
            } else{
207
                normalExplicitRow.setNameStatus("accepted");
208
            }
209
        }else if(key.equalsIgnoreCase("Nomenclatural status from original data source")) {
210

    
211
            if (value.equals("Illegitimate")){
212
                normalExplicitRow.setNameStatus("illegitimate");
213
            } else if (value.equals("Invalid")){
214
                normalExplicitRow.setNameStatus("invalid");
215
            } else{
216
                normalExplicitRow.setNameStatus("accepted");
217
            }
218
        }else {
219
			if (analyzeFeatures(state, keyValue)){
220

    
221
			}else{
222
				String message = "Unexpected column header " + key;
223
				fireWarningEvent(message, state, 10);
224
				state.setUnsuccessfull();
225
				//logger.error(message);
226
			}
227
		}
228
		return;
229
	}
230

    
231

    
232
	/**
233
	 *  Create base taxa and add all information attached to it's name.
234
	 */
235
	@Override
236
    protected void firstPass(TaxonExcelImportState state) {
237

    
238
//		if (1==1){
239
//			return;
240
//		}
241
//		System.out.println("FP:" + state.getCurrentLine());
242
		Rank rank = null;
243
		NormalExplicitRow taxonDataHolder = (NormalExplicitRow)state.getCurrentRow();
244

    
245
		String rankStr = taxonDataHolder.getRank();
246
		String taxonNameStr = taxonDataHolder.getScientificName();
247
		String authorStr = taxonDataHolder.getAuthor();
248
		String publishingAuthor= taxonDataHolder.getPublishingAuthor();
249
		String basionymAuthor = taxonDataHolder.getBasionymAuthor();
250

    
251
		String referenceStr = taxonDataHolder.getReference();
252
		String nameStatus = taxonDataHolder.getNameStatus();
253
		String familyNameStr = taxonDataHolder.getFamily();
254
		String infraFamilyNameStr = taxonDataHolder.getInfraFamily();
255
		String genusNameStr = taxonDataHolder.getGenus();
256
		String infraGenusNameStr = taxonDataHolder.getInfraGenus();
257
		String speciesNameStr = taxonDataHolder.getSpecies();
258
		String infraSpeciesNameStr = taxonDataHolder.getInfraSpecies();
259

    
260
		String version = taxonDataHolder.getVersion();
261

    
262
		String ipni_id = taxonDataHolder.getIpni_id();
263
		String source = taxonDataHolder.getSource();
264
		String source_id = taxonDataHolder.getSource_Id();
265
		String taxonomicStatus = taxonDataHolder.getTaxonomicStatus();
266

    
267

    
268
		String dateStr = taxonDataHolder.getDate();
269
		String id = taxonDataHolder.getId();
270
		UUID cdmUuid = taxonDataHolder.getCdmUuid();
271

    
272
		TaxonBase<?> taxonBase = null;
273
		if (cdmUuid != null){
274
			taxonBase = getTaxonService().find(cdmUuid);
275
		}else{
276
			if (rankStr == null || StringUtils.isBlank(rankStr)){
277
			    if (taxonDataHolder.getInfraSpecies() != null){
278
			        if (taxonDataHolder.getInfraSpecies_Rank() !=  null || !StringUtils.isBlank(taxonDataHolder.getInfraSpecies_Rank()) ){
279
			            try {
280
                            rank = Rank.getRankByLatinNameOrIdInVoc(taxonDataHolder.getInfraSpecies_Rank());
281
                        } catch (UnknownCdmTypeException e) {
282
                            // TODO Auto-generated catch block
283
                            e.printStackTrace();
284
                        }
285
		            }else {
286
		                rank = Rank.INFRASPECIES();
287
		            }
288
		        } else if (taxonDataHolder.getSpecies()!= null){
289
		            rank = Rank.SPECIES();
290
		        } else if (taxonDataHolder.getInfraGenus() != null){
291
		            rank = Rank.INFRAGENUS();
292
		        } else if (taxonDataHolder.getGenus() != null){
293
                    rank = Rank.GENUS();
294
		        }
295

    
296
		    } else{
297
				// Rank
298
				try {
299
				    if (!StringUtils.isBlank(rankStr)) {
300
                        rank = Rank.getRankByLatinNameOrIdInVoc(rankStr);
301
                        }
302
				} catch (UnknownCdmTypeException ex) {
303
					try {
304
						rank = Rank.getRankByEnglishName(rankStr, state.getConfig().getNomenclaturalCode(), false);
305
					} catch (UnknownCdmTypeException e) {
306
					    try {
307
	                           rank = TcsRdfTransformer.rankString2Rank(rankStr);
308
	                       } catch (UnknownCdmTypeException e1) {
309
	                            // TODO Auto-generated catch block
310
	                            e1.printStackTrace();
311
	                       }
312

    
313
					}
314
				}
315
		    }
316
			if (StringUtils.isBlank(taxonNameStr )){
317

    
318
			    if (taxonDataHolder.getGenus() != null){
319
			        taxonNameStr = taxonDataHolder.getGenus();
320
			        if (taxonDataHolder.getSpecies() != null){
321
			            taxonNameStr += " " + taxonDataHolder.getSpecies();
322
			        }
323
			        if (taxonDataHolder.getInfraSpecies_Rank() != null){
324
                        taxonNameStr += " " + taxonDataHolder.getInfraSpecies_Rank();
325
                    }
326
			        if (taxonDataHolder.getInfraSpecies() != null){
327
                        taxonNameStr += " " + taxonDataHolder.getInfraSpecies();
328
                    }
329
			    }
330
			}
331
			if (StringUtils.isBlank(taxonNameStr )){
332
			    return;
333
			}
334
			 //taxon
335
            taxonBase = createTaxon(state, rank, taxonNameStr, authorStr, publishingAuthor, basionymAuthor, referenceStr, dateStr, nameStatus, taxonomicStatus);
336
		}
337

    
338
		if (taxonBase == null){
339
			String message = "Taxon is already in DB. Record will not be handled";
340
			fireWarningEvent(message, "Record: " + state.getCurrentLine(), 6);
341
			logger.warn(message);
342
			//state.setUnsuccessfull();
343
			return;
344
		}
345

    
346
		//protologue
347
		for (String protologue : taxonDataHolder.getProtologues()){
348
			try {
349
				URI uri = new URI(protologue);
350
				taxonBase.getName().addProtologue(uri, null, ExternalLinkType.Unknown);
351
			} catch (URISyntaxException e) {
352
				String warning = "URISyntaxException when trying to convert to URI: " + protologue;
353
				logger.error(warning);
354
				state.setUnsuccessfull();
355
			}
356
		}
357

    
358
		if (ipni_id != null){
359
		    taxonBase.getName().addIdentifier(Identifier.NewInstance(ipni_id, DefinedTerm.IDENTIFIER_NAME_IPNI()));
360
		}
361

    
362
		if (source != null){
363
		    Reference sourceRef = state.getReference(source);
364
		    if (sourceRef == null){
365
		        sourceRef = ReferenceFactory.newGeneric();
366
		        sourceRef.setTitleCache(source, true);
367
		        state.putReference(source, sourceRef);
368
		    }
369
		    taxonBase.addSource(OriginalSourceType.PrimaryTaxonomicSource, source_id,"Taxon" ,sourceRef, null);
370
		}
371

    
372
		//state.putTaxon(id, taxonBase);
373
		taxonBase = getTaxonService().save(taxonBase);
374
		taxonDataHolder.setCdmUuid(taxonBase.getUuid());
375
		if (id == "0"){
376
		    state.putTaxon(taxonNameStr, taxonBase);
377
		}else{
378
		    state.putTaxon(id, taxonBase);
379
		}
380
		return;
381
    }
382

    
383

    
384

    
385
	/**
386
	 *  Stores parent-child, synonym and common name relationships.
387
	 *  Adds all taxon related descriptive information (this is not done in the first pass
388
	 *  because the information may also be attached to a synonym).
389
	 */
390
	@Override
391
    protected void secondPass(TaxonExcelImportState state) {
392
		if (logger.isDebugEnabled()){logger.debug(state.getCurrentLine());}
393
		try {
394
			NormalExplicitRow taxonDataHolder = (NormalExplicitRow)state.getCurrentRow();
395
			String taxonNameStr = taxonDataHolder.getScientificName();
396
			String nameStatus = taxonDataHolder.getNameStatus();
397
			String commonNameStr = taxonDataHolder.getCommonName();
398

    
399
			String synonymNameStr = taxonDataHolder.getSynonym();
400
			String basionymNameStr = taxonDataHolder.getBasionym();
401
			String accepted_idStr = taxonDataHolder.getAccepted_id();
402

    
403
			String parentId = taxonDataHolder.getParentId();
404
			String childId = taxonDataHolder.getId();
405
			UUID cdmUuid = taxonDataHolder.getCdmUuid();
406
			Taxon acceptedTaxon = null;
407
			TaxonName nameUsedInSource = null;
408
			TaxonBase<?> taxonBase = null;
409
			Taxon parentTaxon = null;
410

    
411
			if (cdmUuid != null){
412
				taxonBase = getTaxonService().find(cdmUuid);
413
				if (taxonBase != null ){
414
                    acceptedTaxon = getAcceptedTaxon(taxonBase);
415
                    nameUsedInSource = taxonBase.getName();
416
				}
417
			} else{
418
			    taxonBase = state.getTaxonBase(childId);
419
			    if (accepted_idStr != null){
420
			        acceptedTaxon = (Taxon) state.getTaxonBase(accepted_idStr);
421
			    }
422

    
423
    			 if (parentId == "0" && state.getParent() == null){
424
                     parentTaxon =(Taxon) getTaxonService().load(((NormalExplicitImportConfigurator)state.getConfig()).getParentUUID());
425
                     state.setParent(parentTaxon);
426
                 }else if (parentId != "0"){
427
                    parentTaxon = CdmBase.deproxy(state.getTaxonBase(parentId), Taxon.class);
428
                 } else if (state.getParent() != null){
429
                     parentTaxon = state.getParent();
430
                 }
431
    			if (taxonBase != null ){
432
    			    if (acceptedTaxon == null){
433
    			        acceptedTaxon = getAcceptedTaxon(taxonBase);
434
    			    }
435
    				if (synonymNameStr != null){
436
    				    Synonym syn = createSynonym(state,taxonBase,synonymNameStr);
437
    				    acceptedTaxon.addSynonym(syn, SynonymType.HETEROTYPIC_SYNONYM_OF());
438
    				}
439
    				if (basionymNameStr != null){
440
    				    Synonym syn = createSynonym(state,taxonBase,basionymNameStr);
441
                        acceptedTaxon.addSynonym(syn, SynonymType.HOMOTYPIC_SYNONYM_OF());
442
                        syn.getName().addRelationshipToName(acceptedTaxon.getName(), NameRelationshipType.BASIONYM(), null, null);
443
    				}
444
    				nameUsedInSource = taxonBase.getName();
445

    
446
    			    //TODO error handling for class cast
447

    
448

    
449
    				nameUsedInSource = taxonBase.getName();
450
    				nameStatus = CdmUtils.Nz(nameStatus).trim().toLowerCase();
451
    				if (validMarkers.contains(nameStatus)  && accepted_idStr == null){
452
    						Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
453
    						acceptedTaxon = taxon;
454
    						// Add the parent relationship
455
    						//if (state.getCurrentRow().getParentId() != 0) {
456
    						MergeResult result = null;
457
    							if (parentTaxon != null) {
458
    								//Taxon taxon = (Taxon)state.getTaxonBase(childId);
459

    
460
    							//	Reference sourceRef = state.getConfig().getSourceReference();
461
    								String microCitation = null;
462
    								Taxon childTaxon = taxon;
463
    								makeParent(state, parentTaxon, childTaxon, null, microCitation);
464
    								getTaxonService().saveOrUpdate(childTaxon);
465
    								state.putTaxon(parentId, parentTaxon);
466
    							} else {
467
    								String message = "Taxonomic parent not found for " + taxonNameStr;
468
    								logger.warn(message);
469
    								fireWarningEvent(message, state, 6);
470
    								//state.setUnsuccessfull();
471
    							}
472
    //						}else{
473
    //							//do nothing (parent == 0) no parent exists
474
    //						}
475
    					}else if (synonymMarkers.contains(nameStatus) || accepted_idStr != null){
476
    						//add synonym relationship
477
    					    if (accepted_idStr == null){
478
    					        acceptedTaxon = parentTaxon;
479
    					    }
480
    						try {
481
    							Synonym synonym = CdmBase.deproxy(taxonBase,Synonym.class);
482
    							if (acceptedTaxon == null){
483
    								String message = "Accepted/valid taxon could not be found. Please check referential integrity.";
484
    								fireWarningEvent(message, state, 8);
485
    							}else{
486
    							   acceptedTaxon.addSynonym(synonym, SynonymType.SYNONYM_OF());
487
							       getTaxonService().saveOrUpdate(acceptedTaxon);
488

    
489
    							}
490
    						} catch (Exception e) {
491
    							String message = "Unhandled exception (%s) occurred during synonym import/update";
492
    							message = String.format(message, e.getMessage());
493
    							fireWarningEvent(message, state, 10);
494
    							state.setUnsuccessfull();
495
    						}
496

    
497
    					}else{
498
    					    if (parentTaxon != null) {
499
                                Taxon taxon = (Taxon)state.getTaxonBase(childId);
500

    
501
                               // Reference sourceRef = state.getConfig().getSourceReference();
502
                                String microCitation = null;
503
                                Taxon childTaxon = taxon;
504
                                makeParent(state, parentTaxon, childTaxon, null, microCitation);
505
                                getTaxonService().saveOrUpdate(parentTaxon);
506
                                state.putTaxon(parentId, parentTaxon);
507
                            } else {
508
                                String message = "Taxonomic parent not found for " + taxonNameStr;
509
                                logger.warn(message);
510
                                fireWarningEvent(message, state, 6);
511
                                //state.setUnsuccessfull();
512
                            }
513
    					}
514

    
515

    
516
    			}
517
    			if (StringUtils.isBlank(taxonNameStr) && acceptedTaxon == null) {
518
                    acceptedTaxon = parentTaxon;
519
                    nameUsedInSource = null;
520
                }
521
			}
522
			if (acceptedTaxon == null && (StringUtils.isNotBlank(commonNameStr) ||taxonDataHolder.getFeatures().size() > 0 )){
523
				String message = "Accepted taxon could not be found. Can't add additional data (common names, descriptive data, ...) to taxon";
524
				fireWarningEvent(message, state, 6);
525
			}else{
526
				//common names
527
				if (StringUtils.isNotBlank(commonNameStr)){			// add common name to taxon
528
					handleCommonName(state, taxonNameStr, commonNameStr, acceptedTaxon);
529
				}
530

    
531

    
532
				//media
533
				for (String imageUrl : taxonDataHolder.getImages()){
534
					TaxonDescription td = acceptedTaxon.getImageGallery(true);
535
					DescriptionElementBase mediaHolder;
536
					if (td.getElements().size() != 0){
537
						mediaHolder = td.getElements().iterator().next();
538
					}else{
539
						mediaHolder = TextData.NewInstance(Feature.IMAGE());
540
						td.addElement(mediaHolder);
541
					}
542
					try {
543
						Media media = getImageMedia(imageUrl, READ_MEDIA_DATA);
544
						mediaHolder.addMedia(media);
545
					} catch (MalformedURLException e) {
546
						logger.warn("Can't add media: " + e.getMessage());
547
						state.setUnsuccessfull();
548
					}
549
				}
550

    
551
				//tdwg label
552
				for (String tdwg : taxonDataHolder.getDistributions()){
553
					TaxonDescription td = this.getTaxonDescription(acceptedTaxon, state.getConfig().getSourceReference() ,false, true);
554
					NamedArea area = TdwgAreaProvider.getAreaByTdwgAbbreviation(tdwg);
555
					if (area == null){
556
						area = TdwgAreaProvider.getAreaByTdwgLabel(tdwg);
557
					}
558
					if (area != null){
559
						Distribution distribution = Distribution.NewInstance(area, PresenceAbsenceTerm.PRESENT());
560
						td.addElement(distribution);
561
					}else{
562
						String message = "TDWG area could not be recognized: " + tdwg;
563
						logger.warn(message);
564
						state.setUnsuccessfull();
565
					}
566
				}
567

    
568
				//features
569
				handleFeatures(state, taxonDataHolder, acceptedTaxon, nameUsedInSource);
570
			}
571
		} catch (Exception e) {
572
			e.printStackTrace();
573
		}
574
		return;
575
	}
576

    
577

    
578
	/**
579
     * @param state
580
     * @param taxonBase
581
     * @param synonymNameStr
582
     */
583
    private Synonym createSynonym(TaxonExcelImportState state, TaxonBase<?> taxonBase, String synonymNameStr) {
584
        NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
585
        TaxonName name = null;
586
        if (nc.isKindOf(NomenclaturalCode.ICZN)){
587
            name = TaxonNameFactory.NewZoologicalInstance(taxonBase.getName().getRank());
588
        }else if (nc.isKindOf(NomenclaturalCode.ICNAFP)){
589
            name = TaxonNameFactory.NewBotanicalInstance(taxonBase.getName().getRank());
590
        } else{
591
            name = TaxonNameFactory.NewNonViralInstance(taxonBase.getName().getRank());
592
        }
593
        name.setTitleCache(synonymNameStr, true);
594
        if (name != null){
595
            return Synonym.NewInstance(name, null);
596
        }
597
        logger.debug("The nomenclatural code is not supported.");
598
        return null;
599
    }
600

    
601

    
602
    /**
603
	 * @param state
604
	 * @param taxonDataHolder
605
	 * @param acceptedTaxon
606
	 */
607
	private void handleFeatures(TaxonExcelImportState state, NormalExplicitRow taxonDataHolder, Taxon acceptedTaxon, TaxonName nameUsedInSource) {
608
		//feature
609
		for (UUID featureUuid : taxonDataHolder.getFeatures()){
610
			Feature feature = getFeature(state, featureUuid);
611
			List<String> textList = taxonDataHolder.getFeatureTexts(featureUuid);
612
			List<String> languageList = taxonDataHolder.getFeatureLanguages(featureUuid);
613

    
614
			for (int i = 0; i < textList.size(); i++){
615
				String featureText = textList.get(i);
616
				String featureLanguage = languageList == null ? null :languageList.get(i);
617
				Language language = getFeatureLanguage(featureLanguage, state);
618
				//TODO
619
				TaxonDescription td = this.getTaxonDescription(acceptedTaxon, state.getConfig().getSourceReference() ,false, true);
620
				TextData textData = TextData.NewInstance(feature);
621
				textData.putText(language, featureText);
622
				td.addElement(textData);
623

    
624
				SourceDataHolder sourceDataHolder = taxonDataHolder.getFeatureTextReferences(featureUuid, i);
625
				List<Map<SourceType, String>> sourceList = sourceDataHolder.getSources();
626
				for (Map<SourceType, String> sourceMap : sourceList){
627

    
628
					//ref
629
					Reference ref = ReferenceFactory.newGeneric();
630
					boolean refExists = false; //in case none of the ref fields exists, the ref should not be added
631
					for (SourceType type : sourceMap.keySet()){
632
						String value = sourceMap.get(type);
633
						if (type.equals(SourceType.Author)){
634
							TeamOrPersonBase<?> author = getAuthorAccordingToConfig(value, state);
635
							ref.setAuthorship(author);
636
						}else if (type.equals(SourceType.Title)) {
637
							ref.setTitle(value);
638
						}else if (type.equals(SourceType.Year)) {
639
							ref.setDatePublished(TimePeriodParser.parseStringVerbatim(value));
640
						}else if (type.equals(SourceType.RefExtension)) {
641
							ExtensionType extensionType = getExtensionType(state, uuidRefExtension, "RefExtension", "Reference Extension", "RefExt.");
642
							Extension extension = Extension.NewInstance(ref, value, extensionType);
643
						}
644
						refExists = true;
645
					}
646
					DescriptionElementSource source = DescriptionElementSource.NewInstance(OriginalSourceType.PrimaryTaxonomicSource);
647
					if (refExists){
648
						ref = getReferenceAccordingToConfig(ref, state);
649
						source.setCitation(ref);
650
						source.setNameUsedInSource(nameUsedInSource);
651
					}
652
					textData.addSource(source);
653
				}
654
			}
655
		}
656
	}
657

    
658
	private final Map<String, UUID> referenceMapping = new HashMap<String, UUID>();
659
	private final Map<UUID, Reference> referenceStore = new HashMap<UUID, Reference>();
660

    
661
	private Reference getReferenceAccordingToConfig(Reference value, TaxonExcelImportState state) {
662
		Reference result = null;
663
		String titleCache = value.getTitleCache();
664
		UUID referenceUuid = referenceMapping.get(titleCache);
665
		if (referenceUuid != null){
666
			result = referenceStore.get(referenceUuid);
667
		}
668
		if (result == null){
669
			result = value;
670
			referenceStore.put(result.getUuid(), result);
671
		}
672
		if (referenceUuid == null){
673
			referenceMapping.put(titleCache, result.getUuid());
674
		}
675
		return result;
676
	}
677

    
678

    
679
	private final Map<String, UUID> authorMapping = new HashMap<String, UUID>();
680
	private final Map<UUID, TeamOrPersonBase> authorStore = new HashMap<UUID, TeamOrPersonBase>();
681

    
682
	private TeamOrPersonBase<?> getAuthorAccordingToConfig(String value, TaxonExcelImportState state) {
683
		TeamOrPersonBase<?> result = null;
684
		UUID authorUuid = authorMapping.get(value);
685
		if (authorUuid != null){
686
			result = authorStore.get(authorUuid);
687
		}
688
		if (result == null){
689
			//TODO parsing
690
			TeamOrPersonBase<?> author = Team.NewInstance();
691
			author.setTitleCache(value, true);
692
			result = author;
693
			authorStore.put(result.getUuid(), result);
694
		}
695
		if (authorUuid == null){
696
			authorMapping.put(value, result.getUuid());
697
		}
698
		return result;
699
	}
700

    
701

    
702
	private final Map<String, UUID> languageMapping = new HashMap<String, UUID>();
703

    
704
	private Language getFeatureLanguage(String featureLanguage, TaxonExcelImportState state) {
705
		if (StringUtils.isBlank(featureLanguage)){
706
			return null;
707
		}
708
		UUID languageUuid = languageMapping.get(featureLanguage);
709
		if (languageUuid == null){
710
			Language result = getTermService().getLanguageByIso(featureLanguage);
711
			languageUuid = result.getUuid();
712
			languageMapping.put(featureLanguage, languageUuid);
713
		}
714
		Language result = getLanguage(state, languageUuid, null, null, null);
715
		return result;
716
	}
717

    
718

    
719
	/**
720
	 * @param state
721
	 * @param taxonNameStr
722
	 * @param commonNameStr
723
	 * @param parentId
724
	 */
725
	private void handleCommonName(TaxonExcelImportState state,
726
			String taxonNameStr, String commonNameStr, Taxon acceptedTaxon) {
727
		Language language = getTermService().getLanguageByIso(((NormalExplicitRow)state.getCurrentRow()).getLanguage());
728
		if (language == null && StringUtils.isNotBlank(((NormalExplicitRow)state.getCurrentRow()).getLanguage())  ){
729
			String error ="Language is null but shouldn't";
730
			logger.error(error);
731
			throw new IllegalArgumentException(error);
732
		}
733
		CommonTaxonName commonTaxonName = CommonTaxonName.NewInstance(commonNameStr, language);
734
		try {
735
			TaxonDescription taxonDescription = getTaxonDescription(acceptedTaxon, false, true);
736
			taxonDescription.addElement(commonTaxonName);
737
			logger.info("Common name " + commonNameStr + " added to " + acceptedTaxon.getTitleCache());
738
		} catch (ClassCastException ex) {
739
			logger.error(taxonNameStr + " is not a taxon instance.");
740
		}
741
	}
742

    
743

    
744
	/**
745
	 * @param state
746
	 * @param rank
747
	 * @param taxonNameStr
748
	 * @param authorStr
749
	 * @param nameStatus
750
	 * @param nameStatus2
751
	 * @return
752
	 */
753
	private TaxonBase createTaxon(TaxonExcelImportState state, Rank rank,
754
	        String familyNameStr, String infraFamilyNameStr, String genusNameStr, String infraGenusNameStr, String speciesNameStr, String infraSpeciesNameStr, String authorStr, String publishingAuthorStr, String basionymAuthorStr,String reference, String date, String nameStatus, String taxonomicStatus) {
755
		// Create the taxon name object depending on the setting of the nomenclatural code
756
		// in the configurator (botanical code, zoological code, etc.)
757

    
758
		NomenclaturalCode nc = getConfigurator().getNomenclaturalCode();
759

    
760
		TaxonBase taxonBase;
761
		String nameCache = null;
762
		if (rank == null){
763
		    System.err.println("bla");
764
		}
765
		if (rank.isGenus()){
766
		    nameCache =genusNameStr;
767
		} else if (rank.isInfraGeneric()){
768
		    nameCache =CdmUtils.concat(" " +rank.getIdInVocabulary() + " ",genusNameStr,infraGenusNameStr);
769

    
770
        } else if (rank.isSpecies()){
771
            nameCache = CdmUtils.concat(" ", genusNameStr,speciesNameStr);
772
        } else if (rank.isInfraSpecific()){
773
            nameCache = CdmUtils.concat(" " +rank.getIdInVocabulary() + " ",genusNameStr,infraGenusNameStr);
774
        }
775
		if (! synonymMarkers.contains(nameStatus)  && state.getConfig().isReuseExistingTaxaWhenPossible()){
776
			taxonBase = getTaxonService().findBestMatchingTaxon(nameCache);
777
		}else{
778
			taxonBase = getTaxonService().findBestMatchingSynonym(nameCache, INCLUDE_UNPUBLISHED);
779
			if (taxonBase != null){
780
				logger.info("Matching taxon/synonym found for " + nameCache);
781
			}
782
		}
783
		if (taxonBase != null){
784
			logger.info("Matching taxon/synonym found for " + nameCache);
785
			return null;
786
		}else {
787
			taxonBase = createTaxon(state, rank, nameCache, authorStr, publishingAuthorStr, basionymAuthorStr, reference, date, nameStatus, taxonomicStatus, nc);
788
		}
789
		return taxonBase;
790
	}
791

    
792
	/**
793
     * @param state
794
     * @param rank
795
     * @param taxonNameStr
796
     * @param authorStr
797
     * @param nameStatus
798
     * @param nameStatus2
799
     * @return
800
     */
801
    private TaxonBase createTaxon(TaxonExcelImportState state, Rank rank,
802
            String taxonNameStr, String authorStr, String publishingAuthorStr, String basionymAuthorStr,String reference, String date, String nameStatus, String taxonomicStatus) {
803
        // Create the taxon name object depending on the setting of the nomenclatural code
804
        // in the configurator (botanical code, zoological code, etc.)
805
        if (StringUtils.isBlank(taxonNameStr)){
806
            return null;
807
        }
808
        NomenclaturalCode nc = getConfigurator().getNomenclaturalCode();
809

    
810
        TaxonBase taxonBase = null;
811

    
812
        String titleCache = CdmUtils.concat(" ", taxonNameStr, authorStr);
813
        if (! synonymMarkers.contains(nameStatus)  && state.getConfig().isReuseExistingTaxaWhenPossible()){
814
            titleCache = CdmUtils.concat(" ", taxonNameStr, authorStr);
815
            taxonBase = getTaxonService().findBestMatchingTaxon(titleCache);
816
        }else if ( state.getConfig().isReuseExistingTaxaWhenPossible()){
817
            taxonBase = getTaxonService().findBestMatchingSynonym(titleCache, INCLUDE_UNPUBLISHED);
818
            if (taxonBase != null){
819
                logger.info("Matching taxon/synonym found for " + titleCache);
820
            }
821
        }
822
        if (taxonBase != null && taxonBase.getName().getTitleCache().equals(CdmUtils.concat(" ", taxonNameStr, authorStr))){
823
            logger.info("Matching taxon/synonym found for " + titleCache + " - "+taxonBase.getTitleCache());
824
            return null;
825
        }else {
826
            taxonBase = createTaxon(state, rank, taxonNameStr, authorStr, publishingAuthorStr, basionymAuthorStr, reference, date, nameStatus, taxonomicStatus, nc);
827
        }
828
        return taxonBase;
829
    }
830

    
831

    
832

    
833
	/**
834
	 * @param state
835
	 * @param rank
836
	 * @param taxonNameStr
837
	 * @param authorStr
838
	 * @param nameStatus
839
	 * @param nameStatus2
840
	 * @param nc
841
	 * @return
842
	 */
843
	private TaxonBase<?> createTaxon(TaxonExcelImportState state, Rank rank, String taxonNameStr,
844
			String authorStr, String publishingAutorStr, String basionymAuthor, String reference, String date, String nameStatus, String taxonomicStatus, NomenclaturalCode nc) {
845
		TaxonBase<?> taxonBase;
846
		INonViralName taxonName = null;
847
		if (nc == NomenclaturalCode.ICVCN){
848
			logger.warn("ICVCN not yet supported");
849

    
850
		}else{
851
		    //String taxonNameStr = titleCache.substring(0, titleCache.indexOf(authorStr));
852
			taxonName = nc.getNewTaxonNameInstance(rank);
853
			NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
854
			taxonName = parser.parseFullName(taxonNameStr, nc, rank);
855

    
856
			if (! taxonName.getNameCache().equals(taxonNameStr)){
857
				taxonName.setNameCache(taxonNameStr, true);
858
			}
859

    
860
			// Create the author
861
			if (StringUtils.isNotBlank(authorStr)) {
862
				try {
863
					parser.parseAuthors(taxonName, authorStr);
864
				} catch (StringNotParsableException e) {
865
					taxonName.setAuthorshipCache(authorStr);
866
 				}
867
			}
868
			if (StringUtils.isNotBlank(reference)) {
869
			    String pub = CdmUtils.concat(" ", reference, ((NormalExplicitRow)state.getCurrentRow()).getCollation());
870
			    String[] split = pub.split(":");
871
			    pub = split[0];
872

    
873
			    INomenclaturalReference ref = state.getReference(pub);
874
			    if (ref == null){
875
			        ref = parser.parseReferenceTitle(pub, date, true);
876
			        state.putReference(pub, (Reference) ref);
877
			    }
878
			    if (split.length > 1){
879
                    String detail = split[split.length-1];
880
                    taxonName.setNomenclaturalMicroReference(detail.trim());
881

    
882
                }
883

    
884
             //   ref.setAbbrevTitle(pub);
885

    
886

    
887
			    if (ref.getAuthorship() == null){
888
			        ref.setAuthorship(taxonName.getCombinationAuthorship());
889
			    }
890

    
891
			    if (ref.getAbbrevTitle() == null && !ref.isOfType(ReferenceType.Article)) {
892
                    ref.setAbbrevTitle(reference);
893
                    ref.setProtectedAbbrevTitleCache(false);
894
                }
895

    
896
                ref.setProtectedTitleCache(false);
897
                taxonName.setNomenclaturalReference(ref);
898
			  //  taxonName.setNomenclaturalMicroReference(state.getCurrentRow().getCollation());
899
			}
900
		}
901

    
902
		//Create the taxon
903
		//Reference sec = state.getConfig().getSourceReference();
904
		// Create the status
905
		nameStatus = CdmUtils.Nz(nameStatus).trim().toLowerCase();
906
		taxonomicStatus = CdmUtils.Nz(taxonomicStatus).trim().toLowerCase();
907
		if (synonymMarkers.contains(nameStatus) || synonymMarkers.contains(taxonomicStatus)){
908
            taxonBase = Synonym.NewInstance(taxonName, null);
909
        }else if (validMarkers.contains(nameStatus)){
910
			taxonBase = Taxon.NewInstance(taxonName, null);
911
		}else  {
912
			Taxon taxon = Taxon.NewInstance(taxonName, null);
913
			if (nameStatusMarkers.contains(nameStatus)){
914
			    if (nameStatus.equals(NOM_ILLEG)){
915
			        taxonName.addStatus(NomenclaturalStatusType.ILLEGITIMATE(), null, null);
916
			    } else if (nameStatus.equals(NOM_REJ)){
917
			        taxonName.addStatus(NomenclaturalStatusType.REJECTED(), null, null);
918
			    } else if (nameStatus.equals(NOM_CONS)){
919
                    taxonName.addStatus(NomenclaturalStatusType.CONSERVED(), null, null);
920
                }
921
			}else{
922
			    taxon.setTaxonStatusUnknown(true);
923
			}
924
			taxonBase = taxon;
925
		}
926
		taxonBase.getName().addSource(OriginalSourceType.Import, null,"TaxonName" ,state.getConfig().getSourceReference(), null);
927

    
928
		taxonBase.addSource(OriginalSourceType.Import, null,"TaxonName" ,state.getConfig().getSourceReference(), null);
929

    
930
		return taxonBase;
931
	}
932

    
933
	/**
934
	 * @param taxon
935
	 * @return
936
	 */
937
	//TODO implementation must be improved when matching of taxon names with existing names is implemented
938
	//=> the assumption that the only description is the description added by this import
939
	//is wrong then
940
	private TaxonNameDescription getNameDescription(TaxonName name, TaxonExcelImportState state) {
941
		Set<TaxonNameDescription> descriptions = name.getDescriptions();
942
		if (descriptions.size()>1){
943
			throw new IllegalStateException("Implementation does not yet support names with multiple descriptions");
944
		}else if (descriptions.size()==1){
945
			return descriptions.iterator().next();
946
		}else{
947
		    TaxonNameDescription desc = TaxonNameDescription.NewInstance(name);
948
		    desc.addSource(OriginalSourceType.Import, null, "NameDescription", state.getConfig().getSourceReference(), null);
949
			return desc;
950
		}
951
	}
952

    
953
	private void makeParent(TaxonExcelImportState state, Taxon parentTaxon, Taxon childTaxon, Reference citation, String microCitation){
954
		Reference sec = state.getConfig().getSourceReference();
955

    
956
//		Reference sec = parentTaxon.getSec();
957
		Classification tree = state.getClassification();
958
		if (tree == null){
959
			//tree = makeTree(state, sec);
960
		    if (state.getConfig().getClassificationUuid() != null){
961
		        tree = getClassificationService().load(state.getConfig().getClassificationUuid());
962
		        state.setClassification(tree);
963
		    }
964
		    if (tree == null){
965
		        tree = makeTree(state, sec);
966
		        getClassificationService().save(tree);
967
		        state.setClassification(tree);
968
		    }
969
		}
970
		//if (sec.equals(childTaxon.getSec())){
971
		    boolean success =  (null !=  tree.addParentChild(parentTaxon, childTaxon, citation, microCitation));
972
			if (success == false){
973
				state.setUnsuccessfull();
974
			}
975
//		}else{
976
//			logger.warn("No relationship added for child " + childTaxon.getTitleCache());
977
//		}
978
		return;
979
	}
980

    
981

    
982
	/* (non-Javadoc)
983
	 * @see eu.etaxonomy.cdm.io.excel.common.ExcelTaxonOrSpecimenImportBase#createDataHolderRow()
984
	 */
985
	@Override
986
	protected NormalExplicitRow createDataHolderRow() {
987
		return new NormalExplicitRow();
988
	}
989

    
990

    
991

    
992
	/* (non-Javadoc)
993
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
994
	 */
995
	@Override
996
	protected boolean doCheck(TaxonExcelImportState state) {
997
		logger.warn("DoCheck not yet implemented for NormalExplicitImport");
998
		return true;
999
	}
1000

    
1001
	/* (non-Javadoc)
1002
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
1003
	 */
1004
	@Override
1005
	protected boolean isIgnore(TaxonExcelImportState state) {
1006
		return false;
1007
	}
1008

    
1009

    
1010

    
1011
}
(2-2/8)