Project

General

Profile

Download (39.7 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.URI;
14
import java.net.URISyntaxException;
15
import java.util.Arrays;
16
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.Set;
21
import java.util.UUID;
22

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

    
27
import eu.etaxonomy.cdm.common.CdmUtils;
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.DefinedTerm;
35
import eu.etaxonomy.cdm.model.common.Extension;
36
import eu.etaxonomy.cdm.model.common.ExtensionType;
37
import eu.etaxonomy.cdm.model.common.Identifier;
38
import eu.etaxonomy.cdm.model.common.Language;
39
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
40
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
41
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
42
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
43
import eu.etaxonomy.cdm.model.description.Distribution;
44
import eu.etaxonomy.cdm.model.description.Feature;
45
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
46
import eu.etaxonomy.cdm.model.description.TaxonDescription;
47
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
48
import eu.etaxonomy.cdm.model.description.TextData;
49
import eu.etaxonomy.cdm.model.location.NamedArea;
50
import eu.etaxonomy.cdm.model.media.Media;
51
import eu.etaxonomy.cdm.model.name.INonViralName;
52
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
53
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
54
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
55
import eu.etaxonomy.cdm.model.name.Rank;
56
import eu.etaxonomy.cdm.model.name.TaxonName;
57
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
58
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
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.persistence.dto.MergeResult;
68
import eu.etaxonomy.cdm.strategy.exceptions.StringNotParsableException;
69
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
70
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
71
import eu.etaxonomy.cdm.strategy.parser.TimePeriodParser;
72

    
73
/**
74
 * @author a.babadshanjan
75
 * @created 08.01.2009
76
 */
77

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

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

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

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

    
93

    
94
	/* (non-Javadoc)
95
	 * @see eu.etaxonomy.cdm.io.excel.common.ExcelTaxonOrSpecimenImportBase#analyzeSingleValue(eu.etaxonomy.cdm.io.excel.common.ExcelTaxonOrSpecimenImportBase.KeyValue, eu.etaxonomy.cdm.io.excel.common.ExcelImportState)
96
	 */
97
	@Override
98
	protected void analyzeSingleValue(KeyValue keyValue, TaxonExcelImportState state) {
99

    
100
		NormalExplicitRow normalExplicitRow = state.getCurrentRow();
101
		String key = keyValue.key;
102
		String value = keyValue.value;
103
		Integer index = keyValue.index;
104
		if (((NormalExplicitImportConfigurator)state.getConfig()).getParentUUID() != null){
105
            normalExplicitRow.setParentId("0");
106
		}
107
//  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
108

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

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

    
118
		} else if(key.equalsIgnoreCase(RANK_COLUMN)) {
119
			normalExplicitRow.setRank(value);
120

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

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

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

    
138
		} else if(key.equalsIgnoreCase(COLLATION_COLUMN)) {
139
            normalExplicitRow.setCollation(value);
140

    
141
        }else if(key.equalsIgnoreCase(PAGE_COLUMN)) {
142
            normalExplicitRow.setPage(value);
143

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

    
147
		} else if(key.equalsIgnoreCase(VERNACULAR_NAME_COLUMN)) {
148
			normalExplicitRow.setCommonName(value);
149

    
150
		} else if(key.equalsIgnoreCase(LANGUAGE_COLUMN)) {
151
			normalExplicitRow.setLanguage(value);
152

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

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

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

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

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

    
195

    
196

    
197

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

    
215
            if (value.equals("Illegitimate")){
216
                normalExplicitRow.setNameStatus("illegitimate");
217
            } else if (value.equals("Invalid")){
218
                normalExplicitRow.setNameStatus("invalid");
219
            } else{
220
                normalExplicitRow.setNameStatus("accepted");
221
            }
222
        }else {
223
			if (analyzeFeatures(state, keyValue)){
224

    
225
			}else{
226
				String message = "Unexpected column header " + key;
227
				fireWarningEvent(message, state, 10);
228
				state.setUnsuccessfull();
229
				//logger.error(message);
230
			}
231
		}
232
		return;
233
	}
234

    
235

    
236
	/**
237
	 *  Create base taxa and add all information attached to it's name.
238
	 */
239
	@Override
