Project

General

Profile

Download (34.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.Language;
37
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
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.ITaxonNameBase;
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.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.persistence.dto.MergeResult;
67
import eu.etaxonomy.cdm.strategy.exceptions.StringNotParsableException;
68
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
69
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
70
import eu.etaxonomy.cdm.strategy.parser.TimePeriodParser;
71

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

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

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

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

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

    
92

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

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

    
142
		} else if(key.equalsIgnoreCase(VERNACULAR_NAME_COLUMN)) {
143
			normalExplicitRow.setCommonName(value);
144

    
145
		} else if(key.equalsIgnoreCase(LANGUAGE_COLUMN)) {
146
			normalExplicitRow.setLanguage(value);
147

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

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

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

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

    
162
        } else if(key.equalsIgnoreCase(FAMILY_COLUMN)) {
163
            normalExplicitRow.setFamily(value);
164
        } else if(key.equalsIgnoreCase(INFRA_FAMILY_COLUMN)) {
165
            normalExplicitRow.setInfraFamily(value);
166
        }else if(key.equalsIgnoreCase(GENUS_COLUMN)) {
167
            normalExplicitRow.setGenus(value);
168
        }else if(key.trim().equalsIgnoreCase(INFRA_GENUS_COLUMN.trim())) {
169
            normalExplicitRow.setInfraGenus(value);
170
        }else if(key.equalsIgnoreCase(SPECIES_COLUMN)) {
171
            normalExplicitRow.setSpecies(value);
172
        }else if(key.equalsIgnoreCase(INFRA_SPECIES_COLUMN)) {
173
            normalExplicitRow.setInfraSpecies(value);
174
        } else if (key.equalsIgnoreCase(VERSION_COLUMN)){
175
            normalExplicitRow.setVersion(value);
176
        }
177

    
178

    
179

    
180

    
181
        else if(key.equalsIgnoreCase("!")) {
182
            //! = Legitimate, * = Illegitimate, ** = Invalid, *** = nom. rej., !! = nom. cons.
183
            if (value.equals("!")){
184
                normalExplicitRow.setNameStatus("accepted");
185
            } else if (value.equals("*")){
186
                normalExplicitRow.setNameStatus("illegitimate");
187
            } else if (value.equals("**")){
188
                normalExplicitRow.setNameStatus("invalid");
189
            } else if (value.equals("***")){
190
                normalExplicitRow.setNameStatus("nom. rej.");
191
            } else if (value.equals("!!")){
192
                normalExplicitRow.setNameStatus("nom. cons.");
193
            } else{
194
                normalExplicitRow.setNameStatus("accepted");
195
            }
196
        }else {
197
			if (analyzeFeatures(state, keyValue)){
198

    
199
			}else{
200
				String message = "Unexpected column header " + key;
201
				fireWarningEvent(message, state, 10);
202
				state.setUnsuccessfull();
203
				//logger.error(message);
204
			}
205
		}
206
		return;
207
	}
208

    
209

    
210
	/**
211
	 *  Create base taxa and add all information attached to it's name.
212
	 */
213
	@Override
