2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.io
.specimen
.excel
.in
;
12 import java
.text
.ParseException
;
13 import java
.util
.ArrayList
;
14 import java
.util
.List
;
15 import java
.util
.UUID
;
17 import org
.apache
.commons
.lang
.StringUtils
;
18 import org
.apache
.log4j
.Logger
;
19 import org
.springframework
.stereotype
.Component
;
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
.INonViralName
;
49 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalCode
;
50 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
51 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
52 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignation
;
53 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignationStatus
;
54 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
55 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
56 import eu
.etaxonomy
.cdm
.model
.occurrence
.Collection
;
57 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
58 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
59 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationType
;
60 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
61 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceFactory
;
62 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
63 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
64 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
65 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.StringNotParsableException
;
66 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.UnknownCdmTypeException
;
67 import eu
.etaxonomy
.cdm
.strategy
.parser
.INonViralNameParser
;
68 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImpl
;
69 import eu
.etaxonomy
.cdm
.strategy
.parser
.TimePeriodParser
;
77 public class SpecimenCdmExcelImport
extends ExcelTaxonOrSpecimenImportBase
<SpecimenCdmExcelImportState
, SpecimenRow
> implements ICdmIO
<SpecimenCdmExcelImportState
> {
78 private static final Logger logger
= Logger
.getLogger(SpecimenCdmExcelImport
.class);
80 private static final String WORKSHEET_NAME
= "Specimen";
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 COLLECTORS_COLUMN
= "(?i)(Collectors)";
93 private static final String PRIMARY_COLLECTOR_COLUMN
= "(?i)(PrimaryCollector)";
94 private static final String LONGITUDE_COLUMN
= "(?i)(Longitude)";
95 private static final String LATITUDE_COLUMN
= "(?i)(Latitude)";
96 private static final String REFERENCE_SYSTEM_COLUMN
= "(?i)(ReferenceSystem)";
97 private static final String ERROR_RADIUS_COLUMN
= "(?i)(ErrorRadius)";
100 private static final String COLLECTORS_NUMBER_COLUMN
= "(?i)((Collectors|Field)Number)";
101 private static final String ECOLOGY_COLUMN
= "(?i)(Ecology|Habitat)";
102 private static final String PLANT_DESCRIPTION_COLUMN
= "(?i)(PlantDescription)";
103 private static final String FIELD_NOTES_COLUMN
= "(?i)(FieldNotes)";
104 private static final String SEX_COLUMN
= "(?i)(Sex)";
107 private static final String ACCESSION_NUMBER_COLUMN
= "(?i)(AccessionNumber)";
108 private static final String BARCODE_COLUMN
= "(?i)(Barcode)";
109 private static final String COLLECTION_CODE_COLUMN
= "(?i)(CollectionCode)";
110 private static final String COLLECTION_COLUMN
= "(?i)(Collection)";
111 private static final String UNIT_NOTES_COLUMN
= "(?i)((Unit)?Notes)";
114 private static final String TYPE_CATEGORY_COLUMN
= "(?i)(TypeCategory)";
115 private static final String TYPIFIED_NAME_COLUMN
= "(?i)(TypifiedName|TypeOf)";
118 private static final String SOURCE_COLUMN
= "(?i)(Source)";
119 private static final String ID_IN_SOURCE_COLUMN
= "(?i)(IdInSource)";
122 private static final String DETERMINATION_AUTHOR_COLUMN
= "(?i)(Author)";
123 private static final String DETERMINATION_MODIFIER_COLUMN
= "(?i)(DeterminationModifier)";
124 private static final String DETERMINED_BY_COLUMN
= "(?i)(DeterminationBy)";
125 private static final String DETERMINED_WHEN_COLUMN
= "(?i)(Det(ermination)?When)";
126 private static final String DETERMINATION_NOTES_COLUMN
= "(?i)(DeterminationNote)";
127 private static final String EXTENSION_COLUMN
= "(?i)(Ext(ension)?)";
130 public SpecimenCdmExcelImport() {
138 protected void analyzeSingleValue(KeyValue keyValue
, SpecimenCdmExcelImportState state
) {
139 SpecimenRow row
= state
.getCurrentRow();
140 String value
= keyValue
.value
;
141 if(keyValue
.key
.matches(BASIS_OF_RECORD_COLUMN
)) {
142 row
.setBasisOfRecord(value
);
143 } else if(keyValue
.key
.matches(COUNTRY_COLUMN
)) {
144 row
.setCountry(value
);
145 } else if(keyValue
.key
.matches(ISO_COUNTRY_COLUMN
)) {
146 row
.setIsoCountry(value
);
147 } else if(keyValue
.key
.matches(LOCALITY_COLUMN
)) {
148 row
.setLocality(value
);
149 } else if(keyValue
.key
.matches(FIELD_NOTES_COLUMN
)) {
150 row
.setLocality(value
);
151 } else if(keyValue
.key
.matches(ALTITUDE_COLUMN
)) {
152 row
.setAltitude(value
);
153 } else if(keyValue
.key
.matches(ALTITUDE_MAX_COLUMN
)) {
154 row
.setAltitudeMax(value
);
155 } else if(keyValue
.key
.matches(COLLECTOR_COLUMN
)) {
156 row
.putCollector(keyValue
.index
, value
);
157 } else if(keyValue
.key
.matches(PRIMARY_COLLECTOR_COLUMN
)) {
158 row
.setPrimaryCollector(value
);
159 } else if(keyValue
.key
.matches(ECOLOGY_COLUMN
)) {
160 row
.setEcology(value
);
161 } else if(keyValue
.key
.matches(PLANT_DESCRIPTION_COLUMN
)) {
162 row
.setPlantDescription(value
);
163 } else if(keyValue
.key
.matches(SEX_COLUMN
)) {
165 } else if(keyValue
.key
.matches(COLLECTION_DATE_COLUMN
)) {
166 row
.setCollectingDate(value
);
167 } else if(keyValue
.key
.matches(COLLECTION_DATE_END_COLUMN
)) {
168 row
.setCollectingDateEnd(value
);
169 } else if(keyValue
.key
.matches(COLLECTORS_COLUMN
)) {
170 row
.setCollectors(value
);
171 } else if(keyValue
.key
.matches(COLLECTOR_COLUMN
)) {
172 row
.putCollector(keyValue
.index
, value
);
173 } else if(keyValue
.key
.matches(COLLECTORS_NUMBER_COLUMN
)) {
174 row
.setCollectorsNumber(value
);
175 } else if(keyValue
.key
.matches(LONGITUDE_COLUMN
)) {
176 row
.setLongitude(value
);
177 } else if(keyValue
.key
.matches(LATITUDE_COLUMN
)) {
178 row
.setLatitude(value
);
179 } else if(keyValue
.key
.matches(REFERENCE_SYSTEM_COLUMN
)) {
180 row
.setReferenceSystem(value
);
181 } else if(keyValue
.key
.matches(ERROR_RADIUS_COLUMN
)) {
182 row
.setErrorRadius(value
);
183 } else if(keyValue
.key
.matches(AREA_COLUMN
)) {
184 if (keyValue
.postfix
!= null){
185 row
.addLeveledArea(keyValue
.postfix
, value
);
187 logger
.warn("Not yet implemented");
189 } else if(keyValue
.key
.matches(LANGUAGE
)) {
190 row
.setLanguage(value
);
193 } else if(keyValue
.key
.matches(ACCESSION_NUMBER_COLUMN
)) {
194 row
.setAccessionNumber(value
);
195 } else if(keyValue
.key
.matches(BARCODE_COLUMN
)) {
196 row
.setBarcode(value
);
197 } else if(keyValue
.key
.matches(UNIT_NOTES_COLUMN
)) {
198 row
.putUnitNote(keyValue
.index
, value
);
201 } else if(keyValue
.key
.matches(FAMILY_COLUMN
)) {
202 row
.putDeterminationFamily(keyValue
.index
, value
);
203 } else if(keyValue
.key
.matches(GENUS_COLUMN
)) {
204 row
.putDeterminationGenus(keyValue
.index
, value
);
205 } else if(keyValue
.key
.matches(SPECIFIC_EPITHET_COLUMN
)) {
206 row
.putDeterminationSpeciesEpi(keyValue
.index
, value
);
207 } else if(keyValue
.key
.matches(INFRASPECIFIC_EPITHET_COLUMN
)) {
208 row
.putDeterminationInfraSpeciesEpi(keyValue
.index
, value
);
209 } else if(keyValue
.key
.matches(RANK_COLUMN
)) {
210 row
.putDeterminationRank(keyValue
.index
, value
);
211 } else if(keyValue
.key
.matches(TAXON_UUID_COLUMN
)) {
212 row
.putDeterminationTaxonUuid(keyValue
.index
, value
);
213 } else if(keyValue
.key
.matches(FULL_NAME_COLUMN
)) {
214 row
.putDeterminationFullName(keyValue
.index
, value
);
215 } else if(keyValue
.key
.matches(DETERMINATION_AUTHOR_COLUMN
)) {
216 row
.putDeterminationAuthor(keyValue
.index
, value
);
217 } else if(keyValue
.key
.matches(DETERMINATION_MODIFIER_COLUMN
)) {
218 row
.putDeterminationDeterminationModifier(keyValue
.index
, value
);
219 } else if(keyValue
.key
.matches(DETERMINATION_NOTES_COLUMN
)) {
220 row
.putDeterminationDeterminationNotes(keyValue
.index
, value
);
221 } else if(keyValue
.key
.matches(DETERMINED_BY_COLUMN
)) {
222 row
.putDeterminationDeterminedBy(keyValue
.index
, value
);
223 } else if(keyValue
.key
.matches(DETERMINED_WHEN_COLUMN
)) {
224 row
.putDeterminationDeterminedWhen(keyValue
.index
, value
);
226 } else if(keyValue
.key
.matches(COLLECTION_CODE_COLUMN
)) {
227 row
.setCollectionCode(value
);
228 } else if(keyValue
.key
.matches(COLLECTION_COLUMN
)) {
229 row
.setCollection(value
);
231 } else if(keyValue
.key
.matches(TYPE_CATEGORY_COLUMN
)) {
232 row
.putTypeCategory(keyValue
.index
, getSpecimenTypeStatus(state
, value
));
233 } else if(keyValue
.key
.matches(TYPIFIED_NAME_COLUMN
)) {
234 row
.putTypifiedName(keyValue
.index
, getTaxonName(state
, value
));
237 } else if(keyValue
.key
.matches(SOURCE_COLUMN
)) {
238 row
.putSourceReference(keyValue
.index
, getOrMakeReference(state
, value
) );
239 } else if(keyValue
.key
.matches(ID_IN_SOURCE_COLUMN
)) {
240 row
.putIdInSource(keyValue
.index
, value
);
241 } else if(keyValue
.key
.matches(EXTENSION_COLUMN
)) {
242 if (keyValue
.postfix
!= null){
243 row
.addExtension(keyValue
.postfix
, value
);
245 logger
.warn("Extension without postfix not yet implemented");
249 state
.setUnsuccessfull();
250 logger
.error("Unexpected column header " + keyValue
.originalKey
);
258 protected void firstPass(SpecimenCdmExcelImportState state
) {
259 SpecimenRow row
= state
.getCurrentRow();
262 SpecimenOrObservationType type
= SpecimenOrObservationType
.valueOf2(row
.getBasisOfRecord());
264 String message
= "%s is not a valid BasisOfRecord. 'Unknown' is used instead in line %d.";
265 message
= String
.format(message
, row
.getBasisOfRecord(), state
.getCurrentLine());
266 logger
.warn(message
);
267 type
= SpecimenOrObservationType
.DerivedUnit
;
269 DerivedUnitFacade facade
= DerivedUnitFacade
.NewInstance(type
);
272 Language lang
= Language
.DEFAULT();
273 if (StringUtils
.isNotBlank(row
.getLanguage())){
274 Language langIso
= getTermService().getLanguageByIso(row
.getLanguage());
275 if (langIso
== null){
276 String message
= "Language could not be recognized: %s. Use default language instead. Line %d.";
277 message
= String
.format(message
, langIso
, state
.getCurrentLine());
284 handleCountry(facade
, row
, state
);
285 handleAreas(facade
,row
, state
);
287 facade
.setGatheringPeriod(getTimePeriod(row
.getCollectingDate(), row
.getCollectingDateEnd()));
288 facade
.setLocality(row
.getLocality());
289 facade
.setFieldNotes(row
.getFieldNotes());
290 facade
.setFieldNumber(row
.getCollectorsNumber());
291 facade
.setEcology(row
.getEcology(), lang
);
292 facade
.setPlantDescription(row
.getPlantDescription(), lang
);
293 // facade.setSex(row.get)
294 handleExactLocation(facade
, row
, state
);
295 facade
.setCollector(getOrMakeAgent(state
, row
.getCollectors()));
296 facade
.setPrimaryCollector(getOrMakePrimaryCollector(facade
, row
.getPrimaryCollector(), state
));
297 handleAbsoluteElevation(facade
, row
, state
);
300 facade
.setBarcode(row
.getBarcode());
301 facade
.setAccessionNumber(row
.getAccessionNumber());
302 facade
.setCollection(getOrMakeCollection(state
, row
.getCollectionCode(), row
.getCollection()));
303 for (IdentifiableSource source
: row
.getSources()){
304 facade
.addSource(source
);
306 for (SpecimenTypeDesignation designation
: row
.getTypeDesignations()){
307 logger
.warn("FIXME"); //FIXME
308 // facade.innerDerivedUnit().addSpecimenTypeDesignation(designation);
310 handleDeterminations(state
, row
, facade
);
311 handleExtensions(facade
.innerDerivedUnit(),row
, state
);
312 for (String note
: row
.getUnitNotes()){
313 Annotation annotation
= Annotation
.NewInstance(note
, AnnotationType
.EDITORIAL(), Language
.DEFAULT());
314 facade
.addAnnotation(annotation
);
318 getOccurrenceService().save(facade
.innerDerivedUnit());
322 private void handleAbsoluteElevation(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
326 String altitude
= row
.getAltitude();
327 if (StringUtils
.isBlank(altitude
)){
330 // if (altitude.endsWith(".0")){
331 // altitude = altitude.substring(0, altitude.length() -2);
333 int value
= Integer
.valueOf(altitude
);
334 facade
.setAbsoluteElevation(value
);
335 } catch (NumberFormatException e
) {
336 String message
= "Absolute elevation / altitude '%s' is not an integer number in line %d";
337 message
= String
.format(message
, row
.getAltitude(), state
.getCurrentLine());
338 logger
.warn(message
);
345 String max
= row
.getAltitudeMax();
346 if (StringUtils
.isBlank(max
)){
349 // if (max.endsWith(".0")){
350 // max = max.substring(0, max.length() -2);
352 int value
= Integer
.valueOf(max
);
353 //TODO avoid unequal distance
354 int min
= facade
.getAbsoluteElevation();
355 if ( (value
- min
) % 2 == 1 ){
356 String message
= "Altitude min-max difference ist not equal. Max reduced by 1 in line %d";
357 message
= String
.format(message
, state
.getCurrentLine());
358 logger
.warn(message
);
361 facade
.setAbsoluteElevationRange(min
, value
);
362 } catch (NumberFormatException e
) {
363 String message
= "Absolute elevation / Altitude maximum '%s' is not an integer number in line %d";
364 message
= String
.format(message
, row
.getAltitudeMax(), state
.getCurrentLine());
365 logger
.warn(message
);
367 }catch (Exception e
){
368 String message
= "Error occurred when trying to write Absolute elevation / Altitude maximum '%s' in line %d";
369 message
= String
.format(message
, row
.getAltitudeMax(), state
.getCurrentLine());
370 logger
.warn(message
);
378 private void handleAreas(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
379 List
<PostfixTerm
> areas
= row
.getLeveledAreas();
381 for (PostfixTerm lArea
: areas
){
382 String description
= lArea
.term
;
383 String abbrev
= lArea
.term
;
384 NamedAreaType type
= null;
385 String key
= lArea
.postfix
+ "_" + lArea
.term
;
386 UUID areaUuid
= state
.getArea(key
);
387 NamedAreaLevel level
= state
.getPostfixLevel(lArea
.postfix
);
389 TermMatchMode matchMode
= state
.getConfig().getAreaMatchMode();
390 NamedArea area
= getNamedArea(state
, areaUuid
, lArea
.term
, description
, abbrev
, type
, level
, null, matchMode
);
391 facade
.addCollectingArea(area
);
392 if (areaUuid
== null){
393 state
.putArea(key
, area
.getUuid());
404 private void handleDeterminations(SpecimenCdmExcelImportState state
,SpecimenRow row
, DerivedUnitFacade facade
) {
405 boolean isFirstDetermination
= true;
406 DeterminationLight commonDetermination
= row
.getCommonDetermination();
407 Taxon commonTaxon
= null;
408 TaxonNameBase
<?
,?
> commonName
= null;
410 boolean hasCommonTaxonInfo
= (commonDetermination
== null) ?
false : commonDetermination
.hasTaxonInformation();
411 if (hasCommonTaxonInfo
&& commonDetermination
!= null){
412 TaxonBase
<?
> taxonBase
= null;
413 if (StringUtils
.isNotBlank(commonDetermination
.taxonUuid
)){
414 UUID taxonUuid
= UUID
.fromString(commonDetermination
.taxonUuid
);
415 taxonBase
= getTaxonService().find(taxonUuid
);
416 if (taxonBase
== null){
417 String message
= "Taxon for uuid %s not found in line %d.";
418 message
= String
.format(message
, taxonUuid
.toString(), state
.getCurrentLine());
419 logger
.warn(message
);
422 taxonBase
= findBestMatchingTaxon(state
, commonDetermination
, state
.getConfig().isCreateTaxonIfNotExists());
424 commonTaxon
= getAcceptedTaxon(taxonBase
);
425 if (taxonBase
!= null){
426 commonName
= taxonBase
.getName();
428 commonTaxon
= createTaxonFromDetermination(state
, commonDetermination
);
429 commonName
= commonTaxon
.getName();
434 for (DeterminationLight determinationLight
: row
.getDetermination()){
436 if (! hasCommonTaxonInfo
){
437 taxon
= findBestMatchingTaxon(state
, determinationLight
, state
.getConfig().isCreateTaxonIfNotExists());
442 getTaxonService().saveOrUpdate(taxon
);
443 if (state
.getConfig().isMakeIndividualAssociations() && taxon
!= null){
444 IndividualsAssociation indivAssociciation
= IndividualsAssociation
.NewInstance();
445 DerivedUnit du
= facade
.innerDerivedUnit();
446 indivAssociciation
.setAssociatedSpecimenOrObservation(du
);
447 getTaxonDescription(taxon
).addElement(indivAssociciation
);
448 Feature feature
= Feature
.INDIVIDUALS_ASSOCIATION();
449 if (facade
.getType().isPreservedSpecimen()){
450 feature
= Feature
.SPECIMEN();
451 }else if (facade
.getType().isFeatureObservation()){
452 feature
= Feature
.OBSERVATION();
454 if (state
.getConfig().isUseMaterialsExaminedForIndividualsAssociations()){
455 feature
= Feature
.MATERIALS_EXAMINED();
458 indivAssociciation
.setFeature(feature
);
460 if (state
.getConfig().isDeterminationsAreDeterminationEvent()){
461 DeterminationEvent detEvent
= makeDeterminationEvent(state
, determinationLight
, taxon
);
462 detEvent
.setPreferredFlag(isFirstDetermination
);
463 facade
.addDetermination(detEvent
);
467 if (isFirstDetermination
&& state
.getConfig().isFirstDeterminationIsStoredUnder()){
468 TaxonNameBase
<?
,?
> name
;
470 if (!hasCommonTaxonInfo
){
471 name
= findBestMatchingName(state
, determinationLight
);
473 if (commonName
== null){
474 commonName
= findBestMatchingName(state
, commonDetermination
);
479 facade
.setStoredUnder(name
);
482 isFirstDetermination
= false;
486 private Taxon
createTaxonFromDetermination( SpecimenCdmExcelImportState state
, DeterminationLight commonDetermination
) {
491 rank
= StringUtils
.isBlank(commonDetermination
.rank
) ?
null : Rank
.getRankByNameOrIdInVoc(commonDetermination
.rank
, true);
492 } catch (UnknownCdmTypeException e
) {
498 INonViralNameParser parser
= NonViralNameParserImpl
.NewInstance();
499 NomenclaturalCode nc
= state
.getConfig().getNomenclaturalCode();
500 if (StringUtils
.isNotBlank(commonDetermination
.fullName
)){
501 name
= parser
.parseFullName(commonDetermination
.fullName
, nc
, rank
);
502 if (StringUtils
.isBlank(name
.getAuthorshipCache()) && StringUtils
.isNotBlank(commonDetermination
.author
)){
503 setAuthorship(name
, commonDetermination
.author
, parser
);
507 name
= nc
.getNewTaxonNameInstance(rank
);
509 name
= NonViralName
.NewInstance(rank
);
511 if (StringUtils
.isNotBlank(commonDetermination
.genus
)){
512 name
.setGenusOrUninomial(commonDetermination
.genus
);
514 if (StringUtils
.isNotBlank(commonDetermination
.speciesEpi
)){
515 name
.setSpecificEpithet(commonDetermination
.speciesEpi
);
517 if (StringUtils
.isNotBlank(commonDetermination
.infraSpeciesEpi
)){
518 name
.setInfraSpecificEpithet(commonDetermination
.infraSpeciesEpi
);
520 if (StringUtils
.isNotBlank(commonDetermination
.author
)){
521 setAuthorship(name
, commonDetermination
.author
, parser
);
524 if (name
.getRank() == null){
525 if (name
.getInfraGenericEpithet() != null && name
.getSpecificEpithet() == null){
526 name
.setRank(Rank
.INFRAGENERICTAXON());
527 }else if (name
.getSpecificEpithet() != null && name
.getInfraSpecificEpithet() == null){
528 name
.setRank(Rank
.SPECIES());
529 }else if (name
.getInfraSpecificEpithet() != null){
530 name
.setRank(Rank
.INFRASPECIFICTAXON());
537 Reference sec
= null;
538 if (StringUtils
.isNotBlank(commonDetermination
.determinedBy
)){
539 sec
= ReferenceFactory
.newGeneric();
540 TeamOrPersonBase
<?
> determinedBy
;
541 BotanicalName dummyName
= BotanicalName
.NewInstance(Rank
.SPECIES());
543 parser
.parseAuthors(dummyName
, commonDetermination
.determinedBy
);
544 determinedBy
= dummyName
.getCombinationAuthorship();
545 } catch (StringNotParsableException e
) {
546 determinedBy
= Team
.NewTitledInstance(commonDetermination
.determinedBy
, commonDetermination
.determinedBy
);
548 sec
.setAuthorship(determinedBy
);
552 Taxon taxon
= Taxon
.NewInstance(name
, sec
);
554 if (StringUtils
.isNotBlank(commonDetermination
.family
)){
555 if (name
.getRank() == null || name
.getRank().isLower(Rank
.FAMILY()) ){
556 logger
.warn("Family taxon could not be created");
568 private void setAuthorship(INonViralName name
, String author
, INonViralNameParser
<NonViralName
> parser
) {
569 if (name
instanceof BotanicalName
|| name
instanceof ZoologicalName
){
571 parser
.parseAuthors(name
, author
);
572 } catch (StringNotParsableException e
) {
573 name
.setAuthorshipCache(author
);
576 name
.setAuthorshipCache(author
);
583 * This method tries to find the best matching taxon depending on the import configuration,
584 * the taxon name information and the concept information available.
588 * @param determinationLight
589 * @param createIfNotExists
592 private Taxon
findBestMatchingTaxon(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
, boolean createIfNotExists
) {
593 NonViralName
<?
> name
= makeTaxonName(state
, determinationLight
);
595 String titleCache
= makeSearchNameTitleCache(state
, determinationLight
, name
);
597 if (! StringUtils
.isBlank(titleCache
)){
598 MatchingTaxonConfigurator matchConfigurator
= MatchingTaxonConfigurator
.NewInstance();
599 matchConfigurator
.setTaxonNameTitle(titleCache
);
600 matchConfigurator
.setIncludeSynonyms(false);
601 Taxon taxon
= getTaxonService().findBestMatchingTaxon(matchConfigurator
);
603 if(taxon
== null && createIfNotExists
){
604 logger
.info("creating new Taxon from TaxonName '" + titleCache
+"'");
605 UUID secUuid
= null; //TODO
606 Reference sec
= null;
607 if (secUuid
!= null){
608 sec
= getReferenceService().find(secUuid
);
610 taxon
= Taxon
.NewInstance(name
, sec
);
611 }else if (taxon
== null){
612 String message
= "Taxon '%s' not found in line %d";
613 message
= String
.format(message
, titleCache
, state
.getCurrentLine());
614 logger
.warn(message
);
624 * @param determinationLight
628 private String
makeSearchNameTitleCache(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
,
629 NonViralName
<?
> name
) {
630 String titleCache
= determinationLight
.fullName
;
631 if (! state
.getConfig().isPreferNameCache() || StringUtils
.isBlank(titleCache
) ){
632 String computedTitleCache
= name
.getTitleCache();
633 if (StringUtils
.isNotBlank(computedTitleCache
)){
634 titleCache
= computedTitleCache
;
643 * @param determinationLight
646 private NonViralName
<?
> makeTaxonName(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
) {
647 NonViralName
<?
> name
= NonViralName
.NewInstance(null);
648 NomenclaturalCode nc
= state
.getConfig().getNomenclaturalCode();
650 name
= (NonViralName
<?
>)nc
.getNewTaxonNameInstance(null);
652 name
.setGenusOrUninomial(determinationLight
.genus
);
653 name
.setSpecificEpithet(determinationLight
.speciesEpi
);
654 name
.setInfraSpecificEpithet(determinationLight
.infraSpeciesEpi
);
656 //FIXME bracketAuthors and teams not yet implemented!!!
657 List
<String
> authors
= new ArrayList
<String
>();
658 if (StringUtils
.isNotBlank(determinationLight
.author
)){
659 authors
.add(determinationLight
.author
);
661 TeamOrPersonBase
<?
> agent
= getOrMakeAgent(state
, authors
);
662 name
.setCombinationAuthorship(agent
);
665 if (StringUtils
.isNotBlank(determinationLight
.rank
) ){
666 name
.setRank(Rank
.getRankByNameOrIdInVoc(determinationLight
.rank
, nc
, true));
668 } catch (UnknownCdmTypeException e
) {
669 String message
= "Rank not found: %s: ";
670 message
= String
.format(message
, determinationLight
.rank
);
671 logger
.warn(message
);
673 if (StringUtils
.isBlank(name
.getInfraSpecificEpithet()) && StringUtils
.isNotBlank(name
.getSpecificEpithet() )){
674 name
.setRank(Rank
.SPECIES());
676 if (StringUtils
.isBlank(name
.getSpecificEpithet()) && StringUtils
.isNotBlank(name
.getGenusOrUninomial() )){
677 name
.setRank(Rank
.SPECIES());
679 if (StringUtils
.isBlank(name
.getTitleCache())){
681 name
.setTitleCache(determinationLight
.fullName
, true);
686 private TaxonNameBase
findBestMatchingName(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
) {
688 NonViralName
<?
> name
= makeTaxonName(state
, determinationLight
);
689 String titleCache
= makeSearchNameTitleCache(state
, determinationLight
, name
);
692 List
<TaxonNameBase
> matchingNames
= getNameService().findByName(null, titleCache
, MatchMode
.EXACT
, null, null, null, null, null).getRecords();
693 if (matchingNames
.size() > 0){
694 return matchingNames
.get(0);
695 } else if (matchingNames
.size() > 0){
696 logger
.warn("Get best matching taxon name not yet fully implemeted for specimen import");
697 return matchingNames
.get(0);
705 private DeterminationEvent
makeDeterminationEvent(SpecimenCdmExcelImportState state
, DeterminationLight determination
, Taxon taxon
) {
706 DeterminationEvent event
= DeterminationEvent
.NewInstance();
708 event
.setTaxon(taxon
);
711 TimePeriod date
= TimePeriodParser
.parseString(determination
.determinedWhen
);
712 event
.setTimeperiod(date
);
714 //FIXME bracketAuthors and teams not yet implemented!!!
715 List
<String
> authors
= new ArrayList
<String
>();
716 if (StringUtils
.isNotBlank(determination
.determinedBy
)){
717 authors
.add(determination
.determinedBy
);
719 TeamOrPersonBase
<?
> actor
= getOrMakeAgent(state
, authors
);
720 TeamOrPersonBase
<?
> secAuthor
= taxon
.getSec() == null ?
null : taxon
.getSec().getAuthorship();
721 if (actor
!= null && secAuthor
!= null && secAuthor
.getTitleCache().equals(actor
.getTitleCache()) && secAuthor
.getNomenclaturalTitle().equals(actor
.getNomenclaturalTitle())) {
725 event
.setActor(actor
);
728 if (StringUtils
.isNotBlank(determination
.modifier
)){
729 logger
.warn("DeterminationModifiers not yet implemented for specimen import");
731 // DeterminationModifier modifier = DeterminationModifier.NewInstance(term, label, labelAbbrev);
732 // determination.modifier;
734 if (StringUtils
.isNotEmpty(determination
.notes
)){
735 Annotation annotation
= Annotation
.NewInstance(determination
.notes
, AnnotationType
.EDITORIAL(), Language
.DEFAULT());
736 event
.addAnnotation(annotation
);
742 private TaxonDescription
getTaxonDescription(Taxon taxon
) {
743 TaxonDescription desc
= this.getTaxonDescription(taxon
, ! IMAGE_GALLERY
, CREATE
);
747 private TeamOrPersonBase
<?
> getOrMakeAgent(SpecimenCdmExcelImportState state
, List
<String
> agents
) {
748 if (agents
.size() == 0){
750 }else if (agents
.size() == 1){
751 return getOrMakePerson(state
, agents
.get(0));
753 return getOrMakeTeam(state
, agents
);
757 private Person
getOrMakePrimaryCollector(DerivedUnitFacade facade
, String primaryCollector
, SpecimenCdmExcelImportState state
) {
758 if (StringUtils
.isBlank(primaryCollector
)){
761 AgentBase
<?
> collector
= facade
.getCollector();
762 List
<Person
> collectors
= new ArrayList
<Person
>();
763 if (collector
.isInstanceOf(Team
.class) ){
764 Team team
= CdmBase
.deproxy(collector
, Team
.class);
765 collectors
.addAll(team
.getTeamMembers());
766 }else if (collector
.isInstanceOf(Person
.class)){
767 collectors
.add(CdmBase
.deproxy(collector
, Person
.class));
769 throw new IllegalStateException("Unknown subclass of agentbase: " + collector
.getClass().getName() );
771 for (Person person
:collectors
){
772 if (primaryCollector
.equalsIgnoreCase(person
.getTitleCache())){
775 if (primaryCollector
.equalsIgnoreCase(person
.getNomenclaturalTitle())){
779 String message
= "Primary Agent '%s' could not be determined in collector(s) in line %d";
780 message
= String
.format(message
, primaryCollector
, state
.getCurrentLine());
781 logger
.warn(message
);
785 private Team
getOrMakeTeam(SpecimenCdmExcelImportState state
, List
<String
> agents
) {
786 String key
= CdmUtils
.concat("_", agents
.toArray(new String
[0]));
788 Team result
= state
.getTeam(key
);
790 result
= Team
.NewInstance();
791 for (String member
: agents
){
792 Person person
= getOrMakePerson(state
, member
);
793 result
.addTeamMember(person
);
795 state
.putTeam(key
, result
);
800 private Person
getOrMakePerson(SpecimenCdmExcelImportState state
, String value
) {
801 Person result
= state
.getPerson(value
);
803 result
= Person
.NewInstance();
804 result
.setTitleCache(value
, true);
805 state
.putPerson(value
, result
);
810 private Reference
getOrMakeReference(SpecimenCdmExcelImportState state
, String value
) {
811 Reference result
= state
.getReference(value
);
813 result
= ReferenceFactory
.newGeneric();
814 result
.setTitleCache(value
, true);
815 state
.putReference(value
, result
);
822 private Collection
getOrMakeCollection(SpecimenCdmExcelImportState state
, String collectionCode
, String collectionString
) {
823 Collection result
= state
.getCollection(collectionCode
);
825 result
= Collection
.NewInstance();
826 result
.setCode(collectionCode
);
827 result
.setName(collectionString
);
828 state
.putCollection(collectionCode
, result
);
834 private TaxonNameBase
<?
, ?
> getTaxonName(SpecimenCdmExcelImportState state
, String name
) {
835 TaxonNameBase
<?
,?
> result
= null;
836 result
= state
.getName(name
);
840 List
<TaxonNameBase
> list
= getNameService().findByTitle(null, name
, null, null, null, null, null, null).getRecords();
841 //TODO better strategy to find best name, e.g. depending on the classification it is used in
842 if (! list
.isEmpty()){
843 result
= list
.get(0);
846 NonViralNameParserImpl parser
= NonViralNameParserImpl
.NewInstance();
847 NomenclaturalCode code
= state
.getConfig().getNomenclaturalCode();
848 result
= parser
.parseFullName(name
, code
, null);
852 state
.putName(name
, result
);
857 private SpecimenTypeDesignationStatus
getSpecimenTypeStatus(SpecimenCdmExcelImportState state
, String key
) {
858 SpecimenTypeDesignationStatus result
= null;
860 result
= state
.getTransformer().getSpecimenTypeDesignationStatusByKey(key
);
862 String message
= "Type status not recognized for %s in line %d";
863 message
= String
.format(message
, key
, state
.getCurrentLine());
864 logger
.warn(message
);
867 } catch (UndefinedTransformerMethodException e
) {
868 throw new RuntimeException("getSpecimenTypeDesignationStatusByKey not yet implemented");
875 private void handleExactLocation(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
878 ReferenceSystem refSys
= null;
879 if (StringUtils
.isNotBlank(row
.getReferenceSystem())){
880 String strRefSys
= row
.getReferenceSystem().trim().replaceAll("\\s", "");
883 refSys
= state
.getTransformer().getReferenceSystemByKey(strRefSys
);
885 //TODO we still need user defined Reference Systems here
886 refUuid
= state
.getTransformer().getReferenceSystemUuid(strRefSys
);
887 if (refUuid
== null){
888 String message
= "Unknown reference system %s in line %d";
889 message
= String
.format(message
, strRefSys
, state
.getCurrentLine());
890 logger
.warn(message
);
892 refSys
= getReferenceSystem(state
, refUuid
, strRefSys
, strRefSys
, strRefSys
, null);
895 } catch (UndefinedTransformerMethodException e
) {
896 throw new RuntimeException(e
);
904 String longitude
= row
.getLongitude();
905 String latitude
= row
.getLatitude();
906 Integer errorRadius
= null;
907 if (StringUtils
.isNotBlank(row
.getErrorRadius())){
909 errorRadius
= Integer
.valueOf(row
.getErrorRadius());
910 } catch (NumberFormatException e
) {
911 String message
= "Error radius %s could not be transformed to Integer in line %d";
912 message
= String
.format(message
, row
.getErrorRadius(), state
.getCurrentLine());
913 logger
.warn(message
);
917 facade
.setExactLocationByParsing(longitude
, latitude
, refSys
, errorRadius
);
918 } catch (ParseException e
) {
919 String message
= "Problems when parsing exact location for line %d";
920 message
= String
.format(message
, state
.getCurrentLine());
921 logger
.warn(message
);
931 * Set the current Country
932 * Search in the DB if the isoCode is known
933 * If not, search if the country name is in the DB
934 * If not, create a new Label with the Level Country
935 * @param iso: the country iso code
936 * @param fullName: the country's full name
937 * @param app: the CDM application controller
939 private void handleCountry(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
941 if (StringUtils
.isNotBlank(row
.getIsoCountry())){
942 NamedArea country
= getOccurrenceService().getCountryByIso(row
.getIsoCountry());
943 if (country
!= null){
944 facade
.setCountry(country
);
948 if (StringUtils
.isNotBlank(row
.getCountry())){
949 List
<Country
> countries
= getOccurrenceService().getCountryByName(row
.getCountry());
950 if (countries
.size() >0){
951 facade
.setCountry(countries
.get(0));
953 UUID uuid
= UUID
.randomUUID();
954 String label
= row
.getCountry();
955 String text
= row
.getCountry();
956 String labelAbbrev
= null;
957 NamedAreaType areaType
= NamedAreaType
.ADMINISTRATION_AREA();
958 NamedAreaLevel level
= NamedAreaLevel
.COUNTRY();
959 NamedArea newCountry
= this.getNamedArea(state
, uuid
, label
, text
, labelAbbrev
, areaType
, level
);
960 facade
.setCountry(newCountry
);
966 protected void secondPass(SpecimenCdmExcelImportState state
) {
967 //no second path defined yet
973 protected String
getWorksheetName() {
974 return WORKSHEET_NAME
;
978 protected boolean needsNomenclaturalCode() {
984 * @see eu.etaxonomy.cdm.io.excel.common.ExcelTaxonOrSpecimenImportBase#createDataHolderRow()
987 protected SpecimenRow
createDataHolderRow() {
988 return new SpecimenRow();
995 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
998 protected boolean doCheck(SpecimenCdmExcelImportState state
) {
999 logger
.warn("Validation not yet implemented for " + this.getClass().getSimpleName());
1006 protected boolean isIgnore(SpecimenCdmExcelImportState state
) {
1007 return !state
.getConfig().isDoSpecimen();