240
    protected void firstPass(TaxonExcelImportState state) {
241

    
242
//		if (1==1){
243
//			return;
244
//		}
245
//		System.out.println("FP:" + state.getCurrentLine());
246
		Rank rank = null;
247
		NormalExplicitRow taxonDataHolder = state.getCurrentRow();
248

    
249
		String rankStr = taxonDataHolder.getRank();
250
		String taxonNameStr = taxonDataHolder.getScientificName();
251
		String authorStr = taxonDataHolder.getAuthor();
252
		String publishingAuthor= taxonDataHolder.getPublishingAuthor();
253
		String basionymAuthor = taxonDataHolder.getBasionymAuthor();
254

    
255
		String referenceStr = taxonDataHolder.getReference();
256
		String nameStatus = taxonDataHolder.getNameStatus();
257
		String familyNameStr = taxonDataHolder.getFamily();
258
		String infraFamilyNameStr = taxonDataHolder.getInfraFamily();
259
		String genusNameStr = taxonDataHolder.getGenus();
260
		String infraGenusNameStr = taxonDataHolder.getInfraGenus();
261
		String speciesNameStr = taxonDataHolder.getSpecies();
262
		String infraSpeciesNameStr = taxonDataHolder.getInfraSpecies();
263

    
264
		String version = taxonDataHolder.getVersion();
265

    
266
		String ipni_id = taxonDataHolder.getIpni_id();
267
		String source = taxonDataHolder.getSource();
268
		String source_id = taxonDataHolder.getSource_Id();
269
		String taxonomicStatus = taxonDataHolder.getTaxonomicStatus();
270

    
271

    
272
		String dateStr = taxonDataHolder.getDate();
273
		String id = taxonDataHolder.getId();
274
		UUID cdmUuid = taxonDataHolder.getCdmUuid();
275

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

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

    
317
					}
318
				}
319
		    }
320
			if (StringUtils.isBlank(taxonNameStr )){
321

    
322
			    if (taxonDataHolder.getGenus() != null){
323
			        taxonNameStr = taxonDataHolder.getGenus();
324
			        if (taxonDataHolder.getSpecies() != null){
325
			            taxonNameStr += " " + taxonDataHolder.getSpecies();
326
			        }
327
			        if (taxonDataHolder.getInfraSpecies_Rank() != null){
328
                        taxonNameStr += " " + taxonDataHolder.getInfraSpecies_Rank();
329
                    }
330
			        if (taxonDataHolder.getInfraSpecies() != null){
331
                        taxonNameStr += " " + taxonDataHolder.getInfraSpecies();
332
                    }
333
			    }
334
			}
335
			if (StringUtils.isBlank(taxonNameStr )){
336
			    return;
337
			}
338
			 //taxon
339
            taxonBase = createTaxon(state, rank, taxonNameStr, authorStr, publishingAuthor, basionymAuthor, referenceStr, dateStr, nameStatus, taxonomicStatus);
340
            taxonBase.getName().addSource(OriginalSourceType.Import, taxonDataHolder.getId(),"TaxonName" ,state.getConfig().getSourceReference(), null);
341

    
342
            taxonBase.addSource(OriginalSourceType.Import, taxonDataHolder.getId(),"TaxonName" ,state.getConfig().getSourceReference(), null);
343

    
344
		}
345

    
346
		if (taxonBase == null){
347
			String message = "Taxon is already in DB. Record will not be handled";
348
			fireWarningEvent(message, "Record: " + state.getCurrentLine(), 6);
349
			logger.warn(message);
350
			//state.setUnsuccessfull();
351
			return;
352
		}
353

    
354
		//protologue
355
		for (String protologue : taxonDataHolder.getProtologues()){
356
			TextData textData = TextData.NewInstance(Feature.PROTOLOGUE());
357
			this.getNameDescription(taxonBase.getName(), state).addElement(textData);
358
			URI uri;
359
			try {
360
				uri = new URI(protologue);
361
				textData.addMedia(Media.NewInstance(uri, null, null, null));
362

    
363
			} catch (URISyntaxException e) {
364
				String warning = "URISyntaxException when trying to convert to URI: " + protologue;
365
				logger.error(warning);
366
				state.setUnsuccessfull();
367
			}
368
		}
369

    
370
		if (ipni_id != null){
371
		    taxonBase.getName().addIdentifier(Identifier.NewInstance(ipni_id, DefinedTerm.IPNI_NAME_IDENTIFIER()));
372
		}
373

    
374
		if (source != null){
375
		    Reference sourceRef = state.getReference(source);
376
		    if (sourceRef == null){
377
		        sourceRef = ReferenceFactory.newGeneric();
378
		        sourceRef.setTitleCache(source, true);
379
		        state.putReference(source, sourceRef);
380
		    }
381
		    taxonBase.addSource(OriginalSourceType.PrimaryTaxonomicSource, source_id,"Taxon" ,sourceRef, null);
382
		}
383

    
384
		//state.putTaxon(id, taxonBase);
385
		taxonBase = getTaxonService().save(taxonBase);
386
		taxonDataHolder.setCdmUuid(taxonBase.getUuid());