214
    protected void firstPass(TaxonExcelImportState state) {
215

    
216
//		if (1==1){
217
//			return;
218
//		}
219
//		System.out.println("FP:" + state.getCurrentLine());
220
		Rank rank = null;
221
		NormalExplicitRow taxonDataHolder = state.getCurrentRow();
222

    
223
		String rankStr = taxonDataHolder.getRank();
224
		String taxonNameStr = taxonDataHolder.getScientificName();
225
		String authorStr = taxonDataHolder.getAuthor();
226
		String publishingAuthor= taxonDataHolder.getPublishingAuthor();
227
		String basionymAuthor = taxonDataHolder.getBasionymAuthor();
228

    
229
		String referenceStr = taxonDataHolder.getReference();
230
		String nameStatus = taxonDataHolder.getNameStatus();
231
		String familyNameStr = taxonDataHolder.getFamily();
232
		String infraFamilyNameStr = taxonDataHolder.getInfraFamily();
233
		String genusNameStr = taxonDataHolder.getGenus();
234
		String infraGenusNameStr = taxonDataHolder.getInfraGenus();
235
		String speciesNameStr = taxonDataHolder.getSpecies();
236
		String infraSpeciesNameStr = taxonDataHolder.getInfraSpecies();
237

    
238
		String version = taxonDataHolder.getVersion();
239

    
240

    
241

    
242

    
243
		String dateStr = taxonDataHolder.getDate();
244
		String id = taxonDataHolder.getId();
245
		UUID cdmUuid = taxonDataHolder.getCdmUuid();
246

    
247
		TaxonBase<?> taxonBase = null;
248
		if (cdmUuid != null){
249
			taxonBase = getTaxonService().find(cdmUuid);
250
		}else{
251
			if (StringUtils.isNotBlank(taxonNameStr)) {
252

    
253
				// Rank
254
				try {
255
				    if (!StringUtils.isBlank(rankStr)) {
256
                        rank = Rank.getRankByNameOrIdInVoc(rankStr);
257
                        }
258
				} catch (UnknownCdmTypeException ex) {
259
					try {
260
						rank = Rank.getRankByEnglishName(rankStr, state.getConfig().getNomenclaturalCode(), false);
261
					} catch (UnknownCdmTypeException e) {
262
					    try {
263
	                           rank = TcsRdfTransformer.rankString2Rank(rankStr);
264
	                       } catch (UnknownCdmTypeException e1) {
265
	                            // TODO Auto-generated catch block
266
	                            e1.printStackTrace();
267
	                       }
268

    
269
					}
270
				}
271

    
272
	            //taxon
273
				taxonBase = createTaxon(state, rank, taxonNameStr, authorStr, publishingAuthor, basionymAuthor, referenceStr, dateStr, nameStatus);
274
			}else{
275
				return;
276
			}
277
		}
278
		if (taxonBase == null){
279
			String message = "Taxon is already in DB. Record will not be handled";
280
			fireWarningEvent(message, "Record: " + state.getCurrentLine(), 6);
281
			logger.warn(message);
282
			//state.setUnsuccessfull();
283
			return;
284
		}
285

    
286
		//protologue
287
		for (String protologue : taxonDataHolder.getProtologues()){
288
			TextData textData = TextData.NewInstance(Feature.PROTOLOGUE());
289
			this.getNameDescription(taxonBase.getName()).addElement(textData);
290
			URI uri;
291
			try {
292
				uri = new URI(protologue);
293
				textData.addMedia(Media.NewInstance(uri, null, null, null));
294
			} catch (URISyntaxException e) {
295
				String warning = "URISyntaxException when trying to convert to URI: " + protologue;
296
				logger.error(warning);
297
				state.setUnsuccessfull();
298
			}
299
		}
300

    
301
		state.putTaxon(id, taxonBase);
302
		taxonBase = getTaxonService().save(taxonBase);
303
		taxonDataHolder.setCdmUuid(taxonBase.getUuid());
304
		return;
305
    }
306

    
307

    
308

    
309
	/**
310
	 *  Stores parent-child, synonym and common name relationships.
311
	 *  Adds all taxon related descriptive information (this is not done in the first pass
312
	 *  because the information may also be attached to a synonym).
313
	 */
314
	@Override
