Project

General

Profile

Download (39.6 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.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.Media;
49
import eu.etaxonomy.cdm.model.name.INonViralName;
50
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
51
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
52
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
53
import eu.etaxonomy.cdm.model.name.Rank;
54
import eu.etaxonomy.cdm.model.name.TaxonName;
55
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
56
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
57
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
58
import eu.etaxonomy.cdm.model.reference.Reference;
59
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
60
import eu.etaxonomy.cdm.model.reference.ReferenceType;
61
import eu.etaxonomy.cdm.model.taxon.Classification;
62
import eu.etaxonomy.cdm.model.taxon.Synonym;
63
import eu.etaxonomy.cdm.model.taxon.SynonymType;
64
import eu.etaxonomy.cdm.model.taxon.Taxon;
65
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
66
import eu.etaxonomy.cdm.model.term.DefinedTerm;
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
 * @since 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<>(Arrays.asList(new String[]{"", "valid", "accepted", "a", "v", "t", "!"}));
85
	public static Set<String> synonymMarkers = new HashSet<>(Arrays.asList(new String[]{"**","invalid", "synonym", "s", "i"}));
86
	public static Set<String> nameStatusMarkers = new HashSet<>(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
	@Override
95
	protected void analyzeSingleValue(KeyValue keyValue, TaxonExcelImportState state) {
96

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
192

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

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

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

    
230

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

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

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

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

    
259
		String version = taxonDataHolder.getVersion();
260

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

    
266

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

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

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

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

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

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

    
345
		//protologue
346
		for (String protologue : taxonDataHolder.getProtologues()){
347
			TextData textData = TextData.NewInstance(Feature.PROTOLOGUE());
348
			this.getNameDescription(taxonBase.getName(), state).addElement(textData);
349
			URI uri;
350
			try {
351
				uri = new URI(protologue);
352
				textData.addMedia(Media.NewInstance(uri, null, null, null));
353

    
354
			} catch (URISyntaxException e) {
355
				String warning = "URISyntaxException when trying to convert to URI: " + protologue;
356
				logger.error(warning);
357
				state.setUnsuccessfull();
358
			}
359
		}
360

    
361
		if (ipni_id != null){
362
		    taxonBase.getName().addIdentifier(Identifier.NewInstance(ipni_id, DefinedTerm.IDENTIFIER_NAME_IPNI()));
363
		}
364

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

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

    
386

    
387

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

    
402
			String synonymNameStr = taxonDataHolder.getSynonym();
403
			String basionymNameStr = taxonDataHolder.getBasionym();
404
			String accepted_idStr = taxonDataHolder.getAccepted_id();
405

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

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

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

    
449
    			    //TODO error handling for class cast
450

    
451

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

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

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

    
500
    					}else{
501
    					    if (parentTaxon != null) {
502
                                Taxon taxon = (Taxon)state.getTaxonBase(childId);
503

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

    
518

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

    
534

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

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

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

    
580

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

    
604

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

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

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

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

    
661
	private final Map<String, UUID> referenceMapping = new HashMap<String, UUID>();
662
	private final Map<UUID, Reference> referenceStore = new HashMap<UUID, Reference>();
663

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

    
681

    
682
	private final Map<String, UUID> authorMapping = new HashMap<String, UUID>();
683
	private final Map<UUID, TeamOrPersonBase> authorStore = new HashMap<UUID, TeamOrPersonBase>();
684

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

    
704

    
705
	private final Map<String, UUID> languageMapping = new HashMap<String, UUID>();
706

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

    
721

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

    
746

    
747
	/**
748
	 * @param state
749
	 * @param rank
750
	 * @param taxonNameStr
751
	 * @param authorStr
752
	 * @param nameStatus
753
	 * @param nameStatus2
754
	 * @return
755
	 */
756
	private TaxonBase createTaxon(TaxonExcelImportState state, Rank rank,
757
	        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) {
758
		// Create the taxon name object depending on the setting of the nomenclatural code
759
		// in the configurator (botanical code, zoological code, etc.)
760

    
761
		NomenclaturalCode nc = getConfigurator().getNomenclaturalCode();
762

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

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

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

    
813
        TaxonBase taxonBase = null;
814

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

    
834

    
835

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

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

    
859
			if (! taxonName.getNameCache().equals(taxonNameStr)){
860
				taxonName.setNameCache(taxonNameStr, true);
861
			}
862

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

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

    
885
                }
886

    
887
             //   ref.setAbbrevTitle(pub);
888

    
889

    
890
			    if (ref.getAuthorship() == null){
891
			        ref.setAuthorship(taxonName.getCombinationAuthorship());
892
			    }
893

    
894
			    if (ref.getAbbrevTitle() == null && !ref.isOfType(ReferenceType.Article)) {
895
                    ref.setAbbrevTitle(reference);
896
                    ref.setProtectedAbbrevTitleCache(false);
897
                }
898

    
899
                ref.setProtectedTitleCache(false);
900
                taxonName.setNomenclaturalReference(ref);
901
			  //  taxonName.setNomenclaturalMicroReference(state.getCurrentRow().getCollation());
902
			}
903
		}
904

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

    
931
		taxonBase.addSource(OriginalSourceType.Import, null,"TaxonName" ,state.getConfig().getSourceReference(), null);
932

    
933
		return taxonBase;
934
	}
935

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

    
956
	private void makeParent(TaxonExcelImportState state, Taxon parentTaxon, Taxon childTaxon, Reference citation, String microCitation){
957
		Reference sec = state.getConfig().getSourceReference();
958

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

    
984

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

    
993

    
994

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

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

    
1012

    
1013

    
1014
}
(2-2/8)