387
		if (id == "0"){
388
		    state.putTaxon(taxonNameStr, taxonBase);
389
		}else{
390
		    state.putTaxon(id, taxonBase);
391
		}
392
		return;
393
    }
394

    
395

    
396

    
397
	/**
398
	 *  Stores parent-child, synonym and common name relationships.
399
	 *  Adds all taxon related descriptive information (this is not done in the first pass
400
	 *  because the information may also be attached to a synonym).
401
	 */
402
	@Override
403
    protected void secondPass(TaxonExcelImportState state) {
404
		if (logger.isDebugEnabled()){logger.debug(state.getCurrentLine());}
405
		try {
406
			NormalExplicitRow taxonDataHolder = state.getCurrentRow();
407
			String taxonNameStr = taxonDataHolder.getScientificName();
408
			String nameStatus = taxonDataHolder.getNameStatus();
409
			String commonNameStr = taxonDataHolder.getCommonName();
410

    
411
			String synonymNameStr = taxonDataHolder.getSynonym();
412
			String basionymNameStr = taxonDataHolder.getBasionym();
413
			String accepted_idStr = taxonDataHolder.getAccepted_id();
414

    
415
			String parentId = taxonDataHolder.getParentId();
416
			String childId = taxonDataHolder.getId();
417
			UUID cdmUuid = taxonDataHolder.getCdmUuid();
418
			Taxon acceptedTaxon = null;
419
			TaxonName nameUsedInSource = null;
420
			TaxonBase<?> taxonBase = null;
421
			Taxon parentTaxon = null;
422

    
423
			if (cdmUuid != null){
424
				taxonBase = getTaxonService().find(cdmUuid);
425
				if (taxonBase != null ){
426
                    acceptedTaxon = getAcceptedTaxon(taxonBase);
427
                    nameUsedInSource = taxonBase.getName();
428
				}
429
			} else{
430
			    taxonBase = state.getTaxonBase(childId);
431
			    if (accepted_idStr != null){
432
			        acceptedTaxon = (Taxon) state.getTaxonBase(accepted_idStr);
433
			    }
434

    
435
    			 if (parentId == "0" && state.getParent() == null){
436
                     parentTaxon =(Taxon) getTaxonService().load(((NormalExplicitImportConfigurator)state.getConfig()).getParentUUID());
437
                     state.setParent(parentTaxon);
438
                 }else if (parentId != "0"){
439
                    parentTaxon = CdmBase.deproxy(state.getTaxonBase(parentId), Taxon.class);
440
                 } else if (state.getParent() != null){
441
                     parentTaxon = state.getParent();
442
                 }
443
    			if (taxonBase != null ){
444
    			    if (acceptedTaxon == null){
445
    			        acceptedTaxon = getAcceptedTaxon(taxonBase);
446
    			    }
447
    				if (synonymNameStr != null){
448
    				    Synonym syn = createSynonym(state,taxonBase,synonymNameStr);
449
    				    acceptedTaxon.addSynonym(syn, SynonymType.HETEROTYPIC_SYNONYM_OF());
450
    				}
451
    				if (basionymNameStr != null){
452
    				    Synonym syn = createSynonym(state,taxonBase,basionymNameStr);
453
                        acceptedTaxon.addSynonym(syn, SynonymType.HOMOTYPIC_SYNONYM_OF());
454
                        syn.getName().addRelationshipToName(acceptedTaxon.getName(), NameRelationshipType.BASIONYM(), null);
455
    				}
456
    				nameUsedInSource = taxonBase.getName();
457

    
458
    			    //TODO error handling for class cast
459

    
460

    
461
    				nameUsedInSource = taxonBase.getName();
462
    				nameStatus = CdmUtils.Nz(nameStatus).trim().toLowerCase();
463
    				if (validMarkers.contains(nameStatus)  && accepted_idStr == null){
464
    						Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
465
    						acceptedTaxon = taxon;
466
    						// Add the parent relationship
467
    						//if (state.getCurrentRow().getParentId() != 0) {
468
    						MergeResult result = null;
469
    							if (parentTaxon != null) {
470
    								//Taxon taxon = (Taxon)state.getTaxonBase(childId);
471

    
472
    							//	Reference sourceRef = state.getConfig().getSourceReference();
473
    								String microCitation = null;
474
    								Taxon childTaxon = taxon;
475
    								makeParent(state, parentTaxon, childTaxon, null, microCitation);
476
    								getTaxonService().saveOrUpdate(childTaxon);
477
    								state.putTaxon(parentId, parentTaxon);
478
    							} else {
479
    								String message = "Taxonomic parent not found for " + taxonNameStr;
480
    								logger.warn(message);
481
    								fireWarningEvent(message, state, 6);
482
    								//state.setUnsuccessfull();
483
    							}
484
    //						}else{
485
    //							//do nothing (parent == 0) no parent exists
486
    //						}
487
    					}else if (synonymMarkers.contains(nameStatus) || accepted_idStr != null){
488
    						//add synonym relationship
489
    					    if (accepted_idStr == null){
490
    					        acceptedTaxon = parentTaxon;
491
    					    }
492
    						try {
493
    							Synonym synonym = CdmBase.deproxy(taxonBase,Synonym.class);
494
    							if (acceptedTaxon == null){
495
    								String message = "Accepted/valid taxon could not be found. Please check referential integrity.";
496
    								fireWarningEvent(message, state, 8);
497
    							}else{
498
    							   acceptedTaxon.addSynonym(synonym, SynonymType.SYNONYM_OF());
499
							       getTaxonService().saveOrUpdate(acceptedTaxon);
500

    
501
    							}
502
    						} catch (Exception e) {
503
    							String message = "Unhandled exception (%s) occurred during synonym import/update";
504
    							message = String.format(message, e.getMessage());
505
    							fireWarningEvent(message, state, 10);
506
    							state.setUnsuccessfull();
507
    						}
508

    
509
    					}else{
510
    					    if (parentTaxon != null) {
511
                                Taxon taxon = (Taxon)state.getTaxonBase(childId);
512

    
513
                               // Reference sourceRef = state.getConfig().getSourceReference();
514
                                String microCitation = null;
515
                                Taxon childTaxon = taxon;
516
                                makeParent(state, parentTaxon, childTaxon, null, microCitation);
517
                                getTaxonService().saveOrUpdate(parentTaxon);
518
                                state.putTaxon(parentId, parentTaxon);
519
                            } else {
520
                                String message = "Taxonomic parent not found for " + taxonNameStr;
521
                                logger.warn(message);
522
                                fireWarningEvent(message, state, 6);
523
                                //state.setUnsuccessfull();
524
                            }
525
    					}
526

    
527

    
528
    			}
529
    			if (StringUtils.isBlank(taxonNameStr) && acceptedTaxon == null) {
530
                    acceptedTaxon = parentTaxon;
531
                    nameUsedInSource = null;
532
                }
533
			}
534
			if (acceptedTaxon == null && (StringUtils.isNotBlank(commonNameStr) ||taxonDataHolder.getFeatures().size() > 0 )){
535
				String message = "Accepted taxon could not be found. Can't add additional data (common names, descriptive data, ...) to taxon";
536
				fireWarningEvent(message, state, 6);
537
			}else{
538
				//common names
539
				if (StringUtils.isNotBlank(commonNameStr)){			// add common name to taxon
540
					handleCommonName(state, taxonNameStr, commonNameStr, acceptedTaxon);
541
				}
542

    
543

    
544
				//media
545
				for (String imageUrl : taxonDataHolder.getImages()){
546
					TaxonDescription td = acceptedTaxon.getImageGallery(true);
547
					DescriptionElementBase mediaHolder;
548
					if (td.getElements().size() != 0){
549
						mediaHolder = td.getElements().iterator().next();
550
					}else{
551
						mediaHolder = TextData.NewInstance(Feature.IMAGE());
552
						td.addElement(mediaHolder);
553
					}
554
					try {
555
						Media media = getImageMedia(imageUrl, READ_MEDIA_DATA);
556
						mediaHolder.addMedia(media);
557
					} catch (MalformedURLException e) {
558
						logger.warn("Can't add media: " + e.getMessage());
559
						state.setUnsuccessfull();
560
					}
561
				}
562

    
563
				//tdwg label
564
				for (String tdwg : taxonDataHolder.getDistributions()){
565
					TaxonDescription td = this.getTaxonDescription(acceptedTaxon, state.getConfig().getSourceReference() ,false, true);
566
					NamedArea area = TdwgAreaProvider.getAreaByTdwgAbbreviation(tdwg);
567
					if (area == null){
568
						area = TdwgAreaProvider.getAreaByTdwgLabel(tdwg);
569
					}
570
					if (area != null){
571
						Distribution distribution = Distribution.NewInstance(area, PresenceAbsenceTerm.PRESENT());
572
						td.addElement(distribution);
573
					}else{
574
						String message = "TDWG area could not be recognized: " + tdwg;
575
						logger.warn(message);
576
						state.setUnsuccessfull();
577
					}
578
				}
579

    
580
				//features
581
				handleFeatures(state, taxonDataHolder, acceptedTaxon, nameUsedInSource);
582
			}
583
		} catch (Exception e) {
584
			e.printStackTrace();
585
		}
586
		return;
587
	}