315
    protected void secondPass(TaxonExcelImportState state) {
316
		if (logger.isDebugEnabled()){logger.debug(state.getCurrentLine());}
317
		try {
318
			NormalExplicitRow taxonDataHolder = state.getCurrentRow();
319
			String taxonNameStr = taxonDataHolder.getScientificName();
320
			String nameStatus = taxonDataHolder.getNameStatus();
321
			String commonNameStr = taxonDataHolder.getCommonName();
322

    
323
			String synonymNameStr = taxonDataHolder.getSynonym();
324
			String basionymNameStr = taxonDataHolder.getBasionym();
325

    
326
			String parentId = taxonDataHolder.getParentId();
327
			String childId = taxonDataHolder.getId();
328
			UUID cdmUuid = taxonDataHolder.getCdmUuid();
329
			Taxon acceptedTaxon = null;
330
			TaxonName nameUsedInSource = null;
331
			TaxonBase<?> taxonBase = null;
332
			Taxon parentTaxon = null;
333

    
334
			if (cdmUuid != null){
335
				taxonBase = getTaxonService().find(cdmUuid);
336
				if (taxonBase != null ){
337
                    acceptedTaxon = getAcceptedTaxon(taxonBase);
338
                    nameUsedInSource = taxonBase.getName();
339
				}
340
			} else{
341
			    taxonBase = state.getTaxonBase(childId);
342

    
343
    			 if (parentId == "0" && state.getParent() == null){
344
                     parentTaxon =(Taxon) getTaxonService().load(((NormalExplicitImportConfigurator)state.getConfig()).getParentUUID());
345
                     state.setParent(parentTaxon);
346
                 }else if (parentId != "0"){
347
                    parentTaxon = CdmBase.deproxy(state.getTaxonBase(parentId), Taxon.class);
348
                 } else if (state.getParent() != null){
349
                     parentTaxon = state.getParent();
350
                 }
351
    			if (taxonBase != null ){
352
    				acceptedTaxon = getAcceptedTaxon(taxonBase);
353
    				if (synonymNameStr != null){
354
    				    Synonym syn = createSynonym(state,taxonBase,synonymNameStr);
355
    				    acceptedTaxon.addSynonym(syn, SynonymType.HETEROTYPIC_SYNONYM_OF());
356
    				}
357
    				if (basionymNameStr != null){
358
    				    Synonym syn = createSynonym(state,taxonBase,basionymNameStr);
359
                        acceptedTaxon.addSynonym(syn, SynonymType.HOMOTYPIC_SYNONYM_OF());
360
                        syn.getName().addRelationshipToName(acceptedTaxon.getName(), NameRelationshipType.BASIONYM(), null);
361
    				}
362
    				nameUsedInSource = taxonBase.getName();
363

    
364
    			    //TODO error handling for class cast
365

    
366

    
367
    				nameUsedInSource = taxonBase.getName();
368
    				nameStatus = CdmUtils.Nz(nameStatus).trim().toLowerCase();
369
    				if (validMarkers.contains(nameStatus)){
370
    						Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
371
    						acceptedTaxon = taxon;
372
    						// Add the parent relationship
373
    						//if (state.getCurrentRow().getParentId() != 0) {
374
    						MergeResult result = null;
375
    							if (parentTaxon != null) {
376
    								//Taxon taxon = (Taxon)state.getTaxonBase(childId);
377

    
378
    								Reference sourceRef = state.getConfig().getSourceReference();
379
    								String microCitation = null;
380
    								Taxon childTaxon = taxon;
381
    								makeParent(state, parentTaxon, childTaxon, sourceRef, microCitation);
382
    								getTaxonService().saveOrUpdate(childTaxon);
383
    								state.putTaxon(parentId, parentTaxon);
384
    							} else {
385
    								String message = "Taxonomic parent not found for " + taxonNameStr;
386
    								logger.warn(message);
387
    								fireWarningEvent(message, state, 6);
388
    								//state.setUnsuccessfull();
389
    							}
390
    //						}else{
391
    //							//do nothing (parent == 0) no parent exists
392
    //						}
393
    					}else if (synonymMarkers.contains(nameStatus)){
394
    						//add synonym relationship
395
    						acceptedTaxon = parentTaxon;
396
    						try {
397
    							Synonym synonym = CdmBase.deproxy(taxonBase,Synonym.class);
398
    							if (acceptedTaxon == null){
399
    								String message = "Accepted/valid taxon could not be found. Please check referential integrity.";
400
    								fireWarningEvent(message, state, 8);
401
    							}else{
402
    							    if (parentId != "0"){
403
    							        //if no relation was defined in file skip relationship creation
404
    							        acceptedTaxon.addSynonym(synonym, SynonymType.SYNONYM_OF());
405
    							        getTaxonService().saveOrUpdate(acceptedTaxon);
406
    							    }
407
    							}
408
    						} catch (Exception e) {
409
    							String message = "Unhandled exception (%s) occurred during synonym import/update";
410
    							message = String.format(message, e.getMessage());
411
    							fireWarningEvent(message, state, 10);
412
    							state.setUnsuccessfull();
413
    						}
414

    
415
    					}else{
416
    					    if (parentTaxon != null) {
417
                                Taxon taxon = (Taxon)state.getTaxonBase(childId);
418

    
419
                                Reference sourceRef = state.getConfig().getSourceReference();
420
                                String microCitation = null;
421
                                Taxon childTaxon = taxon;
422
                                makeParent(state, parentTaxon, childTaxon, sourceRef, microCitation);
423
                                getTaxonService().saveOrUpdate(parentTaxon);
424
                                state.putTaxon(parentId, parentTaxon);
425
                            } else {
426
                                String message = "Taxonomic parent not found for " + taxonNameStr;
427
                                logger.warn(message);
428
                                fireWarningEvent(message, state, 6);
429
                                //state.setUnsuccessfull();
430
                            }
431
    					}
432

    
433

    
434
    			}
435
    			if (StringUtils.isBlank(taxonNameStr) && acceptedTaxon == null) {
436
                    acceptedTaxon = parentTaxon;
437
                    nameUsedInSource = null;
438
                }
439
			}
440
			if (acceptedTaxon == null && (StringUtils.isNotBlank(commonNameStr) ||taxonDataHolder.getFeatures().size() > 0 )){
441
				String message = "Accepted taxon could not be found. Can't add additional data (common names, descriptive data, ...) to taxon";
442
				fireWarningEvent(message, state, 6);
443
			}else{
444
				//common names
445
				if (StringUtils.isNotBlank(commonNameStr)){			// add common name to taxon
446
					handleCommonName(state, taxonNameStr, commonNameStr, acceptedTaxon);
447
				}
448

    
449

    
450
				//media
451
				for (String imageUrl : taxonDataHolder.getImages()){
452
					TaxonDescription td = acceptedTaxon.getImageGallery(true);
453
					DescriptionElementBase mediaHolder;
454
					if (td.getElements().size() != 0){
455
						mediaHolder = td.getElements().iterator().next();
456
					}else{
457
						mediaHolder = TextData.NewInstance(Feature.IMAGE());
458
						td.addElement(mediaHolder);
459
					}
460
					try {
461
						Media media = getImageMedia(imageUrl, READ_MEDIA_DATA);
462
						mediaHolder.addMedia(media);
463
					} catch (MalformedURLException e) {
464
						logger.warn("Can't add media: " + e.getMessage());
465
						state.setUnsuccessfull();
466
					}
467
				}
468

    
469
				//tdwg label
470
				for (String tdwg : taxonDataHolder.getDistributions()){
471
					TaxonDescription td = this.getTaxonDescription(acceptedTaxon, state.getConfig().getSourceReference() ,false, true);
472
					NamedArea area = TdwgAreaProvider.getAreaByTdwgAbbreviation(tdwg);
473
					if (area == null){
474
						area = TdwgAreaProvider.getAreaByTdwgLabel(tdwg);
475
					}
476
					if (area != null){
477
						Distribution distribution = Distribution.NewInstance(area, PresenceAbsenceTerm.PRESENT());
478
						td.addElement(distribution);
479
					}else{
480
						String message = "TDWG area could not be recognized: " + tdwg;
481
						logger.warn(message);
482
						state.setUnsuccessfull();
483
					}
484
				}
485

    
486
				//features
487
				handleFeatures(state, taxonDataHolder, acceptedTaxon, nameUsedInSource);
488
			}
489
		} catch (Exception e) {
490
			e.printStackTrace();
491
		}
492
		return;
493
	}
