Project

General

Profile

Download (30.5 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy 
4
* http://www.e-taxonomy.eu
5
* 
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9

    
10
package eu.etaxonomy.cdm.io.specimen.excel.in;
11

    
12
import java.text.ParseException;
13
import java.util.ArrayList;
14
import java.util.HashMap;
15
import java.util.List;
16
import java.util.Set;
17
import java.util.UUID;
18

    
19
import org.apache.commons.lang.StringUtils;
20
import org.apache.log4j.Logger;
21
import org.springframework.stereotype.Component;
22

    
23
import sun.security.util.DerEncoder;
24

    
25
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
26
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade.DerivedUnitType;
27
import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
28
import eu.etaxonomy.cdm.common.CdmUtils;
29
import eu.etaxonomy.cdm.io.common.CdmImportBase.TermMatchMode;
30
import eu.etaxonomy.cdm.io.common.ICdmIO;
31
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
32
import eu.etaxonomy.cdm.io.excel.common.ExcelImporterBase;
33
import eu.etaxonomy.cdm.io.specimen.excel.in.SpecimenRow.DeterminationLight;
34
import eu.etaxonomy.cdm.io.specimen.excel.in.SpecimenRow.PostfixTerm;
35
import eu.etaxonomy.cdm.model.agent.AgentBase;
36
import eu.etaxonomy.cdm.model.agent.Person;
37
import eu.etaxonomy.cdm.model.agent.Team;
38
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
39
import eu.etaxonomy.cdm.model.common.Annotation;
40
import eu.etaxonomy.cdm.model.common.AnnotationType;
41
import eu.etaxonomy.cdm.model.common.Extension;
42
import eu.etaxonomy.cdm.model.common.ExtensionType;
43
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
44
import eu.etaxonomy.cdm.model.common.Language;
45
import eu.etaxonomy.cdm.model.common.TimePeriod;
46
import eu.etaxonomy.cdm.model.description.Feature;
47
import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
48
import eu.etaxonomy.cdm.model.description.TaxonDescription;
49
import eu.etaxonomy.cdm.model.location.NamedArea;
50
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
51
import eu.etaxonomy.cdm.model.location.NamedAreaType;
52
import eu.etaxonomy.cdm.model.location.ReferenceSystem;
53
import eu.etaxonomy.cdm.model.location.WaterbodyOrCountry;
54
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
55
import eu.etaxonomy.cdm.model.name.NonViralName;
56
import eu.etaxonomy.cdm.model.name.Rank;
57
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
58
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
59
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
60
import eu.etaxonomy.cdm.model.occurrence.Collection;
61
import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;
62
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
63
import eu.etaxonomy.cdm.model.reference.Reference;
64
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
65
import eu.etaxonomy.cdm.model.taxon.Taxon;
66
import eu.etaxonomy.cdm.persistence.query.MatchMode;
67
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
68
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
69

    
70
/**
71
 * @author a.mueller
72
 * @created 10.05.2011
73
 * @version 1.0
74
 */
75
@Component
76
public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelImportState>  implements ICdmIO<SpecimenCdmExcelImportState> {
77
	private static final Logger logger = Logger.getLogger(SpecimenCdmExcelImport.class);
78

    
79
	private static final String WORKSHEET_NAME = "Specimen";
80

    
81
	private static final String UUID_COLUMN = "(?i)(UUID)";
82
	private static final String BASIS_OF_RECORD_COLUMN = "(?i)(BasisOfRecord)";
83
	private static final String COUNTRY_COLUMN = "(?i)(Country)";
84
	private static final String AREA_COLUMN = "(?i)(Area)";
85
	private static final String ISO_COUNTRY_COLUMN = "(?i)(ISOCountry|CountryCode)";
86
	private static final String LOCALITY_COLUMN = "(?i)(Locality)";
87
	private static final String ALTITUDE_COLUMN = "(?i)(AbsoluteElevation|Altitude)";
88
	private static final String ALTITUDE_MAX_COLUMN = "(?i)(AbsoluteElevation|Altitude)Max(imum)?";
89
	private static final String COLLECTION_DATE_COLUMN = "(?i)(CollectionDate)";
90
	private static final String COLLECTION_DATE_END_COLUMN = "(?i)(CollectionDateEnd)";
91
	private static final String COLLECTOR_COLUMN = "(?i)(Collector)";
92
	private static final String LONGITUDE_COLUMN = "(?i)(Longitude)";
93
	private static final String LATITUDE_COLUMN = "(?i)(Latitude)";
94
	private static final String REFERENCE_SYSTEM_COLUMN = "(?i)(ReferenceSystem)";
95
	private static final String ERROR_RADIUS_COLUMN = "(?i)(ErrorRadius)";
96
	
97
	
98
	private static final String COLLECTORS_NUMBER_COLUMN = "(?i)((Collectors|Field)Number)";
99
	private static final String ECOLOGY_COLUMN = "(?i)(Ecology|Habitat)";
100
	private static final String PLANT_DESCRIPTION_COLUMN = "(?i)(PlantDescription)";
101
	private static final String FIELD_NOTES_COLUMN = "(?i)(FieldNotes)";
102
	private static final String SEX_COLUMN = "(?i)(Sex)";
103
	
104
	
105
	private static final String ACCESSION_NUMBER_COLUMN = "(?i)(AccessionNumber)";
106
	private static final String BARCODE_COLUMN = "(?i)(Barcode)";
107
	private static final String COLLECTION_CODE_COLUMN = "(?i)(CollectionCode)";
108
	private static final String COLLECTION_COLUMN = "(?i)(Collection)";
109
	private static final String UNIT_NOTES_COLUMN = "(?i)((Unit)?Notes)";
110
	
111
	
112
	private static final String TYPE_CATEGORY_COLUMN = "(?i)(TypeCategory)";
113
	private static final String TYPIFIED_NAME_COLUMN = "(?i)(TypifiedName|TypeOf)";
114
	
115
	
116
	private static final String SOURCE_COLUMN = "(?i)(Source)";
117
	private static final String ID_IN_SOURCE_COLUMN = "(?i)(IdInSource)";
118
	
119
	
120
	private static final String RANK_COLUMN = "(?i)(Rank)";
121
	private static final String FULL_NAME_COLUMN = "(?i)(FullName)";
122
	private static final String FAMILY_COLUMN = "(?i)(Family)";
123
	private static final String GENUS_COLUMN = "(?i)(Genus)";
124
	private static final String SPECIFIC_EPITHET_COLUMN = "(?i)(SpecificEpi(thet)?)";
125
	private static final String INFRASPECIFIC_EPITHET_COLUMN = "(?i)(InfraSpecificEpi(thet)?)";
126
	private static final String DETERMINATION_AUTHOR_COLUMN = "(?i)(Author)";
127
	private static final String DETERMINATION_MODIFIER_COLUMN = "(?i)(DeterminationModifier)";
128
	private static final String DETERMINED_BY_COLUMN = "(?i)(DeterminationBy)";
129
	private static final String DETERMINED_WHEN_COLUMN = "(?i)(DeterminationWhen)";
130
	private static final String DETERMINATION_NOTES_COLUMN = "(?i)(DeterminationNote)";
131
	private static final String EXTENSION_COLUMN = "(?i)(Ext(ension)?)";
132
	
133
	private static final String IGNORE_COLUMN = "(?i)(Ignore|Not)";
134
	
135

    
136
	public SpecimenCdmExcelImport() {
137
		super();
138
	}
139

    
140
	
141
	@Override
142
	protected boolean analyzeRecord(HashMap<String, String> record, SpecimenCdmExcelImportState state) {
143
		boolean success = true;
144
    	Set<String> keys = record.keySet();
145
    	
146
    	SpecimenRow row = new SpecimenRow();
147
    	state.setSpecimenRow(row);
148
    	
149
    	for (String originalKey: keys) {
150
    		Integer index = 0;
151
    		String postfix = null;
152
    		String indexedKey = CdmUtils.removeDuplicateWhitespace(originalKey.trim()).toString();
153
    		String[] split = indexedKey.split("_");
154
    		String key = split[0];
155
    		if (split.length > 1){
156
    			for (int i = 1 ; i < split.length ; i++ ){
157
    				String indexString = split[i];
158
        			if (isInteger(indexString)){
159
        				index = Integer.valueOf(indexString);
160
        			}else{
161
        				postfix = split[i];
162
        			}
163
    			}
164
    		}
165
    		
166
    		String value = (String) record.get(indexedKey);
167
    		if (! StringUtils.isBlank(value)) {
168
    			if (logger.isDebugEnabled()) { logger.debug(key + ": " + value); }
169
        		value = CdmUtils.removeDuplicateWhitespace(value.trim()).toString();
170
    		}else{
171
    			continue;
172
    		}
173
    		
174
    		if (key.matches(UUID_COLUMN)) {
175
    			row.setUuid(UUID.fromString(value)); //VALIDATE UUID
176
 			} else if(key.matches(BASIS_OF_RECORD_COLUMN)) {
177
				row.setBasisOfRecord(value);
178
			} else if(key.matches(COUNTRY_COLUMN)) {
179
				row.setCountry(value);
180
			} else if(key.matches(ISO_COUNTRY_COLUMN)) {
181
				row.setIsoCountry(value);
182
			} else if(key.matches(LOCALITY_COLUMN)) {
183
				row.setLocality(value);
184
			} else if(key.matches(FIELD_NOTES_COLUMN)) {
185
				row.setLocality(value);
186
			} else if(key.matches(ALTITUDE_COLUMN)) {
187
				row.setAltitude(value);		
188
			} else if(key.matches(ALTITUDE_MAX_COLUMN)) {
189
				row.setAltitudeMax(value);		
190
			} else if(key.matches(COLLECTOR_COLUMN)) {
191
				row.putCollector(index, value);		
192
			} else if(key.matches(ECOLOGY_COLUMN)) {
193
				row.setEcology(value);
194
			} else if(key.matches(PLANT_DESCRIPTION_COLUMN)) {
195
				row.setPlantDescription(value);		
196
			} else if(key.matches(SEX_COLUMN)) {
197
				row.setSex(value);
198
			} else if(key.matches(COLLECTION_DATE_COLUMN)) {
199
				row.setCollectingDate(value);		
200
			} else if(key.matches(COLLECTION_DATE_END_COLUMN)) {
201
				row.setCollectingDateEnd(value);		
202
			} else if(key.matches(COLLECTOR_COLUMN)) {
203
				row.putCollector(index, value);	
204
			} else if(key.matches(COLLECTORS_NUMBER_COLUMN)) {
205
				row.setCollectorsNumber(value);		
206
			} else if(key.matches(LONGITUDE_COLUMN)) {
207
				row.setLongitude(value);		
208
			} else if(key.matches(LATITUDE_COLUMN)) {
209
				row.setLatitude(value);		
210
			} else if(key.matches(REFERENCE_SYSTEM_COLUMN)) {
211
				row.setReferenceSystem(value);		
212
			} else if(key.matches(ERROR_RADIUS_COLUMN)) {
213
				row.setErrorRadius(value);		
214
			} else if(key.matches(AREA_COLUMN)) {
215
				if (postfix != null){
216
					row.addLeveledArea(postfix, value);		
217
				}else{
218
					logger.warn("Not yet implemented");
219
				}
220
			
221
				
222
				
223
			} else if(key.matches(ACCESSION_NUMBER_COLUMN)) {
224
				row.setLocality(value);		
225
			} else if(key.matches(BARCODE_COLUMN)) {
226
				row.setBarcode(value);		
227
			} else if(key.matches(UNIT_NOTES_COLUMN)) {
228
				row.putUnitNote(index, value);		
229
			
230
				
231
			} else if(key.matches(FAMILY_COLUMN)) {
232
				row.putDeterminationFamily(index, value);		
233
			} else if(key.matches(GENUS_COLUMN)) {
234
				row.putDeterminationGenus(index, value);		
235
			} else if(key.matches(SPECIFIC_EPITHET_COLUMN)) {
236
				row.putDeterminationSpeciesEpi(index, value);			
237
			} else if(key.matches(INFRASPECIFIC_EPITHET_COLUMN)) {
238
				row.putDeterminationInfraSpeciesEpi(index, value);			
239
			} else if(key.matches(RANK_COLUMN)) {
240
				row.putDeterminationRank(index, value);			
241
			} else if(key.matches(FULL_NAME_COLUMN)) {
242
				row.putDeterminationFullName(index, value);			
243
			} else if(key.matches(DETERMINATION_AUTHOR_COLUMN)) {
244
				row.putDeterminationAuthor(index, value);			
245
			} else if(key.matches(DETERMINATION_MODIFIER_COLUMN)) {
246
				row.putDeterminationDeterminationModifier(index, value);			
247
			} else if(key.matches(DETERMINATION_NOTES_COLUMN)) {
248
				row.putDeterminationDeterminationNotes(index, value);			
249
			} else if(key.matches(DETERMINED_BY_COLUMN)) {
250
				row.putDeterminationDeterminedBy(index, value);			
251
			} else if(key.matches(DETERMINED_WHEN_COLUMN)) {
252
				row.putDeterminationDeterminedWhen(index, value);			
253
			
254
			} else if(key.matches(COLLECTION_CODE_COLUMN)) {
255
				row.setCollectionCode(value);		
256
			} else if(key.matches(COLLECTION_COLUMN)) {
257
				row.setCollection(value);		
258
			
259
			} else if(key.matches(TYPE_CATEGORY_COLUMN)) {
260
				row.putTypeCategory(index, getSpecimenTypeStatus(state, value));	
261
			} else if(key.matches(TYPIFIED_NAME_COLUMN)) {
262
				row.putTypifiedName(index, getTaxonName(state, value));		
263
			
264
			
265
			} else if(key.matches(SOURCE_COLUMN)) {
266
				row.putSourceReference(index, getOrMakeReference(state, value));	
267
			} else if(key.matches(ID_IN_SOURCE_COLUMN)) {
268
				row.putIdInSource(index, value);		
269
			} else if(key.matches(EXTENSION_COLUMN)) {
270
				if (postfix != null){
271
					row.addExtension(postfix, value);		
272
				}else{
273
					logger.warn("Extension without postfix not yet implemented");
274
				}
275
				
276
			} else if(key.matches(IGNORE_COLUMN)) {
277
				logger.debug("Ignored column" + originalKey);		
278
			}else {
279
				success = false;
280
				logger.error("Unexpected column header " + originalKey);
281
			}
282
    	}
283
    	return success;
284
	}
285

    
286
	@Override
287
	protected boolean firstPass(SpecimenCdmExcelImportState state) {
288
		SpecimenRow row = state.getSpecimenRow();
289
		
290
		//basis of record
291
		DerivedUnitType type = DerivedUnitType.valueOf2(row.getBasisOfRecord());
292
		if (type == null){
293
			String message = "%s is not a valid BasisOfRecord. 'Unknown' is used instead.";
294
			message = String.format(message, row.getBasisOfRecord());
295
			logger.warn(message);
296
			type = DerivedUnitType.DerivedUnit;
297
		}
298
		DerivedUnitFacade facade = DerivedUnitFacade.NewInstance(type);
299
		
300
		//country
301
		handleCountry(facade, row, state);
302
		handleAreas(facade,row, state);
303
		
304
		facade.setGatheringPeriod(getTimePeriod(row.getCollectingDate(), row.getCollectingDateEnd()));
305
		facade.setLocality(row.getLocality());
306
		facade.setFieldNotes(row.getFieldNotes());
307
		facade.setFieldNumber(row.getCollectorsNumber());
308
		facade.setEcology(row.getEcology());
309
		facade.setPlantDescription(row.getPlantDescription());
310
//		facade.setSex(row.get)
311
		handleExactLocation(facade, row, state);
312
		facade.setCollector(getOrMakeAgent(state, row.getCollectors()));
313
		handleAbsoluteElevation(facade, row, state);
314
		
315
		
316
		//derivedUnit
317
		facade.setBarcode(row.getBarcode());
318
		facade.setAccessionNumber(row.getAccessionNumber());
319
		facade.setCollection(getOrMakeCollection(state, row.getCollectionCode(), row.getCollection()));
320
		for (IdentifiableSource source : row.getSources()){
321
			facade.addSource(source);
322
		}
323
		for (SpecimenTypeDesignation designation : row.getTypeDesignations()){
324
			//FIXME
325
//			facade.innerDerivedUnit().addSpecimenTypeDesignation(designation);
326
		}
327
		handleDeterminations(state, row, facade);
328
		handleExtensions(facade,row, state);
329
		for (String note : row.getUnitNotes()){
330
			Annotation annotation = Annotation.NewInstance(note, AnnotationType.EDITORIAL(), Language.DEFAULT());
331
			facade.addAnnotation(annotation);
332
		}
333
		
334
		//save
335
		getOccurrenceService().save(facade.innerDerivedUnit());
336
		return true;
337
	}
338

    
339
	private void handleAbsoluteElevation(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
340
		//altitude
341
		
342
		try {
343
			String altitude = row.getAltitude();
344
			if (StringUtils.isBlank(altitude)){
345
				return;
346
			}
347
//			if (altitude.endsWith(".0")){
348
//				altitude = altitude.substring(0, altitude.length() -2);
349
//			}
350
			int value = Integer.valueOf(altitude);
351
			facade.setAbsoluteElevation(value);
352
		} catch (NumberFormatException e) {
353
			String message = "Absolute elevation / Altitude '%s' is not an integer number in line %d";
354
			message = String.format(message, row.getAltitude(), state.getCurrentLine());
355
			logger.warn(message);
356
			return;
357
		}
358
		
359
		//max
360
		
361
		try {
362
			String max = row.getAltitudeMax();
363
			if (StringUtils.isBlank(max)){
364
				return;
365
			}
366
//			if (max.endsWith(".0")){
367
//				max = max.substring(0, max.length() -2);
368
//			}
369
			int value = Integer.valueOf(max);
370
			//TODO avoid unequal distance
371
			int min = facade.getAbsoluteElevation();
372
			if ( (value - min) % 2 == 1 ){
373
				String message = "Altitude min-max difference ist not equal. Max reduced by 1 in line %d";
374
				message = String.format(message, state.getCurrentLine());
375
				logger.warn(message);
376
				value--;
377
			}
378
			facade.setAbsoluteElevationRange(min, value);
379
		} catch (NumberFormatException e) {
380
			String message = "Absolute elevation / Altitude maximum '%s' is not an integer number in line %d";
381
			message = String.format(message, row.getAltitudeMax(), state.getCurrentLine());
382
			logger.warn(message);
383
			return;
384
		}catch (Exception e){
385
			String message = "Error occurred when trying to write Absolute elevation / Altitude maximum '%s' in line %d";
386
			message = String.format(message, row.getAltitudeMax(), state.getCurrentLine());
387
			logger.warn(message);
388
			return;
389
			
390
		}
391
		
392
		
393
	}
394

    
395

    
396
	private void handleExtensions(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
397
		List<PostfixTerm> extensions = row.getExtensions();
398
		
399
		for (PostfixTerm exType : extensions){
400
			ExtensionType extensionType = state.getPostfixExtensionType(exType.postfix);
401
			
402
			Extension extension = Extension.NewInstance();
403
			extension.setType(extensionType);
404
			extension.setValue(exType.term);
405
			facade.innerDerivedUnit().addExtension(extension);
406
		}
407
		
408
	}
409

    
410

    
411
	private void handleAreas(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
412
		List<PostfixTerm> areas = row.getLeveledAreas();
413
		
414
		for (PostfixTerm lArea : areas){
415
			String description = lArea.term;
416
			String abbrev = lArea.term;
417
			NamedAreaType type = null;
418
			String key = lArea.postfix + "_" + lArea.term;
419
			UUID areaUuid = state.getArea(key);
420
			NamedAreaLevel level = state.getPostfixLevel(lArea.postfix);
421
			
422
			TermMatchMode matchMode = state.getConfig().getAreaMatchMode();
423
			NamedArea area = getNamedArea(state, areaUuid, lArea.term, description, abbrev, type, level, null, matchMode);
424
			facade.addCollectingArea(area);
425
			if (areaUuid == null){ 
426
				state.putArea(key, area.getUuid());
427
			}
428
		}
429
	}
430

    
431

    
432
	/**
433
	 * @param state
434
	 * @param row
435
	 * @param facade
436
	 */
437
	private void handleDeterminations(SpecimenCdmExcelImportState state,SpecimenRow row, DerivedUnitFacade facade) {
438
		boolean isFirstDetermination = true;
439
		for (DeterminationLight determinationLight : row.getDetermination()){
440
			Taxon taxon = findBestMatchingTaxon(state, determinationLight, state.getConfig().isCreateTaxonIfNotExists());
441
			TaxonNameBase<?,?> name = findBestMatchingName(state, determinationLight);
442
			if (taxon != null){
443
				getTaxonService().saveOrUpdate(taxon);
444
				if (state.getConfig().isMakeIndividualAssociations() && taxon != null){
445
					IndividualsAssociation indivAssociciation = IndividualsAssociation.NewInstance();
446
					DerivedUnitBase<?> du = facade.innerDerivedUnit();
447
					indivAssociciation.setAssociatedSpecimenOrObservation(du);
448
					getTaxonDescription(taxon).addElement(indivAssociciation);
449
					Feature feature = Feature.INDIVIDUALS_ASSOCIATION();
450
					if (facade.getType().equals(DerivedUnitType.Specimen)){
451
						feature = Feature.SPECIMEN();
452
					}else if (facade.getType().equals(DerivedUnitType.Observation)){
453
						feature = Feature.OBSERVATION();
454
					}
455
					if (state.getConfig().isUseMaterialsExaminedForIndividualsAssociations()){
456
						feature = Feature.MATERIALS_EXAMINED();
457
					}
458
					
459
					indivAssociciation.setFeature(feature);
460
				}
461
				if (state.getConfig().isDeterminationsAreDeterminationEvent()){
462
					DeterminationEvent detEvent = makeDeterminationEvent(state, determinationLight, taxon);
463
					detEvent.setPreferredFlag(isFirstDetermination);
464
					facade.addDetermination(detEvent);
465
				}
466
			}
467
			if (name != null){
468
				if (isFirstDetermination && state.getConfig().isFirstDeterminationIsStoredUnder()){
469
					facade.setStoredUnder(name);
470
				}
471
			}
472
			isFirstDetermination = false;
473
		}
474
	}
475

    
476
	/**
477
	 * This method tries to find the best matching taxon depending on the import configuration,
478
	 * the taxon name information and the concept information available.
479
	 * 
480
	 * 
481
	 * @param state
482
	 * @param determinationLight
483
	 * @param createIfNotExists
484
	 * @return
485
	 */
486
	private Taxon findBestMatchingTaxon(SpecimenCdmExcelImportState state, DeterminationLight determinationLight, boolean createIfNotExists) {
487
		NonViralName<?> name = makeTaxonName(state, determinationLight);
488
		
489
		String titleCache = makeSearchNameTitleCache(state, determinationLight, name);
490
		
491
		if (! StringUtils.isBlank(titleCache)){
492
			MatchingTaxonConfigurator matchConfigurator = MatchingTaxonConfigurator.NewInstance();
493
			matchConfigurator.setTaxonNameTitle(titleCache);
494
			matchConfigurator.setIncludeSynonyms(false);
495
			Taxon taxon = getTaxonService().findBestMatchingTaxon(matchConfigurator);
496
		
497
			if(taxon == null && createIfNotExists){
498
				logger.info("creating new Taxon from TaxonName '" + titleCache+"'");
499
				UUID secUuid = null; //TODO
500
				Reference<?> sec = null;
501
				if (secUuid != null){
502
					sec = getReferenceService().find(secUuid);
503
				}
504
				taxon = Taxon.NewInstance(name, sec);
505
			}else if (taxon == null){
506
				String message = "Taxon '%s' not found in line %d";
507
				message = String.format(message, titleCache, state.getCurrentLine());
508
				logger.warn(message);
509
			}
510
			return taxon;
511
		}else {
512
			return null;
513
		}
514
	}
515

    
516
	/**
517
	 * @param state
518
	 * @param determinationLight
519
	 * @param name
520
	 * @return
521
	 */
522
	private String makeSearchNameTitleCache(SpecimenCdmExcelImportState state, DeterminationLight determinationLight, 
523
				NonViralName name) {
524
		String titleCache = determinationLight.fullName;
525
		if (! state.getConfig().isPreferNameCache() || StringUtils.isBlank(titleCache) ){
526
			String computedTitleCache = name.getTitleCache();
527
			if (StringUtils.isNotBlank(computedTitleCache)){
528
				titleCache = computedTitleCache;
529
			}
530
			
531
		}
532
		return titleCache;
533
	}
534

    
535
	/**
536
	 * @param state
537
	 * @param determinationLight
538
	 * @return
539
	 */
540
	private NonViralName makeTaxonName(SpecimenCdmExcelImportState state, DeterminationLight determinationLight) {
541
		//TODO correct type by config.nc
542
		NonViralName name =NonViralName.NewInstance(null);
543
		name.setGenusOrUninomial(determinationLight.genus);
544
		name.setSpecificEpithet(determinationLight.speciesEpi);
545
		name.setInfraSpecificEpithet(determinationLight.infraSpeciesEpi);
546
		
547
		//FIXME bracketAuthors and teams not yet implemented!!!
548
		List<String> authors = new ArrayList<String>();
549
		if (StringUtils.isNotBlank(determinationLight.author)){
550
			authors.add(determinationLight.author);
551
		}
552
		TeamOrPersonBase agent = (TeamOrPersonBase)getOrMakeAgent(state, authors);
553
		name.setCombinationAuthorTeam(agent);
554
		
555
		NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
556
		try {
557
			if (StringUtils.isNotBlank(determinationLight.rank) ){
558
				name.setRank(Rank.getRankByNameOrAbbreviation(determinationLight.rank, nc, true));
559
			}
560
		} catch (UnknownCdmTypeException e) {
561
			String message = "Rank not found: %s: ";
562
			message = String.format(message, determinationLight.rank);
563
			logger.warn(message);
564
		}
565
		if (StringUtils.isBlank(name.getInfraSpecificEpithet()) && StringUtils.isNotBlank(name.getSpecificEpithet() )){
566
			name.setRank(Rank.SPECIES());
567
		}
568
		if (StringUtils.isBlank(name.getSpecificEpithet()) && StringUtils.isNotBlank(name.getGenusOrUninomial() )){
569
			name.setRank(Rank.SPECIES());
570
		}
571
		if (StringUtils.isBlank(name.getTitleCache())){
572
			//TODO test
573
			name.setTitleCache(determinationLight.fullName, true);
574
		}
575
		return name;
576
	}
577

    
578
	private TaxonNameBase findBestMatchingName(SpecimenCdmExcelImportState state, DeterminationLight determinationLight) {
579
		
580
		NonViralName name = makeTaxonName(state, determinationLight);
581
		String titleCache = makeSearchNameTitleCache(state, determinationLight, name);
582
		
583
		//TODO
584
		List<TaxonNameBase> matchingNames = getNameService().findByName(null, titleCache, MatchMode.EXACT, null, null, null, null, null).getRecords();
585
		if (matchingNames.size() > 0){
586
			return matchingNames.get(0);
587
		} else if (matchingNames.size() > 0){
588
			logger.warn("Get best matching taxon name not yet fully implemeted for specimen import");
589
			return matchingNames.get(0);
590
		}else{
591
			return null;	
592
		}
593
		
594
	}
595

    
596
	
597
	private DeterminationEvent makeDeterminationEvent(SpecimenCdmExcelImportState state, DeterminationLight determination, Taxon taxon) {
598
		DeterminationEvent event = DeterminationEvent.NewInstance();
599
		//taxon
600
		event.setTaxon(taxon);
601
		
602
		//date
603
		TimePeriod date = TimePeriod.parseString(determination.determinedWhen);
604
		event.setTimeperiod(date);
605
		//by
606
		//FIXME bracketAuthors and teams not yet implemented!!!
607
		List<String> authors = new ArrayList<String>();
608
		if (StringUtils.isNotBlank(determination.determinedBy)){
609
			authors.add(determination.determinedBy);
610
		}
611
		AgentBase actor = getOrMakeAgent(state, authors);
612
		event.setActor(actor);
613
		
614
		//TODO
615
		if (StringUtils.isNotBlank(determination.modifier)){
616
			logger.warn("DeterminationModifiers not yet implemented for specimen import");
617
		}
618
//		DeterminationModifier modifier = DeterminationModifier.NewInstance(term, label, labelAbbrev);
619
//		determination.modifier;
620
		//notes
621
		Annotation annotation = Annotation.NewInstance(determination.notes, AnnotationType.EDITORIAL(), Language.DEFAULT());
622
		event.addAnnotation(annotation);
623

    
624
		return event;
625
	}
626

    
627
	private TaxonDescription getTaxonDescription(Taxon taxon) {
628
		TaxonDescription desc = this.getTaxonDescription(taxon, ! IMAGE_GALLERY, CREATE);
629
		return desc;
630
	}
631

    
632
	private AgentBase<?> getOrMakeAgent(SpecimenCdmExcelImportState state, List<String> agents) {
633
		if (agents.size() == 0){
634
			return null;
635
		}else if (agents.size() == 1){
636
			return getOrMakePerson(state, agents.get(0));
637
		}else{
638
			return getOrMakeTeam(state, agents);
639
		}
640
	}
641

    
642
	private Team getOrMakeTeam(SpecimenCdmExcelImportState state, List<String> agents) {
643
		String key = CdmUtils.concat("_", agents.toArray(new String[0]));
644
		
645
		Team result = state.getTeam(key);
646
		if (result == null){
647
			result = Team.NewInstance();
648
			for (String member : agents){
649
				Person person = getOrMakePerson(state, member);
650
				result.addTeamMember(person);
651
			}
652
			state.putTeam(key, result);
653
		}
654
		return result;
655
	}
656

    
657
	private Person getOrMakePerson(SpecimenCdmExcelImportState state, String value) {
658
		Person result = state.getPerson(value);
659
		if (result == null){
660
			result = Person.NewInstance();
661
			result.setTitleCache(value, true);
662
			state.putPerson(value, result);
663
		}
664
		return result;
665
	}
666

    
667
	private Reference<?> getOrMakeReference(SpecimenCdmExcelImportState state, String value) {
668
		Reference<?> result = state.getReference(value);
669
		if (result == null){
670
			result = ReferenceFactory.newGeneric();
671
			result.setTitleCache(value, true);
672
			state.putReference(value, result);
673
		}
674
		return result;
675
	}
676

    
677

    
678

    
679
	private Collection getOrMakeCollection(SpecimenCdmExcelImportState state, String collectionCode, String collectionString) {
680
		Collection result = state.getCollection(collectionCode);
681
		if (result == null){
682
			result = Collection.NewInstance();
683
			result.setCode(collectionCode);
684
			result.setName(collectionString);
685
			state.putCollection(collectionCode, result);
686
		}
687
		return result;
688
	}
689
	
690

    
691
	private TaxonNameBase<?, ?> getTaxonName(SpecimenCdmExcelImportState state, String name) {
692
		TaxonNameBase<?,?> result = null;
693
		result = state.getName(name);
694
		if (result != null){
695
			return result;
696
		}
697
		List<TaxonNameBase<?,?>> list = getNameService().findNamesByTitle(name);
698
		//TODO better strategy to find best name, e.g. depending on the classification it is used in
699
		if (! list.isEmpty()){
700
			result = list.get(0);
701
		}
702
		if (result == null){
703
			NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
704
			NomenclaturalCode code = state.getConfig().getNomenclaturalCode();
705
			result = parser.parseFullName(name, code, null);
706
			
707
		}
708
		if (result != null){
709
			state.putName(name, result);
710
		}
711
		return result;
712
	}
713

    
714
	private SpecimenTypeDesignationStatus getSpecimenTypeStatus(SpecimenCdmExcelImportState state, String key)  {
715
		SpecimenTypeDesignationStatus result = null;
716
		try {
717
			result = state.getTransformer().getSpecimenTypeDesignationStatusByKey(key);
718
			if (result == null){
719
				String message = "Type status not recognized for %s in line %d";
720
				message = String.format(message, key, state.getCurrentLine());
721
				logger.warn(message);
722
			}
723
			return result;
724
		} catch (UndefinedTransformerMethodException e) {
725
			throw new RuntimeException("getSpecimenTypeDesignationStatusByKey not yet implemented");
726
		}
727
		
728
		
729
	}
730

    
731

    
732
	private void handleExactLocation(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
733
			
734
		//reference system
735
		ReferenceSystem refSys = null;
736
		if (StringUtils.isNotBlank(row.getReferenceSystem())){
737
			String strRefSys = row.getReferenceSystem().trim().replaceAll("\\s", "");
738
			UUID refUuid;
739
			try {
740
				refSys = state.getTransformer().getReferenceSystemByKey(strRefSys);
741
				if (refSys == null){
742
					refUuid = state.getTransformer().getReferenceSystemUuid(strRefSys);
743
					if (refUuid == null){
744
						String message = "Unknown reference system %s in line %d";
745
						message = String.format(message, strRefSys, state.getCurrentLine());
746
						logger.warn(message);
747
					}
748
					refSys = getReferenceSystem(state, refUuid, strRefSys, strRefSys, strRefSys, null);
749
				}
750
				
751
			} catch (UndefinedTransformerMethodException e) {
752
				throw new RuntimeException(e);
753
			}
754
		}
755

    
756
		
757
		
758
		// lat/ long /error
759
		try {
760
			String longitude = row.getLongitude();
761
			String latitude = row.getLatitude();
762
			Integer errorRadius = null;
763
			if (StringUtils.isNotBlank(row.getErrorRadius())){
764
				try {
765
					errorRadius = Integer.valueOf(row.getErrorRadius());
766
				} catch (NumberFormatException e) {
767
					String message = "Error radius %s could not be transformed to Integer in line %d";
768
					message = String.format(message, row.getErrorRadius(), state.getCurrentLine());
769
					logger.warn(message);
770
				}
771
			}
772
			//all
773
			facade.setExactLocationByParsing(longitude, latitude, refSys, errorRadius);
774
		} catch (ParseException e) {
775
			String message = "Problems when parsing exact location for line %d";
776
			message = String.format(message, state.getCurrentLine());
777
			logger.warn(message);
778
			
779
		}
780
		
781
		
782
		
783
	}
784

    
785

    
786
	/*
787
	 * Set the current Country
788
	 * Search in the DB if the isoCode is known
789
	 * If not, search if the country name is in the DB
790
	 * If not, create a new Label with the Level Country
791
	 * @param iso: the country iso code
792
	 * @param fullName: the country's full name
793
	 * @param app: the CDM application controller
794
	 */
795
	private void handleCountry(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
796
		
797
		if (StringUtils.isNotBlank(row.getIsoCountry())){
798
			NamedArea country = getOccurrenceService().getCountryByIso(row.getIsoCountry());
799
			if (country != null){
800
				facade.setCountry(country);
801
				return;
802
			}
803
		}
804
		if (StringUtils.isNotBlank(row.getCountry())){
805
			List<WaterbodyOrCountry> countries = getOccurrenceService().getWaterbodyOrCountryByName(row.getCountry());
806
			if (countries.size() >0){
807
				facade.setCountry(countries.get(0));
808
			}else{
809
				UUID uuid = UUID.randomUUID();
810
				String label = row.getCountry();
811
				String text = row.getCountry();
812
				String labelAbbrev = null;
813
				NamedAreaType areaType = NamedAreaType.ADMINISTRATION_AREA();
814
				NamedAreaLevel level = NamedAreaLevel.COUNTRY();
815
				NamedArea newCountry = this.getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level);
816
				facade.setCountry(newCountry);
817
			}
818
		}
819
	}
820
		
821

    
822
	
823
	
824
	protected boolean isInteger(String value){
825
		try {
826
			Integer.valueOf(value);
827
			return true;
828
		} catch (NumberFormatException e) {
829
			return false;
830
		}
831
	}
832

    
833
	@Override
834
	protected boolean secondPass(SpecimenCdmExcelImportState state) {
835
		//no second path defined yet
836
		return true;
837
	}
838

    
839

    
840
	@Override
841
	protected String getWorksheetName() {
842
		return WORKSHEET_NAME;
843
	}
844
	
845
	@Override
846
	protected boolean needsNomenclaturalCode() {
847
		return false;
848
	}
849
	
850
	
851
	/* (non-Javadoc)
852
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
853
	 */
854
	@Override
855
	protected boolean doCheck(SpecimenCdmExcelImportState state) {
856
		logger.warn("Validation not yet implemented for " + this.getClass().getSimpleName());
857
		return true;
858
	}
859

    
860

    
861

    
862
	@Override
863
	protected boolean isIgnore(SpecimenCdmExcelImportState state) {
864
		return !state.getConfig().isDoSpecimen();
865
	}
866

    
867

    
868
}
(5-5/12)