588

    
589

    
590
	/**
591
     * @param state
592
     * @param taxonBase
593
     * @param synonymNameStr
594
     */
595
    private Synonym createSynonym(TaxonExcelImportState state, TaxonBase<?> taxonBase, String synonymNameStr) {
596
        NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
597
        TaxonName name = null;
598
        if (nc.isKindOf(NomenclaturalCode.ICZN)){
599
            name = TaxonNameFactory.NewZoologicalInstance(taxonBase.getName().getRank());
600
        }else if (nc.isKindOf(NomenclaturalCode.ICNAFP)){
601
            name = TaxonNameFactory.NewBotanicalInstance(taxonBase.getName().getRank());
602
        } else{
603
            name = TaxonNameFactory.NewNonViralInstance(taxonBase.getName().getRank());
604
        }
605
        name.setTitleCache(synonymNameStr, true);
606
        if (name != null){
607
            return Synonym.NewInstance(name, null);
608
        }
609
        logger.debug("The nomenclatural code is not supported.");
610
        return null;
611
    }
612

    
613

    
614
    /**
615
	 * @param state
616
	 * @param taxonDataHolder
617
	 * @param acceptedTaxon
618
	 */
619
	private void handleFeatures(TaxonExcelImportState state, NormalExplicitRow taxonDataHolder, Taxon acceptedTaxon, TaxonName nameUsedInSource) {
620
		//feature
621
		for (UUID featureUuid : taxonDataHolder.getFeatures()){
622
			Feature feature = getFeature(state, featureUuid);
623
			List<String> textList = taxonDataHolder.getFeatureTexts(featureUuid);
624
			List<String> languageList = taxonDataHolder.getFeatureLanguages(featureUuid);
625

    
626
			for (int i = 0; i < textList.size(); i++){
627
				String featureText = textList.get(i);
628
				String featureLanguage = languageList == null ? null :languageList.get(i);
629
				Language language = getFeatureLanguage(featureLanguage, state);
630
				//TODO
631
				TaxonDescription td = this.getTaxonDescription(acceptedTaxon, state.getConfig().getSourceReference() ,false, true);
632
				TextData textData = TextData.NewInstance(feature);
633
				textData.putText(language, featureText);
634
				td.addElement(textData);
635

    
636
				SourceDataHolder sourceDataHolder = taxonDataHolder.getFeatureTextReferences(featureUuid, i);
637
				List<Map<SourceType, String>> sourceList = sourceDataHolder.getSources();
638
				for (Map<SourceType, String> sourceMap : sourceList){
639

    
640
					//ref
641
					Reference ref = ReferenceFactory.newGeneric();
642
					boolean refExists = false; //in case none of the ref fields exists, the ref should not be added
643
					for (SourceType type : sourceMap.keySet()){
644
						String value = sourceMap.get(type);
645
						if (type.equals(SourceType.Author)){
646
							TeamOrPersonBase<?> author = getAuthorAccordingToConfig(value, state);
647
							ref.setAuthorship(author);
648
						}else if (type.equals(SourceType.Title)) {
649
							ref.setTitle(value);
650
						}else if (type.equals(SourceType.Year)) {
651
							ref.setDatePublished(TimePeriodParser.parseString(value));
652
						}else if (type.equals(SourceType.RefExtension)) {
653
							ExtensionType extensionType = getExtensionType(state, uuidRefExtension, "RefExtension", "Reference Extension", "RefExt.");
654
							Extension extension = Extension.NewInstance(ref, value, extensionType);
655
						}
656
						refExists = true;
657
					}
658
					DescriptionElementSource source = DescriptionElementSource.NewInstance(OriginalSourceType.PrimaryTaxonomicSource);
659
					if (refExists){
660
						ref = getReferenceAccordingToConfig(ref, state);
661
						source.setCitation(ref);
662
						source.setNameUsedInSource(nameUsedInSource);
663
					}
664
					textData.addSource(source);
665
				}
666
			}
667
		}
668
	}