494

    
495

    
496
	/**
497
     * @param state
498
     * @param taxonBase
499
     * @param synonymNameStr
500
     */
501
    private Synonym createSynonym(TaxonExcelImportState state, TaxonBase<?> taxonBase, String synonymNameStr) {
502
        NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
503
        ITaxonNameBase name = null;
504
        if (nc.isKindOf(NomenclaturalCode.ICZN)){
505
            name = TaxonNameFactory.NewZoologicalInstance(taxonBase.getName().getRank());
506
        }else if (nc.isKindOf(NomenclaturalCode.ICNAFP)){
507
            name = TaxonNameFactory.NewBotanicalInstance(taxonBase.getName().getRank());
508
        } else{
509
            name = TaxonNameFactory.NewNonViralInstance(taxonBase.getName().getRank());
510
        }
511
        name.setTitleCache(synonymNameStr, true);
512
        if (name != null){
513
            return Synonym.NewInstance(name, null);
514
        }
515
        logger.debug("The nomenclatural code is not supported.");
516
        return null;
517
    }
518

    
519

    
520
    /**
521
	 * @param state
522
	 * @param taxonDataHolder
523
	 * @param acceptedTaxon
524
	 */
525
	private void handleFeatures(TaxonExcelImportState state, NormalExplicitRow taxonDataHolder, Taxon acceptedTaxon, TaxonName nameUsedInSource) {
526
		//feature
527
		for (UUID featureUuid : taxonDataHolder.getFeatures()){
528
			Feature feature = getFeature(state, featureUuid);
529
			List<String> textList = taxonDataHolder.getFeatureTexts(featureUuid);
530
			List<String> languageList = taxonDataHolder.getFeatureLanguages(featureUuid);
531

    
532
			for (int i = 0; i < textList.size(); i++){
533
				String featureText = textList.get(i);
534
				String featureLanguage = languageList == null ? null :languageList.get(i);
535
				Language language = getFeatureLanguage(featureLanguage, state);
536
				//TODO
537
				TaxonDescription td = this.getTaxonDescription(acceptedTaxon, state.getConfig().getSourceReference() ,false, true);
538
				TextData textData = TextData.NewInstance(feature);
539
				textData.putText(language, featureText);
540
				td.addElement(textData);
541

    
542
				SourceDataHolder sourceDataHolder = taxonDataHolder.getFeatureTextReferences(featureUuid, i);
543
				List<Map<SourceType, String>> sourceList = sourceDataHolder.getSources();
544
				for (Map<SourceType, String> sourceMap : sourceList){
545

    
546
					//ref
547
					Reference ref = ReferenceFactory.newGeneric();
548
					boolean refExists = false; //in case none of the ref fields exists, the ref should not be added
549
					for (SourceType type : sourceMap.keySet()){
550
						String value = sourceMap.get(type);
551
						if (type.equals(SourceType.Author)){
552
							TeamOrPersonBase<?> author = getAuthorAccordingToConfig(value, state);
553
							ref.setAuthorship(author);
554
						}else if (type.equals(SourceType.Title)) {
555
							ref.setTitle(value);
556
						}else if (type.equals(SourceType.Year)) {
557
							ref.setDatePublished(TimePeriodParser.parseString(value));
558
						}else if (type.equals(SourceType.RefExtension)) {
559
							ExtensionType extensionType = getExtensionType(state, uuidRefExtension, "RefExtension", "Reference Extension", "RefExt.");
560
							Extension extension = Extension.NewInstance(ref, value, extensionType);
561
						}
562
						refExists = true;
563
					}
564
					DescriptionElementSource source = DescriptionElementSource.NewInstance(OriginalSourceType.PrimaryTaxonomicSource);
565
					if (refExists){
566
						ref = getReferenceAccordingToConfig(ref, state);
567
						source.setCitation(ref);
568
						source.setNameUsedInSource(nameUsedInSource);
569
					}
570
					textData.addSource(source);
571
				}
572
			}
573
		}
574
	}
