Project

General

Profile

Download (36.1 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.specimen.excel.in;
11

    
12
import java.text.ParseException;
13
import java.util.ArrayList;
14
import java.util.List;
15
import java.util.UUID;
16

    
17
import org.apache.commons.lang.StringUtils;
18
import org.apache.log4j.Logger;
19
import org.springframework.stereotype.Component;
20

    
21
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
22
import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
23
import eu.etaxonomy.cdm.common.CdmUtils;
24
import eu.etaxonomy.cdm.io.common.ICdmIO;
25
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
26
import eu.etaxonomy.cdm.io.excel.common.ExcelRowBase.PostfixTerm;
27
import eu.etaxonomy.cdm.io.excel.common.ExcelTaxonOrSpecimenImportBase;
28
import eu.etaxonomy.cdm.io.specimen.excel.in.SpecimenRow.DeterminationLight;
29
import eu.etaxonomy.cdm.model.agent.AgentBase;
30
import eu.etaxonomy.cdm.model.agent.Person;
31
import eu.etaxonomy.cdm.model.agent.Team;
32
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
33
import eu.etaxonomy.cdm.model.common.Annotation;
34
import eu.etaxonomy.cdm.model.common.AnnotationType;
35
import eu.etaxonomy.cdm.model.common.CdmBase;
36
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
37
import eu.etaxonomy.cdm.model.common.Language;
38
import eu.etaxonomy.cdm.model.common.TimePeriod;
39
import eu.etaxonomy.cdm.model.description.Feature;
40
import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
41
import eu.etaxonomy.cdm.model.description.TaxonDescription;
42
import eu.etaxonomy.cdm.model.location.Country;
43
import eu.etaxonomy.cdm.model.location.NamedArea;
44
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
45
import eu.etaxonomy.cdm.model.location.NamedAreaType;
46
import eu.etaxonomy.cdm.model.location.ReferenceSystem;
47
import eu.etaxonomy.cdm.model.name.BotanicalName;
48
import eu.etaxonomy.cdm.model.name.IBotanicalName;
49
import eu.etaxonomy.cdm.model.name.INonViralName;
50
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
51
import eu.etaxonomy.cdm.model.name.NonViralName;
52
import eu.etaxonomy.cdm.model.name.Rank;
53
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
54
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
55
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
56
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
57
import eu.etaxonomy.cdm.model.name.ZoologicalName;
58
import eu.etaxonomy.cdm.model.occurrence.Collection;
59
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
60
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
61
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
62
import eu.etaxonomy.cdm.model.reference.Reference;
63
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
64
import eu.etaxonomy.cdm.model.taxon.Taxon;
65
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
66
import eu.etaxonomy.cdm.persistence.query.MatchMode;
67
import eu.etaxonomy.cdm.strategy.exceptions.StringNotParsableException;
68
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
69
import eu.etaxonomy.cdm.strategy.parser.INonViralNameParser;
70
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
71
import eu.etaxonomy.cdm.strategy.parser.TimePeriodParser;
72

    
73
/**
74
 * @author a.mueller
75
 * @created 10.05.2011
76
 * @version 1.0
77
 */
78
@Component
79
public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<SpecimenCdmExcelImportState, SpecimenRow>  implements ICdmIO<SpecimenCdmExcelImportState> {
80
	private static final Logger logger = Logger.getLogger(SpecimenCdmExcelImport.class);
81

    
82
	private static final String WORKSHEET_NAME = "Specimen";
83

    
84
	private static final String BASIS_OF_RECORD_COLUMN = "(?i)(BasisOfRecord)";
85
	private static final String COUNTRY_COLUMN = "(?i)(Country)";
86
	private static final String AREA_COLUMN = "(?i)(Area)";
87
	private static final String ISO_COUNTRY_COLUMN = "(?i)(ISOCountry|CountryCode)";
88
	private static final String LOCALITY_COLUMN = "(?i)(Locality)";
89
	private static final String ALTITUDE_COLUMN = "(?i)(AbsoluteElevation|Altitude)";
90
	private static final String ALTITUDE_MAX_COLUMN = "(?i)(AbsoluteElevation|Altitude)Max(imum)?";
91
	private static final String COLLECTION_DATE_COLUMN = "(?i)(CollectionDate)";
92
	private static final String COLLECTION_DATE_END_COLUMN = "(?i)(CollectionDateEnd)";
93
	private static final String COLLECTOR_COLUMN = "(?i)(Collector)";
94
	private static final String COLLECTORS_COLUMN = "(?i)(Collectors)";
95
	private static final String PRIMARY_COLLECTOR_COLUMN = "(?i)(PrimaryCollector)";
96
	private static final String LONGITUDE_COLUMN = "(?i)(Longitude)";
97
	private static final String LATITUDE_COLUMN = "(?i)(Latitude)";
98
	private static final String REFERENCE_SYSTEM_COLUMN = "(?i)(ReferenceSystem)";
99
	private static final String ERROR_RADIUS_COLUMN = "(?i)(ErrorRadius)";
100

    
101

    
102
	private static final String COLLECTORS_NUMBER_COLUMN = "(?i)((Collectors|Field)Number)";
103
	private static final String ECOLOGY_COLUMN = "(?i)(Ecology|Habitat)";
104
	private static final String PLANT_DESCRIPTION_COLUMN = "(?i)(PlantDescription)";
105
	private static final String FIELD_NOTES_COLUMN = "(?i)(FieldNotes)";
106
	private static final String SEX_COLUMN = "(?i)(Sex)";
107

    
108

    
109
	private static final String ACCESSION_NUMBER_COLUMN = "(?i)(AccessionNumber)";
110
	private static final String BARCODE_COLUMN = "(?i)(Barcode)";
111
	private static final String COLLECTION_CODE_COLUMN = "(?i)(CollectionCode)";
112
	private static final String COLLECTION_COLUMN = "(?i)(Collection)";
113
	private static final String UNIT_NOTES_COLUMN = "(?i)((Unit)?Notes)";
114

    
115

    
116
	private static final String TYPE_CATEGORY_COLUMN = "(?i)(TypeCategory)";
117
	private static final String TYPIFIED_NAME_COLUMN = "(?i)(TypifiedName|TypeOf)";
118

    
119

    
120
	private static final String SOURCE_COLUMN = "(?i)(Source)";
121
	private static final String ID_IN_SOURCE_COLUMN = "(?i)(IdInSource)";
122

    
123

    
124
	private static final String DETERMINATION_AUTHOR_COLUMN = "(?i)(Author)";
125
	private static final String DETERMINATION_MODIFIER_COLUMN = "(?i)(DeterminationModifier)";
126
	private static final String DETERMINED_BY_COLUMN = "(?i)(DeterminationBy)";
127
	private static final String DETERMINED_WHEN_COLUMN = "(?i)(Det(ermination)?When)";
128
	private static final String DETERMINATION_NOTES_COLUMN = "(?i)(DeterminationNote)";
129
	private static final String EXTENSION_COLUMN = "(?i)(Ext(ension)?)";
130

    
131

    
132
	public SpecimenCdmExcelImport() {
133
		super();
134
	}
135

    
136

    
137

    
138

    
139
	@Override
140
	protected void analyzeSingleValue(KeyValue keyValue, SpecimenCdmExcelImportState state) {
141
		SpecimenRow row = state.getCurrentRow();
142
		String value = keyValue.value;
143
		if(keyValue.key.matches(BASIS_OF_RECORD_COLUMN)) {
144
			row.setBasisOfRecord(value);
145
		} else if(keyValue.key.matches(COUNTRY_COLUMN)) {
146
			row.setCountry(value);
147
		} else if(keyValue.key.matches(ISO_COUNTRY_COLUMN)) {
148
			row.setIsoCountry(value);
149
		} else if(keyValue.key.matches(LOCALITY_COLUMN)) {
150
			row.setLocality(value);
151
		} else if(keyValue.key.matches(FIELD_NOTES_COLUMN)) {
152
			row.setLocality(value);
153
		} else if(keyValue.key.matches(ALTITUDE_COLUMN)) {
154
			row.setAltitude(value);
155
		} else if(keyValue.key.matches(ALTITUDE_MAX_COLUMN)) {
156
			row.setAltitudeMax(value);
157
		} else if(keyValue.key.matches(COLLECTOR_COLUMN)) {
158
			row.putCollector(keyValue.index, value);
159
		} else if(keyValue.key.matches(PRIMARY_COLLECTOR_COLUMN)) {
160
			row.setPrimaryCollector(value);
161
		} else if(keyValue.key.matches(ECOLOGY_COLUMN)) {
162
			row.setEcology(value);
163
		} else if(keyValue.key.matches(PLANT_DESCRIPTION_COLUMN)) {
164
			row.setPlantDescription(value);
165
		} else if(keyValue.key.matches(SEX_COLUMN)) {
166
			row.setSex(value);
167
		} else if(keyValue.key.matches(COLLECTION_DATE_COLUMN)) {
168
			row.setCollectingDate(value);
169
		} else if(keyValue.key.matches(COLLECTION_DATE_END_COLUMN)) {
170
			row.setCollectingDateEnd(value);
171
		} else if(keyValue.key.matches(COLLECTORS_COLUMN)) {
172
			row.setCollectors(value);
173
		} else if(keyValue.key.matches(COLLECTOR_COLUMN)) {
174
			row.putCollector(keyValue.index, value);
175
		} else if(keyValue.key.matches(COLLECTORS_NUMBER_COLUMN)) {
176
			row.setCollectorsNumber(value);
177
		} else if(keyValue.key.matches(LONGITUDE_COLUMN)) {
178
			row.setLongitude(value);
179
		} else if(keyValue.key.matches(LATITUDE_COLUMN)) {
180
			row.setLatitude(value);
181
		} else if(keyValue.key.matches(REFERENCE_SYSTEM_COLUMN)) {
182
			row.setReferenceSystem(value);
183
		} else if(keyValue.key.matches(ERROR_RADIUS_COLUMN)) {
184
			row.setErrorRadius(value);
185
		} else if(keyValue.key.matches(AREA_COLUMN)) {
186
			if (keyValue.postfix != null){
187
				row.addLeveledArea(keyValue.postfix, value);
188
			}else{
189
				logger.warn("Not yet implemented");
190
			}
191
		} else if(keyValue.key.matches(LANGUAGE)) {
192
			row.setLanguage(value);
193

    
194

    
195
		} else if(keyValue.key.matches(ACCESSION_NUMBER_COLUMN)) {
196
			row.setAccessionNumber(value);
197
		} else if(keyValue.key.matches(BARCODE_COLUMN)) {
198
			row.setBarcode(value);
199
		} else if(keyValue.key.matches(UNIT_NOTES_COLUMN)) {
200
			row.putUnitNote(keyValue.index, value);
201

    
202

    
203
		} else if(keyValue.key.matches(FAMILY_COLUMN)) {
204
			row.putDeterminationFamily(keyValue.index, value);
205
		} else if(keyValue.key.matches(GENUS_COLUMN)) {
206
			row.putDeterminationGenus(keyValue.index, value);
207
		} else if(keyValue.key.matches(SPECIFIC_EPITHET_COLUMN)) {
208
			row.putDeterminationSpeciesEpi(keyValue.index, value);
209
		} else if(keyValue.key.matches(INFRASPECIFIC_EPITHET_COLUMN)) {
210
			row.putDeterminationInfraSpeciesEpi(keyValue.index, value);
211
		} else if(keyValue.key.matches(RANK_COLUMN)) {
212
			row.putDeterminationRank(keyValue.index, value);
213
		} else if(keyValue.key.matches(TAXON_UUID_COLUMN)) {
214
			row.putDeterminationTaxonUuid(keyValue.index, value);
215
		} else if(keyValue.key.matches(FULL_NAME_COLUMN)) {
216
			row.putDeterminationFullName(keyValue.index, value);
217
		} else if(keyValue.key.matches(DETERMINATION_AUTHOR_COLUMN)) {
218
			row.putDeterminationAuthor(keyValue.index, value);
219
		} else if(keyValue.key.matches(DETERMINATION_MODIFIER_COLUMN)) {
220
			row.putDeterminationDeterminationModifier(keyValue.index, value);
221
		} else if(keyValue.key.matches(DETERMINATION_NOTES_COLUMN)) {
222
			row.putDeterminationDeterminationNotes(keyValue.index, value);
223
		} else if(keyValue.key.matches(DETERMINED_BY_COLUMN)) {
224
			row.putDeterminationDeterminedBy(keyValue.index, value);
225
		} else if(keyValue.key.matches(DETERMINED_WHEN_COLUMN)) {
226
			row.putDeterminationDeterminedWhen(keyValue.index, value);
227

    
228
		} else if(keyValue.key.matches(COLLECTION_CODE_COLUMN)) {
229
			row.setCollectionCode(value);
230
		} else if(keyValue.key.matches(COLLECTION_COLUMN)) {
231
			row.setCollection(value);
232

    
233
		} else if(keyValue.key.matches(TYPE_CATEGORY_COLUMN)) {
234
			row.putTypeCategory(keyValue.index, getSpecimenTypeStatus(state, value));
235
		} else if(keyValue.key.matches(TYPIFIED_NAME_COLUMN)) {
236
			row.putTypifiedName(keyValue.index, getTaxonName(state, value));
237

    
238

    
239
		} else if(keyValue.key.matches(SOURCE_COLUMN)) {
240
			row.putSourceReference(keyValue.index, getOrMakeReference(state, value) );
241
		} else if(keyValue.key.matches(ID_IN_SOURCE_COLUMN)) {
242
			row.putIdInSource(keyValue.index, value);
243
		} else if(keyValue.key.matches(EXTENSION_COLUMN)) {
244
			if (keyValue.postfix != null){
245
				row.addExtension(keyValue.postfix, value);
246
			}else{
247
				logger.warn("Extension without postfix not yet implemented");
248
			}
249

    
250
		}else {
251
			state.setUnsuccessfull();
252
			logger.error("Unexpected column header " + keyValue.originalKey);
253
		}
254

    
255
    	return;
256
	}
257

    
258

    
259
	@Override
260
	protected void firstPass(SpecimenCdmExcelImportState state) {
261
		SpecimenRow row = state.getCurrentRow();
262

    
263
		//basis of record
264
		SpecimenOrObservationType type = SpecimenOrObservationType.valueOf2(row.getBasisOfRecord());
265
		if (type == null){
266
			String message = "%s is not a valid BasisOfRecord. 'Unknown' is used instead in line %d.";
267
			message = String.format(message, row.getBasisOfRecord(), state.getCurrentLine());
268
			logger.warn(message);
269
			type = SpecimenOrObservationType.DerivedUnit;
270
		}
271
		DerivedUnitFacade facade = DerivedUnitFacade.NewInstance(type);
272

    
273

    
274
		Language lang = Language.DEFAULT();
275
		if (StringUtils.isNotBlank(row.getLanguage())){
276
			Language langIso = getTermService().getLanguageByIso(row.getLanguage());
277
			if (langIso == null){
278
				String message = "Language could not be recognized: %s. Use default language instead. Line %d.";
279
				message = String.format(message, langIso, state.getCurrentLine());
280
			}else{
281
				lang = langIso;
282
			}
283
		}
284

    
285
		//country
286
		handleCountry(facade, row, state);
287
		handleAreas(facade,row, state);
288

    
289
		facade.setGatheringPeriod(getTimePeriod(row.getCollectingDate(), row.getCollectingDateEnd()));
290
		facade.setLocality(row.getLocality());
291
		facade.setFieldNotes(row.getFieldNotes());
292
		facade.setFieldNumber(row.getCollectorsNumber());
293
		facade.setEcology(row.getEcology(), lang);
294
		facade.setPlantDescription(row.getPlantDescription(), lang);
295
//		facade.setSex(row.get)
296
		handleExactLocation(facade, row, state);
297
		facade.setCollector(getOrMakeAgent(state, row.getCollectors()));
298
		facade.setPrimaryCollector(getOrMakePrimaryCollector(facade, row.getPrimaryCollector(), state));
299
		handleAbsoluteElevation(facade, row, state);
300

    
301
		//derivedUnit
302
		facade.setBarcode(row.getBarcode());
303
		facade.setAccessionNumber(row.getAccessionNumber());
304
		facade.setCollection(getOrMakeCollection(state, row.getCollectionCode(), row.getCollection()));
305
		for (IdentifiableSource source : row.getSources()){
306
			facade.addSource(source);
307
		}
308
		for (SpecimenTypeDesignation designation : row.getTypeDesignations()){
309
			logger.warn("FIXME"); //FIXME
310
//			facade.innerDerivedUnit().addSpecimenTypeDesignation(designation);
311
		}
312
		handleDeterminations(state, row, facade);
313
		handleExtensions(facade.innerDerivedUnit(),row, state);
314
		for (String note : row.getUnitNotes()){
315
			Annotation annotation = Annotation.NewInstance(note, AnnotationType.EDITORIAL(), Language.DEFAULT());
316
			facade.addAnnotation(annotation);
317
		}
318

    
319
		//save
320
		getOccurrenceService().save(facade.innerDerivedUnit());
321
		return;
322
	}
323

    
324
	private void handleAbsoluteElevation(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
325
		//altitude
326

    
327
		try {
328
			String altitude = row.getAltitude();
329
			if (StringUtils.isBlank(altitude)){
330
				return;
331
			}
332
//			if (altitude.endsWith(".0")){
333
//				altitude = altitude.substring(0, altitude.length() -2);
334
//			}
335
			int value = Integer.valueOf(altitude);
336
			facade.setAbsoluteElevation(value);
337
		} catch (NumberFormatException e) {
338
			String message = "Absolute elevation / altitude '%s' is not an integer number in line %d";
339
			message = String.format(message, row.getAltitude(), state.getCurrentLine());
340
			logger.warn(message);
341
			return;
342
		}
343

    
344
		//max
345

    
346
		try {
347
			String max = row.getAltitudeMax();
348
			if (StringUtils.isBlank(max)){
349
				return;
350
			}
351
//			if (max.endsWith(".0")){
352
//				max = max.substring(0, max.length() -2);
353
//			}
354
			int value = Integer.valueOf(max);
355
			//TODO avoid unequal distance
356
			int min = facade.getAbsoluteElevation();
357
			if ( (value - min) % 2 == 1 ){
358
				String message = "Altitude min-max difference ist not equal. Max reduced by 1 in line %d";
359
				message = String.format(message, state.getCurrentLine());
360
				logger.warn(message);
361
				value--;
362
			}
363
			facade.setAbsoluteElevationRange(min, value);
364
		} catch (NumberFormatException e) {
365
			String message = "Absolute elevation / Altitude maximum '%s' is not an integer number in line %d";
366
			message = String.format(message, row.getAltitudeMax(), state.getCurrentLine());
367
			logger.warn(message);
368
			return;
369
		}catch (Exception e){
370
			String message = "Error occurred when trying to write Absolute elevation / Altitude maximum '%s' in line %d";
371
			message = String.format(message, row.getAltitudeMax(), state.getCurrentLine());
372
			logger.warn(message);
373
			return;
374

    
375
		}
376

    
377

    
378
	}
379

    
380
	private void handleAreas(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
381
		List<PostfixTerm> areas = row.getLeveledAreas();
382

    
383
		for (PostfixTerm lArea : areas){
384
			String description = lArea.term;
385
			String abbrev = lArea.term;
386
			NamedAreaType type = null;
387
			String key = lArea.postfix + "_" + lArea.term;
388
			UUID areaUuid = state.getArea(key);
389
			NamedAreaLevel level = state.getPostfixLevel(lArea.postfix);
390

    
391
			TermMatchMode matchMode = state.getConfig().getAreaMatchMode();
392
			NamedArea area = getNamedArea(state, areaUuid, lArea.term, description, abbrev, type, level, null, matchMode);
393
			facade.addCollectingArea(area);
394
			if (areaUuid == null){
395
				state.putArea(key, area.getUuid());
396
			}
397
		}
398
	}
399

    
400

    
401
	/**
402
	 * @param state
403
	 * @param row
404
	 * @param facade
405
	 */
406
	private void handleDeterminations(SpecimenCdmExcelImportState state,SpecimenRow row, DerivedUnitFacade facade) {
407
		boolean isFirstDetermination = true;
408
		DeterminationLight commonDetermination = row.getCommonDetermination();
409
		Taxon commonTaxon = null;
410
		TaxonNameBase<?,?> commonName = null;
411

    
412
		boolean hasCommonTaxonInfo = (commonDetermination == null) ? false : commonDetermination.hasTaxonInformation();
413
		if (hasCommonTaxonInfo && commonDetermination != null){
414
			TaxonBase<?> taxonBase = null;
415
			if (StringUtils.isNotBlank(commonDetermination.taxonUuid)){
416
				UUID taxonUuid = UUID.fromString(commonDetermination.taxonUuid);
417
				taxonBase = getTaxonService().find(taxonUuid);
418
				if (taxonBase == null){
419
					String message = "Taxon for uuid %s not found in line %d.";
420
					message = String.format(message, taxonUuid.toString(), state.getCurrentLine());
421
					logger.warn(message);
422
				}
423
			}else{
424
				taxonBase = findBestMatchingTaxon(state, commonDetermination, state.getConfig().isCreateTaxonIfNotExists());
425
			}
426
			commonTaxon = getAcceptedTaxon(taxonBase);
427
			if (taxonBase != null){
428
				commonName = taxonBase.getName();
429
			}else{
430
				commonTaxon = createTaxonFromDetermination(state, commonDetermination);
431
				commonName = commonTaxon.getName();
432
			}
433
		}
434

    
435

    
436
		for (DeterminationLight determinationLight : row.getDetermination()){
437
			Taxon taxon;
438
			if (! hasCommonTaxonInfo){
439
				taxon = findBestMatchingTaxon(state, determinationLight, state.getConfig().isCreateTaxonIfNotExists());
440
			}else{
441
				taxon = commonTaxon;
442
			}
443
			if (taxon != null){
444
				getTaxonService().saveOrUpdate(taxon);
445
				if (state.getConfig().isMakeIndividualAssociations() && taxon != null){
446
					IndividualsAssociation indivAssociciation = IndividualsAssociation.NewInstance();
447
					DerivedUnit du = facade.innerDerivedUnit();
448
					indivAssociciation.setAssociatedSpecimenOrObservation(du);
449
					getTaxonDescription(taxon).addElement(indivAssociciation);
450
					Feature feature = Feature.INDIVIDUALS_ASSOCIATION();
451
					if (facade.getType().isPreservedSpecimen()){
452
						feature = Feature.SPECIMEN();
453
					}else if (facade.getType().isFeatureObservation()){
454
						feature = Feature.OBSERVATION();
455
					}
456
					if (state.getConfig().isUseMaterialsExaminedForIndividualsAssociations()){
457
						feature = Feature.MATERIALS_EXAMINED();
458
					}
459

    
460
					indivAssociciation.setFeature(feature);
461
				}
462
				if (state.getConfig().isDeterminationsAreDeterminationEvent()){
463
					DeterminationEvent detEvent = makeDeterminationEvent(state, determinationLight, taxon);
464
					detEvent.setPreferredFlag(isFirstDetermination);
465
					facade.addDetermination(detEvent);
466
				}
467
			}
468

    
469
			if (isFirstDetermination && state.getConfig().isFirstDeterminationIsStoredUnder()){
470
				TaxonNameBase<?,?> name;
471

    
472
				if (!hasCommonTaxonInfo){
473
					name = findBestMatchingName(state, determinationLight);
474
				}else{
475
					if (commonName == null){
476
						commonName = findBestMatchingName(state, commonDetermination);
477
					}
478
					name = commonName;
479
				}
480
				if (name != null){
481
					facade.setStoredUnder(name);
482
				}
483
			}
484
			isFirstDetermination = false;
485
		}
486
	}
487

    
488
	private Taxon createTaxonFromDetermination( SpecimenCdmExcelImportState state, DeterminationLight commonDetermination) {
489

    
490
		//rank
491
		Rank rank;
492
		try {
493
			rank = StringUtils.isBlank(commonDetermination.rank) ? null : Rank.getRankByNameOrIdInVoc(commonDetermination.rank, true);
494
		} catch (UnknownCdmTypeException e) {
495
			rank = null;
496
		}
497

    
498
		//name
499
		INonViralName name;
500
		INonViralNameParser parser = NonViralNameParserImpl.NewInstance();
501
		NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
502
		if (StringUtils.isNotBlank(commonDetermination.fullName)){
503
			name = parser.parseFullName(commonDetermination.fullName, nc, rank);
504
			if (StringUtils.isBlank(name.getAuthorshipCache()) && StringUtils.isNotBlank(commonDetermination.author)){
505
				setAuthorship(name, commonDetermination.author, parser);
506
			}
507
		}else{
508
			if (nc != null){
509
				name = nc.getNewTaxonNameInstance(rank);
510
			}else{
511
				name = TaxonNameFactory.NewNonViralInstance(rank);
512
			}
513
			if (StringUtils.isNotBlank(commonDetermination.genus)){
514
				name.setGenusOrUninomial(commonDetermination.genus);
515
			}
516
			if (StringUtils.isNotBlank(commonDetermination.speciesEpi)){
517
				name.setSpecificEpithet(commonDetermination.speciesEpi);
518
			}
519
			if (StringUtils.isNotBlank(commonDetermination.infraSpeciesEpi)){
520
				name.setInfraSpecificEpithet(commonDetermination.infraSpeciesEpi);
521
			}
522
			if (StringUtils.isNotBlank(commonDetermination.author)){
523
				setAuthorship(name, commonDetermination.author, parser);
524
			}
525
			//guess rank if null
526
			if (name.getRank() == null){
527
				if (name.getInfraGenericEpithet() != null && name.getSpecificEpithet() == null){
528
					name.setRank(Rank.INFRAGENERICTAXON());
529
				}else if (name.getSpecificEpithet() != null && name.getInfraSpecificEpithet() == null){
530
					name.setRank(Rank.SPECIES());
531
				}else if (name.getInfraSpecificEpithet() != null){
532
					name.setRank(Rank.INFRASPECIFICTAXON());
533
				}
534

    
535
			}
536

    
537
		}
538
		//sec
539
		Reference sec = null;
540
		if (StringUtils.isNotBlank(commonDetermination.determinedBy)){
541
			sec = ReferenceFactory.newGeneric();
542
			TeamOrPersonBase<?> determinedBy;
543
			IBotanicalName dummyName = TaxonNameFactory.NewBotanicalInstance(Rank.SPECIES());
544
			try {
545
				parser.parseAuthors(dummyName, commonDetermination.determinedBy);
546
				determinedBy = dummyName.getCombinationAuthorship();
547
			} catch (StringNotParsableException e) {
548
				determinedBy = Team.NewTitledInstance(commonDetermination.determinedBy, commonDetermination.determinedBy);
549
			}
550
			sec.setAuthorship(determinedBy);
551
		}
552

    
553
		//taxon
554
		Taxon taxon = Taxon.NewInstance(name, sec);
555

    
556
		if (StringUtils.isNotBlank(commonDetermination.family)){
557
			if (name.getRank() == null || name.getRank().isLower(Rank.FAMILY()) ){
558
				logger.warn("Family taxon could not be created");
559
			}
560
		}
561

    
562
		//return
563
		return taxon;
564

    
565
	}
566

    
567

    
568

    
569

    
570
	private void setAuthorship(INonViralName name, String author, INonViralNameParser<NonViralName> parser) {
571
		if (name instanceof BotanicalName || name instanceof ZoologicalName){
572
			try {
573
				parser.parseAuthors(name, author);
574
			} catch (StringNotParsableException e) {
575
				name.setAuthorshipCache(author);
576
			}
577
		}else{
578
			name.setAuthorshipCache(author);
579
		}
580
	}
581

    
582

    
583

    
584
	/**
585
	 * This method tries to find the best matching taxon depending on the import configuration,
586
	 * the taxon name information and the concept information available.
587
	 *
588
	 *
589
	 * @param state
590
	 * @param determinationLight
591
	 * @param createIfNotExists
592
	 * @return
593
	 */
594
	private Taxon findBestMatchingTaxon(SpecimenCdmExcelImportState state, DeterminationLight determinationLight, boolean createIfNotExists) {
595
		INonViralName name = makeTaxonName(state, determinationLight);
596

    
597
		String titleCache = makeSearchNameTitleCache(state, determinationLight, name);
598

    
599
		if (! StringUtils.isBlank(titleCache)){
600
			MatchingTaxonConfigurator matchConfigurator = MatchingTaxonConfigurator.NewInstance();
601
			matchConfigurator.setTaxonNameTitle(titleCache);
602
			matchConfigurator.setIncludeSynonyms(false);
603
			Taxon taxon = getTaxonService().findBestMatchingTaxon(matchConfigurator);
604

    
605
			if(taxon == null && createIfNotExists){
606
				logger.info("creating new Taxon from TaxonName '" + titleCache+"'");
607
				UUID secUuid = null; //TODO
608
				Reference sec = null;
609
				if (secUuid != null){
610
					sec = getReferenceService().find(secUuid);
611
				}
612
				taxon = Taxon.NewInstance(name, sec);
613
			}else if (taxon == null){
614
				String message = "Taxon '%s' not found in line %d";
615
				message = String.format(message, titleCache, state.getCurrentLine());
616
				logger.warn(message);
617
			}
618
			return taxon;
619
		}else {
620
			return null;
621
		}
622
	}
623

    
624
	/**
625
	 * @param state
626
	 * @param determinationLight
627
	 * @param name
628
	 * @return
629
	 */
630
	private String makeSearchNameTitleCache(SpecimenCdmExcelImportState state, DeterminationLight determinationLight,
631
				INonViralName name) {
632
		String titleCache = determinationLight.fullName;
633
		if (! state.getConfig().isPreferNameCache() || StringUtils.isBlank(titleCache) ){
634
			String computedTitleCache = name.getTitleCache();
635
			if (StringUtils.isNotBlank(computedTitleCache)){
636
				titleCache = computedTitleCache;
637
			}
638

    
639
		}
640
		return titleCache;
641
	}
642

    
643
	/**
644
	 * @param state
645
	 * @param determinationLight
646
	 * @return
647
	 */
648
	private INonViralName makeTaxonName(SpecimenCdmExcelImportState state, DeterminationLight determinationLight) {
649
		INonViralName name = TaxonNameFactory.NewNonViralInstance(null);
650
		NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
651
		if (nc != null){
652
			name = nc.getNewTaxonNameInstance(null);
653
		}
654
		name.setGenusOrUninomial(determinationLight.genus);
655
		name.setSpecificEpithet(determinationLight.speciesEpi);
656
		name.setInfraSpecificEpithet(determinationLight.infraSpeciesEpi);
657

    
658
		//FIXME bracketAuthors and teams not yet implemented!!!
659
		List<String> authors = new ArrayList<String>();
660
		if (StringUtils.isNotBlank(determinationLight.author)){
661
			authors.add(determinationLight.author);
662
		}
663
		TeamOrPersonBase<?> agent = getOrMakeAgent(state, authors);
664
		name.setCombinationAuthorship(agent);
665

    
666
		try {
667
			if (StringUtils.isNotBlank(determinationLight.rank) ){
668
				name.setRank(Rank.getRankByNameOrIdInVoc(determinationLight.rank, nc, true));
669
			}
670
		} catch (UnknownCdmTypeException e) {
671
			String message = "Rank not found: %s: ";
672
			message = String.format(message, determinationLight.rank);
673
			logger.warn(message);
674
		}
675
		if (StringUtils.isBlank(name.getInfraSpecificEpithet()) && StringUtils.isNotBlank(name.getSpecificEpithet() )){
676
			name.setRank(Rank.SPECIES());
677
		}
678
		if (StringUtils.isBlank(name.getSpecificEpithet()) && StringUtils.isNotBlank(name.getGenusOrUninomial() )){
679
			name.setRank(Rank.SPECIES());
680
		}
681
		if (StringUtils.isBlank(name.getTitleCache())){
682
			//TODO test
683
			name.setTitleCache(determinationLight.fullName, true);
684
		}
685
		return name;
686
	}
687

    
688
	private TaxonNameBase findBestMatchingName(SpecimenCdmExcelImportState state, DeterminationLight determinationLight) {
689

    
690
		INonViralName name = makeTaxonName(state, determinationLight);
691
		String titleCache = makeSearchNameTitleCache(state, determinationLight, name);
692

    
693
		//TODO
694
		List<TaxonNameBase> matchingNames = getNameService().findByName(null, titleCache, MatchMode.EXACT, null, null, null, null, null).getRecords();
695
		if (matchingNames.size() > 0){
696
			return matchingNames.get(0);
697
		} else if (matchingNames.size() > 0){
698
			logger.warn("Get best matching taxon name not yet fully implemeted for specimen import");
699
			return matchingNames.get(0);
700
		}else{
701
			return null;
702
		}
703

    
704
	}
705

    
706

    
707
	private DeterminationEvent makeDeterminationEvent(SpecimenCdmExcelImportState state, DeterminationLight determination, Taxon taxon) {
708
		DeterminationEvent event = DeterminationEvent.NewInstance();
709
		//taxon
710
		event.setTaxon(taxon);
711

    
712
		//date
713
		TimePeriod date = TimePeriodParser.parseString(determination.determinedWhen);
714
		event.setTimeperiod(date);
715
		//by
716
		//FIXME bracketAuthors and teams not yet implemented!!!
717
		List<String> authors = new ArrayList<String>();
718
		if (StringUtils.isNotBlank(determination.determinedBy)){
719
			authors.add(determination.determinedBy);
720
		}
721
		TeamOrPersonBase<?> actor = getOrMakeAgent(state, authors);
722
		TeamOrPersonBase<?> secAuthor = taxon.getSec() == null ? null : taxon.getSec().getAuthorship();
723
		if (actor != null && secAuthor != null && secAuthor.getTitleCache().equals(actor.getTitleCache()) && secAuthor.getNomenclaturalTitle().equals(actor.getNomenclaturalTitle())) {
724
			actor = secAuthor;
725
		}
726

    
727
		event.setActor(actor);
728

    
729
		//TODO
730
		if (StringUtils.isNotBlank(determination.modifier)){
731
			logger.warn("DeterminationModifiers not yet implemented for specimen import");
732
		}
733
//		DeterminationModifier modifier = DeterminationModifier.NewInstance(term, label, labelAbbrev);
734
//		determination.modifier;
735
		//notes
736
		if (StringUtils.isNotEmpty(determination.notes)){
737
			Annotation annotation = Annotation.NewInstance(determination.notes, AnnotationType.EDITORIAL(), Language.DEFAULT());
738
			event.addAnnotation(annotation);
739
		}
740

    
741
		return event;
742
	}
743

    
744
	private TaxonDescription getTaxonDescription(Taxon taxon) {
745
		TaxonDescription desc = this.getTaxonDescription(taxon, ! IMAGE_GALLERY, CREATE);
746
		return desc;
747
	}
748

    
749
	private TeamOrPersonBase<?> getOrMakeAgent(SpecimenCdmExcelImportState state, List<String> agents) {
750
		if (agents.size() == 0){
751
			return null;
752
		}else if (agents.size() == 1){
753
			return getOrMakePerson(state, agents.get(0));
754
		}else{
755
			return getOrMakeTeam(state, agents);
756
		}
757
	}
758

    
759
	private Person getOrMakePrimaryCollector(DerivedUnitFacade facade, String primaryCollector, SpecimenCdmExcelImportState state) {
760
		if (StringUtils.isBlank(primaryCollector)){
761
			return null;
762
		}
763
		AgentBase<?> collector = facade.getCollector();
764
		List<Person> collectors = new ArrayList<Person>();
765
		if (collector.isInstanceOf(Team.class) ){
766
			Team team = CdmBase.deproxy(collector, Team.class);
767
			collectors.addAll(team.getTeamMembers());
768
		}else if (collector.isInstanceOf(Person.class)){
769
			collectors.add(CdmBase.deproxy(collector, Person.class));
770
		}else{
771
			throw new IllegalStateException("Unknown subclass of agentbase: " + collector.getClass().getName() );
772
		}
773
		for (Person person :collectors){
774
			if (primaryCollector.equalsIgnoreCase(person.getTitleCache())){
775
				return person;
776
			}
777
			if (primaryCollector.equalsIgnoreCase(person.getNomenclaturalTitle())){
778
				return person;
779
			}
780
		}
781
		String message = "Primary Agent '%s' could not be determined in collector(s) in line %d";
782
		message = String.format(message, primaryCollector, state.getCurrentLine());
783
		logger.warn(message);
784
		return null;
785
	}
786

    
787
	private Team getOrMakeTeam(SpecimenCdmExcelImportState state, List<String> agents) {
788
		String key = CdmUtils.concat("_", agents.toArray(new String[0]));
789

    
790
		Team result = state.getTeam(key);
791
		if (result == null){
792
			result = Team.NewInstance();
793
			for (String member : agents){
794
				Person person = getOrMakePerson(state, member);
795
				result.addTeamMember(person);
796
			}
797
			state.putTeam(key, result);
798
		}
799
		return result;
800
	}
801

    
802
	private Person getOrMakePerson(SpecimenCdmExcelImportState state, String value) {
803
		Person result = state.getPerson(value);
804
		if (result == null){
805
			result = Person.NewInstance();
806
			result.setTitleCache(value, true);
807
			state.putPerson(value, result);
808
		}
809
		return result;
810
	}
811

    
812
	private Reference getOrMakeReference(SpecimenCdmExcelImportState state, String value) {
813
		Reference result = state.getReference(value);
814
		if (result == null){
815
			result = ReferenceFactory.newGeneric();
816
			result.setTitleCache(value, true);
817
			state.putReference(value, result);
818
		}
819
		return result;
820
	}
821

    
822

    
823

    
824
	private Collection getOrMakeCollection(SpecimenCdmExcelImportState state, String collectionCode, String collectionString) {
825
		Collection result = state.getCollection(collectionCode);
826
		if (result == null){
827
			result = Collection.NewInstance();
828
			result.setCode(collectionCode);
829
			result.setName(collectionString);
830
			state.putCollection(collectionCode, result);
831
		}
832
		return result;
833
	}
834

    
835

    
836
	private TaxonNameBase<?,?> getTaxonName(SpecimenCdmExcelImportState state, String name) {
837
		TaxonNameBase<?,?> result = null;
838
		result = state.getName(name);
839
		if (result != null){
840
			return result;
841
		}
842
		List<TaxonNameBase> list = getNameService().findByTitle(null, name, null, null, null, null, null, null).getRecords();
843
		//TODO better strategy to find best name, e.g. depending on the classification it is used in
844
		if (! list.isEmpty()){
845
			result = list.get(0);
846
		}
847
		if (result == null){
848
			NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
849
			NomenclaturalCode code = state.getConfig().getNomenclaturalCode();
850
			result = (TaxonNameBase<?,?>)parser.parseFullName(name, code, null);
851

    
852
		}
853
		if (result != null){
854
			state.putName(name, result);
855
		}
856
		return result;
857
	}
858

    
859
	private SpecimenTypeDesignationStatus getSpecimenTypeStatus(SpecimenCdmExcelImportState state, String key)  {
860
		SpecimenTypeDesignationStatus result = null;
861
		try {
862
			result = state.getTransformer().getSpecimenTypeDesignationStatusByKey(key);
863
			if (result == null){
864
				String message = "Type status not recognized for %s in line %d";
865
				message = String.format(message, key, state.getCurrentLine());
866
				logger.warn(message);
867
			}
868
			return result;
869
		} catch (UndefinedTransformerMethodException e) {
870
			throw new RuntimeException("getSpecimenTypeDesignationStatusByKey not yet implemented");
871
		}
872

    
873

    
874
	}
875

    
876

    
877
	private void handleExactLocation(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
878

    
879
		//reference system
880
		ReferenceSystem refSys = null;
881
		if (StringUtils.isNotBlank(row.getReferenceSystem())){
882
			String strRefSys = row.getReferenceSystem().trim().replaceAll("\\s", "");
883
			UUID refUuid;
884
			try {
885
				refSys = state.getTransformer().getReferenceSystemByKey(strRefSys);
886
				if (refSys == null){
887
					//TODO we still need user defined Reference Systems here
888
					refUuid = state.getTransformer().getReferenceSystemUuid(strRefSys);
889
					if (refUuid == null){
890
						String message = "Unknown reference system %s in line %d";
891
						message = String.format(message, strRefSys, state.getCurrentLine());
892
						logger.warn(message);
893
					}
894
					refSys = getReferenceSystem(state, refUuid, strRefSys, strRefSys, strRefSys, null);
895
				}
896

    
897
			} catch (UndefinedTransformerMethodException e) {
898
				throw new RuntimeException(e);
899
			}
900
		}
901

    
902

    
903

    
904
		// lat/ long /error
905
		try {
906
			String longitude = row.getLongitude();
907
			String latitude = row.getLatitude();
908
			Integer errorRadius = null;
909
			if (StringUtils.isNotBlank(row.getErrorRadius())){
910
				try {
911
					errorRadius = Integer.valueOf(row.getErrorRadius());
912
				} catch (NumberFormatException e) {
913
					String message = "Error radius %s could not be transformed to Integer in line %d";
914
					message = String.format(message, row.getErrorRadius(), state.getCurrentLine());
915
					logger.warn(message);
916
				}
917
			}
918
			//all
919
			facade.setExactLocationByParsing(longitude, latitude, refSys, errorRadius);
920
		} catch (ParseException e) {
921
			String message = "Problems when parsing exact location for line %d";
922
			message = String.format(message, state.getCurrentLine());
923
			logger.warn(message);
924

    
925
		}
926

    
927

    
928

    
929
	}
930

    
931

    
932
	/*
933
	 * Set the current Country
934
	 * Search in the DB if the isoCode is known
935
	 * If not, search if the country name is in the DB
936
	 * If not, create a new Label with the Level Country
937
	 * @param iso: the country iso code
938
	 * @param fullName: the country's full name
939
	 * @param app: the CDM application controller
940
	 */
941
	private void handleCountry(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
942

    
943
		if (StringUtils.isNotBlank(row.getIsoCountry())){
944
			NamedArea country = getOccurrenceService().getCountryByIso(row.getIsoCountry());
945
			if (country != null){
946
				facade.setCountry(country);
947
				return;
948
			}
949
		}
950
		if (StringUtils.isNotBlank(row.getCountry())){
951
			List<Country> countries = getOccurrenceService().getCountryByName(row.getCountry());
952
			if (countries.size() >0){
953
				facade.setCountry(countries.get(0));
954
			}else{
955
				UUID uuid = UUID.randomUUID();
956
				String label = row.getCountry();
957
				String text = row.getCountry();
958
				String labelAbbrev = null;
959
				NamedAreaType areaType = NamedAreaType.ADMINISTRATION_AREA();
960
				NamedAreaLevel level = NamedAreaLevel.COUNTRY();
961
				NamedArea newCountry = this.getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level);
962
				facade.setCountry(newCountry);
963
			}
964
		}
965
	}
966

    
967
	@Override
968
	protected void secondPass(SpecimenCdmExcelImportState state) {
969
		//no second path defined yet
970
		return;
971
	}
972

    
973

    
974
	@Override
975
	protected String getWorksheetName() {
976
		return WORKSHEET_NAME;
977
	}
978

    
979
	@Override
980
	protected boolean needsNomenclaturalCode() {
981
		return false;
982
	}
983

    
984

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

    
993

    
994

    
995

    
996
	/* (non-Javadoc)
997
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
998
	 */
999
	@Override
1000
	protected boolean doCheck(SpecimenCdmExcelImportState state) {
1001
		logger.warn("Validation not yet implemented for " + this.getClass().getSimpleName());
1002
		return true;
1003
	}
1004

    
1005

    
1006

    
1007
	@Override
1008
	protected boolean isIgnore(SpecimenCdmExcelImportState state) {
1009
		return !state.getConfig().isDoSpecimen();
1010
	}
1011

    
1012

    
1013
}
(5-5/12)