669

    
670
	private final Map<String, UUID> referenceMapping = new HashMap<String, UUID>();
671
	private final Map<UUID, Reference> referenceStore = new HashMap<UUID, Reference>();
672

    
673
	private Reference getReferenceAccordingToConfig(Reference value, TaxonExcelImportState state) {
674
		Reference result = null;
675
		String titleCache = value.getTitleCache();
676
		UUID referenceUuid = referenceMapping.get(titleCache);
677
		if (referenceUuid != null){
678
			result = referenceStore.get(referenceUuid);
679
		}
680
		if (result == null){
681
			result = value;
682
			referenceStore.put(result.getUuid(), result);
683
		}
684
		if (referenceUuid == null){
685
			referenceMapping.put(titleCache, result.getUuid());
686
		}
687
		return result;
688
	}
689

    
690

    
691
	private final Map<String, UUID> authorMapping = new HashMap<String, UUID>();
692
	private final Map<UUID, TeamOrPersonBase> authorStore = new HashMap<UUID, TeamOrPersonBase>();
693

    
694
	private TeamOrPersonBase<?> getAuthorAccordingToConfig(String value, TaxonExcelImportState state) {
695
		TeamOrPersonBase<?> result = null;
696
		UUID authorUuid = authorMapping.get(value);
697
		if (authorUuid != null){
698
			result = authorStore.get(authorUuid);
699
		}
700
		if (result == null){
701
			//TODO parsing
702
			TeamOrPersonBase<?> author = Team.NewInstance();
703
			author.setTitleCache(value, true);
704
			result = author;
705
			authorStore.put(result.getUuid(), result);
706
		}
707
		if (authorUuid == null){
708
			authorMapping.put(value, result.getUuid());
709
		}
710
		return result;
711
	}