575

    
576
	private final Map<String, UUID> referenceMapping = new HashMap<String, UUID>();
577
	private final Map<UUID, Reference> referenceStore = new HashMap<UUID, Reference>();
578

    
579
	private Reference getReferenceAccordingToConfig(Reference value, TaxonExcelImportState state) {
580
		Reference result = null;
581
		String titleCache = value.getTitleCache();
582
		UUID referenceUuid = referenceMapping.get(titleCache);
583
		if (referenceUuid != null){
584
			result = referenceStore.get(referenceUuid);
585
		}
586
		if (result == null){
587
			result = value;
588
			referenceStore.put(result.getUuid(), result);
589
		}
590
		if (referenceUuid == null){
591
			referenceMapping.put(titleCache, result.getUuid());
592
		}
593
		return result;
594
	}
595

    
596

    
597
	private final Map<String, UUID> authorMapping = new HashMap<String, UUID>();
598
	private final Map<UUID, TeamOrPersonBase> authorStore = new HashMap<UUID, TeamOrPersonBase>();
599

    
600
	private TeamOrPersonBase<?> getAuthorAccordingToConfig(String value, TaxonExcelImportState state) {
601
		TeamOrPersonBase<?> result = null;
602
		UUID authorUuid = authorMapping.get(value);
603
		if (authorUuid != null){
604
			result = authorStore.get(authorUuid);
605
		}
606
		if (result == null){
607
			//TODO parsing
608
			TeamOrPersonBase<?> author = Team.NewInstance();
609
			author.setTitleCache(value, true);
610
			result = author;
611
			authorStore.put(result.getUuid(), result);
612
		}
613
		if (authorUuid == null){
614
			authorMapping.put(value, result.getUuid());
615
		}
616
		return result;
617
	}
618

    
619

    
620
	private final Map<String, UUID> languageMapping = new HashMap<String, UUID>();
621

    
622
	private Language getFeatureLanguage(String featureLanguage, TaxonExcelImportState state) {
623
		if (StringUtils.isBlank(featureLanguage)){
624
			return null;
625
		}
626
		UUID languageUuid = languageMapping.get(featureLanguage);
627
		if (languageUuid == null){
628
			Language result = getTermService().getLanguageByIso(featureLanguage);
629
			languageUuid = result.getUuid();
630
			languageMapping.put(featureLanguage, languageUuid);
631
		}
632
		Language result = getLanguage(state, languageUuid, null, null, null);
633
		return result;
634
	}
635

    
636

    
637
	/**
638
	 * @param state
639
	 * @param taxonNameStr
640
	 * @param commonNameStr
641
	 * @param parentId
642
	 */
643
	private void handleCommonName(TaxonExcelImportState state,
644
			String taxonNameStr, String commonNameStr, Taxon acceptedTaxon) {
645
		Language language = getTermService().getLanguageByIso(state.getCurrentRow().getLanguage());
646
		if (language == null && CdmUtils.isNotEmpty(state.getCurrentRow().getLanguage())  ){
647
			String error ="Language is null but shouldn't";
648
			logger.error(error);
649
			throw new IllegalArgumentException(error);
650
		}
651
		CommonTaxonName commonTaxonName = CommonTaxonName.NewInstance(commonNameStr, language);
652
		try {
653
			TaxonDescription taxonDescription = getTaxonDescription(acceptedTaxon, false, true);
654
			taxonDescription.addElement(commonTaxonName);
655
			logger.info("Common name " + commonNameStr + " added to " + acceptedTaxon.getTitleCache());
656
		} catch (ClassCastException ex) {
657
			logger.error(taxonNameStr + " is not a taxon instance.");
658
		}
659
	}
660

    
661

    
662
	/**
663
	 * @param state
664
	 * @param rank
665
	 * @param taxonNameStr
666
	 * @param authorStr
667
	 * @param nameStatus
668
	 * @param nameStatus2
669
	 * @return
670
	 */
671
	private TaxonBase createTaxon(TaxonExcelImportState state, Rank rank,
672
	        String familyNameStr, String infraFamilyNameStr, String genusNameStr, String infraGenusNameStr, String speciesNameStr, String infraSpeciesNameStr, String authorStr, String publishingAuthorStr, String basionymAuthorStr,String reference, String date, String nameStatus) {
673
		// Create the taxon name object depending on the setting of the nomenclatural code
674
		// in the configurator (botanical code, zoological code, etc.)
675

    
676
		NomenclaturalCode nc = getConfigurator().getNomenclaturalCode();
677

    
678
		TaxonBase taxonBase;
679
		String nameCache = null;
680
		if (rank == null){
681
		    System.err.println("bla");
682
		}
683
		if (rank.isGenus()){
684
		    nameCache =genusNameStr;
685
		} else if (rank.isInfraGeneric()){
686
		    nameCache =CdmUtils.concat(" " +rank.getIdInVocabulary() + " ",genusNameStr,infraGenusNameStr);
687

    
688
        } else if (rank.isSpecies()){
689
            nameCache = CdmUtils.concat(" ", genusNameStr,speciesNameStr);
690
        } else if (rank.isInfraSpecific()){
691
            nameCache = CdmUtils.concat(" " +rank.getIdInVocabulary() + " ",genusNameStr,infraGenusNameStr);
692
        }
693
		if (! synonymMarkers.contains(nameStatus)  && state.getConfig().isReuseExistingTaxaWhenPossible()){
694
			taxonBase = getTaxonService().findBestMatchingTaxon(nameCache);
695
		}else{
696
			taxonBase = getTaxonService().findBestMatchingSynonym(nameCache);
697
			if (taxonBase != null){
698
				logger.info("Matching taxon/synonym found for " + nameCache);
699
			}
700
		}
701
		if (taxonBase != null){
702
			logger.info("Matching taxon/synonym found for " + nameCache);
703
			return null;
704
		}else {
705
			taxonBase = createTaxon(state, rank, nameCache, authorStr, publishingAuthorStr, basionymAuthorStr, reference, date, nameStatus, nc);
706
		}
707
		return taxonBase;
708
	}