712

    
713

    
714
	private final Map<String, UUID> languageMapping = new HashMap<String, UUID>();
715

    
716
	private Language getFeatureLanguage(String featureLanguage, TaxonExcelImportState state) {
717
		if (StringUtils.isBlank(featureLanguage)){
718
			return null;
719
		}
720
		UUID languageUuid = languageMapping.get(featureLanguage);
721
		if (languageUuid == null){
722
			Language result = getTermService().getLanguageByIso(featureLanguage);
723
			languageUuid = result.getUuid();
724
			languageMapping.put(featureLanguage, languageUuid);
725
		}
726
		Language result = getLanguage(state, languageUuid, null, null, null);
727
		return result;
728
	}
729

    
730

    
731
	/**
732
	 * @param state
733
	 * @param taxonNameStr
734
	 * @param commonNameStr
735
	 * @param parentId
736
	 */
737
	private void handleCommonName(TaxonExcelImportState state,
738
			String taxonNameStr, String commonNameStr, Taxon acceptedTaxon) {
739
		Language language = getTermService().getLanguageByIso(state.getCurrentRow().getLanguage());
740
		if (language == null && CdmUtils.isNotEmpty(state.getCurrentRow().getLanguage())  ){
741
			String error ="Language is null but shouldn't";
742
			logger.error(error);
743
			throw new IllegalArgumentException(error);
744
		}
745
		CommonTaxonName commonTaxonName = CommonTaxonName.NewInstance(commonNameStr, language);
746
		try {
747
			TaxonDescription taxonDescription = getTaxonDescription(acceptedTaxon, false, true);
748
			taxonDescription.addElement(commonTaxonName);
749
			logger.info("Common name " + commonNameStr + " added to " + acceptedTaxon.getTitleCache());
750
		} catch (ClassCastException ex) {
751
			logger.error(taxonNameStr + " is not a taxon instance.");
752
		}
753
	}
754

    
755

    
756
	/**
757
	 * @param state
758
	 * @param rank
759
	 * @param taxonNameStr
760
	 * @param authorStr
761
	 * @param nameStatus
762
	 * @param nameStatus2
763
	 * @return
764
	 */
765
	private TaxonBase createTaxon(TaxonExcelImportState state, Rank rank,
766
	        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) {
767
		// Create the taxon name object depending on the setting of the nomenclatural code
768
		// in the configurator (botanical code, zoological code, etc.)
769

    
770
		NomenclaturalCode nc = getConfigurator().getNomenclaturalCode();
771

    
772
		TaxonBase taxonBase;
773
		String nameCache = null;
774
		if (rank == null){
775
		    System.err.println("bla");
776
		}
777
		if (rank.isGenus()){
778
		    nameCache =genusNameStr;
779
		} else if (rank.isInfraGeneric()){
780
		    nameCache =CdmUtils.concat(" " +rank.getIdInVocabulary() + " ",genusNameStr,infraGenusNameStr);
781

    
782
        } else if (rank.isSpecies()){
783
            nameCache = CdmUtils.concat(" ", genusNameStr,speciesNameStr);
784
        } else if (rank.isInfraSpecific()){
785
            nameCache = CdmUtils.concat(" " +rank.getIdInVocabulary() + " ",genusNameStr,infraGenusNameStr);
786
        }
787
		if (! synonymMarkers.contains(nameStatus)  && state.getConfig().isReuseExistingTaxaWhenPossible()){
788
			taxonBase = getTaxonService().findBestMatchingTaxon(nameCache);
789
		}else{
790
			taxonBase = getTaxonService().findBestMatchingSynonym(nameCache);
791
			if (taxonBase != null){
792
				logger.info("Matching taxon/synonym found for " + nameCache);
793
			}
794
		}
795
		if (taxonBase != null){
796
			logger.info("Matching taxon/synonym found for " + nameCache);
797
			return null;
798
		}else {
799
			taxonBase = createTaxon(state, rank, nameCache, authorStr, publishingAuthorStr, basionymAuthorStr, reference, date, nameStatus, taxonomicStatus, nc);
800
		}
801
		return taxonBase;
802
	}
803

    
804
	/**
805
     * @param state
806
     * @param rank
807
     * @param taxonNameStr
808
     * @param authorStr
809
     * @param nameStatus
810
     * @param nameStatus2
811
     * @return
812
     */
813
    private TaxonBase createTaxon(TaxonExcelImportState state, Rank rank,
814
            String taxonNameStr, String authorStr, String publishingAuthorStr, String basionymAuthorStr,String reference, String date, String nameStatus, String taxonomicStatus) {
815
        // Create the taxon name object depending on the setting of the nomenclatural code
816
        // in the configurator (botanical code, zoological code, etc.)
817
        if (StringUtils.isBlank(taxonNameStr)){
818
            return null;
819
        }
820
        NomenclaturalCode nc = getConfigurator().getNomenclaturalCode();
821

    
822
        TaxonBase taxonBase = null;
823

    
824
        String titleCache = CdmUtils.concat(" ", taxonNameStr, authorStr);
825
        if (! synonymMarkers.contains(nameStatus)  && state.getConfig().isReuseExistingTaxaWhenPossible()){
826
            titleCache = CdmUtils.concat(" ", taxonNameStr, authorStr);
827
            taxonBase = getTaxonService().findBestMatchingTaxon(titleCache);
828
        }else if ( state.getConfig().isReuseExistingTaxaWhenPossible()){
829
            taxonBase = getTaxonService().findBestMatchingSynonym(titleCache);
830
            if (taxonBase != null){
831
                logger.info("Matching taxon/synonym found for " + titleCache);
832
            }
833
        }
834
        if (taxonBase != null && taxonBase.getName().getTitleCache().equals(CdmUtils.concat(" ", taxonNameStr, authorStr))){
835
            logger.info("Matching taxon/synonym found for " + titleCache + " - "+taxonBase.getTitleCache());
836
            return null;
837
        }else {
838
            taxonBase = createTaxon(state, rank, taxonNameStr, authorStr, publishingAuthorStr, basionymAuthorStr, reference, date, nameStatus, taxonomicStatus, nc);
839
        }
840
        return taxonBase;
841
    }
842

    
843

    
844

    
845
	/**
846
	 * @param state
847
	 * @param rank
848
	 * @param taxonNameStr
849
	 * @param authorStr
850
	 * @param nameStatus
851
	 * @param nameStatus2
852
	 * @param nc
853
	 * @return
854
	 */