709

    
710
	/**
711
     * @param state
712
     * @param rank
713
     * @param taxonNameStr
714
     * @param authorStr
715
     * @param nameStatus
716
     * @param nameStatus2
717
     * @return
718
     */
719
    private TaxonBase createTaxon(TaxonExcelImportState state, Rank rank,
720
            String taxonNameStr, String authorStr, String publishingAuthorStr, String basionymAuthorStr,String reference, String date, String nameStatus) {
721
        // Create the taxon name object depending on the setting of the nomenclatural code
722
        // in the configurator (botanical code, zoological code, etc.)
723
        if (StringUtils.isBlank(taxonNameStr)){
724
            return null;
725
        }
726
        NomenclaturalCode nc = getConfigurator().getNomenclaturalCode();
727

    
728
        TaxonBase taxonBase = null;
729

    
730
        String titleCache = CdmUtils.concat(" ", taxonNameStr, authorStr);
731
        if (! synonymMarkers.contains(nameStatus)  && state.getConfig().isReuseExistingTaxaWhenPossible()){
732
            titleCache = CdmUtils.concat(" ", taxonNameStr, authorStr);
733
            taxonBase = getTaxonService().findBestMatchingTaxon(titleCache);
734
        }else if ( state.getConfig().isReuseExistingTaxaWhenPossible()){
735
            taxonBase = getTaxonService().findBestMatchingSynonym(titleCache);
736
            if (taxonBase != null){
737
                logger.info("Matching taxon/synonym found for " + titleCache);
738
            }
739
        }
740
        if (taxonBase != null){
741
            logger.info("Matching taxon/synonym found for " + titleCache);
742
            return null;
743
        }else {
744
            taxonBase = createTaxon(state, rank, taxonNameStr, authorStr, publishingAuthorStr, basionymAuthorStr, reference, date, nameStatus, nc);
745
        }
746
        return taxonBase;
747
    }
748

    
749

    
750

    
751
	/**
752
	 * @param state
753
	 * @param rank
754
	 * @param taxonNameStr
755
	 * @param authorStr
756
	 * @param nameStatus
757
	 * @param nameStatus2
758
	 * @param nc
759
	 * @return
760
	 */
761
	private TaxonBase<?> createTaxon(TaxonExcelImportState state, Rank rank, String taxonNameStr,
762
			String authorStr, String publishingAutorStr, String basionymAuthor, String reference, String date, String nameStatus, NomenclaturalCode nc) {
763
		TaxonBase<?> taxonBase;
764
		INonViralName taxonName = null;
765
		if (nc == NomenclaturalCode.ICVCN){
766
			logger.warn("ICVCN not yet supported");
767

    
768
		}else{
769
		    //String taxonNameStr = titleCache.substring(0, titleCache.indexOf(authorStr));
770
			taxonName = nc.getNewTaxonNameInstance(rank);
771
			NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
772
			taxonName = parser.parseFullName(taxonNameStr, nc, rank);
773

    
774
			if (! taxonName.getNameCache().equals(taxonNameStr)){
775
				taxonName.setNameCache(taxonNameStr, true);
776
			}
777

    
778
			// Create the author
779
			if (StringUtils.isNotBlank(authorStr)) {
780
				try {
781
					parser.parseAuthors(taxonName, authorStr);
782
				} catch (StringNotParsableException e) {
783
					taxonName.setAuthorshipCache(authorStr);
784
 				}
785
			}
786
			if (StringUtils.isNotBlank(reference)) {
787

    
788
			    INomenclaturalReference ref = parser.parseReferenceTitle(reference, date, true);
789
			    if (ref.getAuthorship() == null){
790
			        ref.setAuthorship(taxonName.getCombinationAuthorship());
791
			    }
792

    
793
			    if (ref.getAbbrevTitle() == null && !ref.isOfType(ReferenceType.Article)) {
794
                    ref.setAbbrevTitle(reference);
795
                    ref.setProtectedAbbrevTitleCache(false);
796
                }
797

    
798
                ref.setProtectedTitleCache(false);
799

    
800
			    taxonName.setNomenclaturalReference(ref);
801
			    taxonName.setNomenclaturalMicroReference(state.getCurrentRow().getCollation());
802
			}
803
		}
804

    
805
		//Create the taxon
806
		Reference sec = state.getConfig().getSourceReference();
807
		// Create the status
808
		nameStatus = CdmUtils.Nz(nameStatus).trim().toLowerCase();
809
		if (validMarkers.contains(nameStatus)){
810
			taxonBase = Taxon.NewInstance(taxonName, sec);
811
		}else if (synonymMarkers.contains(nameStatus)){
812
			taxonBase = Synonym.NewInstance(taxonName, sec);
813
		}else {
814
			Taxon taxon = Taxon.NewInstance(taxonName, sec);
815
			if (nameStatusMarkers.contains(nameStatus)){
816
			    if (nameStatus.equals(NOM_ILLEG)){
817
			        taxonName.addStatus(NomenclaturalStatusType.ILLEGITIMATE(), null, null);
818
			    } else if (nameStatus.equals(NOM_REJ)){
819
			        taxonName.addStatus(NomenclaturalStatusType.REJECTED(), null, null);
820
			    } else if (nameStatus.equals(NOM_CONS)){
821
                    taxonName.addStatus(NomenclaturalStatusType.CONSERVED(), null, null);
822
                }
823
			}else{
824
			    taxon.setTaxonStatusUnknown(true);
825
			}
826
			taxonBase = taxon;
827
		}
828
		taxonBase.getName().addSource(OriginalSourceType.Import, "urn:lsid:ipni.org:names:"+ state.getCurrentRow().getId()+":"+state.getCurrentRow().getVersion(),"TaxonName" ,null, null);
829
		taxonBase.addSource(OriginalSourceType.Import, "urn:lsid:ipni.org:names:"+ state.getCurrentRow().getId()+":"+state.getCurrentRow().getVersion(),"TaxonName" ,null, null);
830

    
831
		return taxonBase;
832
	}