855
	private TaxonBase<?> createTaxon(TaxonExcelImportState state, Rank rank, String taxonNameStr,
856
			String authorStr, String publishingAutorStr, String basionymAuthor, String reference, String date, String nameStatus, String taxonomicStatus, NomenclaturalCode nc) {
857
		TaxonBase<?> taxonBase;
858
		INonViralName taxonName = null;
859
		if (nc == NomenclaturalCode.ICVCN){
860
			logger.warn("ICVCN not yet supported");
861

    
862
		}else{
863
		    //String taxonNameStr = titleCache.substring(0, titleCache.indexOf(authorStr));
864
			taxonName = nc.getNewTaxonNameInstance(rank);
865
			NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
866
			taxonName = parser.parseFullName(taxonNameStr, nc, rank);
867

    
868
			if (! taxonName.getNameCache().equals(taxonNameStr)){
869
				taxonName.setNameCache(taxonNameStr, true);
870
			}
871

    
872
			// Create the author
873
			if (StringUtils.isNotBlank(authorStr)) {
874
				try {
875
					parser.parseAuthors(taxonName, authorStr);
876
				} catch (StringNotParsableException e) {
877
					taxonName.setAuthorshipCache(authorStr);
878
 				}
879
			}
880
			if (StringUtils.isNotBlank(reference)) {
881
			    String pub = CdmUtils.concat(" ", reference, state.getCurrentRow().getCollation());
882
			    String[] split = pub.split(":");
883
			    pub = split[0];
884

    
885
			    INomenclaturalReference ref = state.getReference(pub);
886
			    if (ref == null){
887
			        ref = parser.parseReferenceTitle(pub, date, true);
888
			        state.putReference(pub, (Reference) ref);
889
			    }
890
			    if (split.length > 1){
891
                    String detail = split[split.length-1];
892
                    taxonName.setNomenclaturalMicroReference(detail.trim());
893

    
894
                }
895

    
896
             //   ref.setAbbrevTitle(pub);
897

    
898

    
899
			    if (ref.getAuthorship() == null){
900
			        ref.setAuthorship(taxonName.getCombinationAuthorship());
901
			    }
902

    
903
			    if (ref.getAbbrevTitle() == null && !ref.isOfType(ReferenceType.Article)) {
904
                    ref.setAbbrevTitle(reference);
905
                    ref.setProtectedAbbrevTitleCache(false);
906
                }
907

    
908
                ref.setProtectedTitleCache(false);
909
                taxonName.setNomenclaturalReference(ref);
910
			  //  taxonName.setNomenclaturalMicroReference(state.getCurrentRow().getCollation());
911
			}
912
		}
913

    
914
		//Create the taxon
915
		//Reference sec = state.getConfig().getSourceReference();
916
		// Create the status
917
		nameStatus = CdmUtils.Nz(nameStatus).trim().toLowerCase();
918
		taxonomicStatus = CdmUtils.Nz(taxonomicStatus).trim().toLowerCase();
919
		if (synonymMarkers.contains(nameStatus) || synonymMarkers.contains(taxonomicStatus)){
920
            taxonBase = Synonym.NewInstance(taxonName, null);
921
        }else if (validMarkers.contains(nameStatus)){
922
			taxonBase = Taxon.NewInstance(taxonName, null);
923
		}else  {
924
			Taxon taxon = Taxon.NewInstance(taxonName, null);
925
			if (nameStatusMarkers.contains(nameStatus)){
926
			    if (nameStatus.equals(NOM_ILLEG)){
927
			        taxonName.addStatus(NomenclaturalStatusType.ILLEGITIMATE(), null, null);
928
			    } else if (nameStatus.equals(NOM_REJ)){
929
			        taxonName.addStatus(NomenclaturalStatusType.REJECTED(), null, null);
930
			    } else if (nameStatus.equals(NOM_CONS)){
931
                    taxonName.addStatus(NomenclaturalStatusType.CONSERVED(), null, null);
932
                }
933
			}else{
934
			    taxon.setTaxonStatusUnknown(true);
935
			}
936
			taxonBase = taxon;
937
		}
938

    
939
		return taxonBase;
940
	}
941

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

    
962
	private void makeParent(TaxonExcelImportState state, Taxon parentTaxon, Taxon childTaxon, Reference citation, String microCitation){
963
		Reference sec = state.getConfig().getSourceReference();
964

    
965
//		Reference sec = parentTaxon.getSec();
966
		Classification tree = state.getClassification();
967
		if (tree == null){
968
			//tree = makeTree(state, sec);
969
		    if (state.getConfig().getClassificationUuid() != null){
970
		        tree = getClassificationService().load(state.getConfig().getClassificationUuid());
971
		        state.setClassification(tree);
972
		    }
973
		    if (tree == null){
974
		        tree = makeTree(state, sec);
975
		        getClassificationService().save(tree);
976
		        state.setClassification(tree);
977
		    }
978
		}
979
		//if (sec.equals(childTaxon.getSec())){
980
		    boolean success =  (null !=  tree.addParentChild(parentTaxon, childTaxon, citation, microCitation));
981
			if (success == false){
982
				state.setUnsuccessfull();
983
			}
984
//		}else{
985
//			logger.warn("No relationship added for child " + childTaxon.getTitleCache());
986
//		}
987
		return;
988
	}
989

    
990

    
991
	/* (non-Javadoc)
992
	 * @see eu.etaxonomy.cdm.io.excel.common.ExcelTaxonOrSpecimenImportBase#createDataHolderRow()
993
	 */
994
	@Override
995
	protected NormalExplicitRow createDataHolderRow() {
996
		return new NormalExplicitRow();
997
	}
998

    
999

    
1000

    
1001
	/* (non-Javadoc)
1002
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
1003
	 */
1004
	@Override
1005
	protected boolean doCheck(TaxonExcelImportState state) {
1006
		logger.warn("DoCheck not yet implemented for NormalExplicitImport");
1007
		return true;
1008
	}
1009

    
1010
	/* (non-Javadoc)
1011
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
1012
	 */
1013
	@Override
1014
	protected boolean isIgnore(TaxonExcelImportState state) {
1015
		return false;
1016
	}
1017

    
1018

    
1019

    
1020
}
(1-1/5)