833

    
834
	/**
835
	 * @param taxon
836
	 * @return
837
	 */
838
	//TODO implementation must be improved when matching of taxon names with existing names is implemented
839
	//=> the assumption that the only description is the description added by this import
840
	//is wrong then
841
	private TaxonNameDescription getNameDescription(TaxonName name) {
842
		Set<TaxonNameDescription> descriptions = name.getDescriptions();
843
		if (descriptions.size()>1){
844
			throw new IllegalStateException("Implementation does not yet support names with multiple descriptions");
845
		}else if (descriptions.size()==1){
846
			return descriptions.iterator().next();
847
		}else{
848
			return TaxonNameDescription.NewInstance(name);
849
		}
850
	}
851

    
852
	private void makeParent(TaxonExcelImportState state, Taxon parentTaxon, Taxon childTaxon, Reference citation, String microCitation){
853
		Reference sec = state.getConfig().getSourceReference();
854

    
855
//		Reference sec = parentTaxon.getSec();
856
		Classification tree = state.getClassification();
857
		if (tree == null){
858
			//tree = makeTree(state, sec);
859
		    if (state.getConfig().getClassificationUuid() != null){
860
		        tree = getClassificationService().load(state.getConfig().getClassificationUuid());
861
		        state.setClassification(tree);
862
		    }
863
		    if (tree == null){
864
		        tree = makeTree(state, sec);
865
		        getClassificationService().save(tree);
866
		        state.setClassification(tree);
867
		    }
868
		}
869
		if (sec.equals(childTaxon.getSec())){
870
		    boolean success =  (null !=  tree.addParentChild(parentTaxon, childTaxon, citation, microCitation));
871
			if (success == false){
872
				state.setUnsuccessfull();
873
			}
874
		}else{
875
			logger.warn("No relationship added for child " + childTaxon.getTitleCache());
876
		}
877
		return;
878
	}
879

    
880

    
881
	/* (non-Javadoc)
882
	 * @see eu.etaxonomy.cdm.io.excel.common.ExcelTaxonOrSpecimenImportBase#createDataHolderRow()
883
	 */
884
	@Override
885
	protected NormalExplicitRow createDataHolderRow() {
886
		return new NormalExplicitRow();
887
	}
888

    
889

    
890

    
891
	/* (non-Javadoc)
892
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
893
	 */
894
	@Override
895
	protected boolean doCheck(TaxonExcelImportState state) {
896
		logger.warn("DoCheck not yet implemented for NormalExplicitImport");
897
		return true;
898
	}
899

    
900
	/* (non-Javadoc)
901
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
902
	 */
903
	@Override
904
	protected boolean isIgnore(TaxonExcelImportState state) {
905
		return false;
906
	}
907

    
908

    
909

    
910
}
(1-1/5)