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
.TaxonNameFactory
;
56 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
57 import eu
.etaxonomy
.cdm
.model
.occurrence
.Collection
;
58 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
59 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
60 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationType
;
61 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
62 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceFactory
;
63 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
64 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
65 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
66 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.StringNotParsableException
;
67 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.UnknownCdmTypeException
;
68 import eu
.etaxonomy
.cdm
.strategy
.parser
.INonViralNameParser
;
69 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImpl
;
70 import eu
.etaxonomy
.cdm
.strategy
.parser
.TimePeriodParser
;
78 public class SpecimenCdmExcelImport
extends ExcelTaxonOrSpecimenImportBase
<SpecimenCdmExcelImportState
, SpecimenRow
> implements ICdmIO
<SpecimenCdmExcelImportState
> {
79 private static final Logger logger
= Logger
.getLogger(SpecimenCdmExcelImport
.class);
81 private static final String WORKSHEET_NAME
= "Specimen";
83 private static final String BASIS_OF_RECORD_COLUMN
= "(?i)(BasisOfRecord)";
84 private static final String COUNTRY_COLUMN
= "(?i)(Country)";
85 private static final String AREA_COLUMN
= "(?i)(Area)";
86 private static final String ISO_COUNTRY_COLUMN
= "(?i)(ISOCountry|CountryCode)";
87 private static final String LOCALITY_COLUMN
= "(?i)(Locality)";
88 private static final String ALTITUDE_COLUMN
= "(?i)(AbsoluteElevation|Altitude)";
89 private static final String ALTITUDE_MAX_COLUMN
= "(?i)(AbsoluteElevation|Altitude)Max(imum)?";
90 private static final String COLLECTION_DATE_COLUMN
= "(?i)(CollectionDate)";
91 private static final String COLLECTION_DATE_END_COLUMN
= "(?i)(CollectionDateEnd)";
92 private static final String COLLECTOR_COLUMN
= "(?i)(Collector)";
93 private static final String COLLECTORS_COLUMN
= "(?i)(Collectors)";
94 private static final String PRIMARY_COLLECTOR_COLUMN
= "(?i)(PrimaryCollector)";
95 private static final String LONGITUDE_COLUMN
= "(?i)(Longitude)";
96 private static final String LATITUDE_COLUMN
= "(?i)(Latitude)";
97 private static final String REFERENCE_SYSTEM_COLUMN
= "(?i)(ReferenceSystem)";
98 private static final String ERROR_RADIUS_COLUMN
= "(?i)(ErrorRadius)";
101 private static final String COLLECTORS_NUMBER_COLUMN
= "(?i)((Collectors|Field)Number)";
102 private static final String ECOLOGY_COLUMN
= "(?i)(Ecology|Habitat)";
103 private static final String PLANT_DESCRIPTION_COLUMN
= "(?i)(PlantDescription)";
104 private static final String FIELD_NOTES_COLUMN
= "(?i)(FieldNotes)";
105 private static final String SEX_COLUMN
= "(?i)(Sex)";
108 private static final String ACCESSION_NUMBER_COLUMN
= "(?i)(AccessionNumber)";
109 private static final String BARCODE_COLUMN
= "(?i)(Barcode)";
110 private static final String COLLECTION_CODE_COLUMN
= "(?i)(CollectionCode)";
111 private static final String COLLECTION_COLUMN
= "(?i)(Collection)";
112 private static final String UNIT_NOTES_COLUMN
= "(?i)((Unit)?Notes)";
115 private static final String TYPE_CATEGORY_COLUMN
= "(?i)(TypeCategory)";
116 private static final String TYPIFIED_NAME_COLUMN
= "(?i)(TypifiedName|TypeOf)";
119 private static final String SOURCE_COLUMN
= "(?i)(Source)";
120 private static final String ID_IN_SOURCE_COLUMN
= "(?i)(IdInSource)";
123 private static final String DETERMINATION_AUTHOR_COLUMN
= "(?i)(Author)";
124 private static final String DETERMINATION_MODIFIER_COLUMN
= "(?i)(DeterminationModifier)";
125 private static final String DETERMINED_BY_COLUMN
= "(?i)(DeterminationBy)";
126 private static final String DETERMINED_WHEN_COLUMN
= "(?i)(Det(ermination)?When)";
127 private static final String DETERMINATION_NOTES_COLUMN
= "(?i)(DeterminationNote)";
128 private static final String EXTENSION_COLUMN
= "(?i)(Ext(ension)?)";
131 public SpecimenCdmExcelImport() {
139 protected void analyzeSingleValue(KeyValue keyValue
, SpecimenCdmExcelImportState state
) {
140 SpecimenRow row
= state
.getCurrentRow();
141 String value
= keyValue
.value
;
142 if(keyValue
.key
.matches(BASIS_OF_RECORD_COLUMN
)) {
143 row
.setBasisOfRecord(value
);
144 } else if(keyValue
.key
.matches(COUNTRY_COLUMN
)) {
145 row
.setCountry(value
);
146 } else if(keyValue
.key
.matches(ISO_COUNTRY_COLUMN
)) {
147 row
.setIsoCountry(value
);
148 } else if(keyValue
.key
.matches(LOCALITY_COLUMN
)) {
149 row
.setLocality(value
);
150 } else if(keyValue
.key
.matches(FIELD_NOTES_COLUMN
)) {
151 row
.setLocality(value
);
152 } else if(keyValue
.key
.matches(ALTITUDE_COLUMN
)) {
153 row
.setAltitude(value
);
154 } else if(keyValue
.key
.matches(ALTITUDE_MAX_COLUMN
)) {
155 row
.setAltitudeMax(value
);
156 } else if(keyValue
.key
.matches(COLLECTOR_COLUMN
)) {
157 row
.putCollector(keyValue
.index
, value
);
158 } else if(keyValue
.key
.matches(PRIMARY_COLLECTOR_COLUMN
)) {
159 row
.setPrimaryCollector(value
);
160 } else if(keyValue
.key
.matches(ECOLOGY_COLUMN
)) {
161 row
.setEcology(value
);
162 } else if(keyValue
.key
.matches(PLANT_DESCRIPTION_COLUMN
)) {
163 row
.setPlantDescription(value
);
164 } else if(keyValue
.key
.matches(SEX_COLUMN
)) {
166 } else if(keyValue
.key
.matches(COLLECTION_DATE_COLUMN
)) {
167 row
.setCollectingDate(value
);
168 } else if(keyValue
.key
.matches(COLLECTION_DATE_END_COLUMN
)) {
169 row
.setCollectingDateEnd(value
);
170 } else if(keyValue
.key
.matches(COLLECTORS_COLUMN
)) {
171 row
.setCollectors(value
);
172 } else if(keyValue
.key
.matches(COLLECTOR_COLUMN
)) {
173 row
.putCollector(keyValue
.index
, value
);
174 } else if(keyValue
.key
.matches(COLLECTORS_NUMBER_COLUMN
)) {
175 row
.setCollectorsNumber(value
);
176 } else if(keyValue
.key
.matches(LONGITUDE_COLUMN
)) {
177 row
.setLongitude(value
);
178 } else if(keyValue
.key
.matches(LATITUDE_COLUMN
)) {
179 row
.setLatitude(value
);
180 } else if(keyValue
.key
.matches(REFERENCE_SYSTEM_COLUMN
)) {
181 row
.setReferenceSystem(value
);
182 } else if(keyValue
.key
.matches(ERROR_RADIUS_COLUMN
)) {
183 row
.setErrorRadius(value
);
184 } else if(keyValue
.key
.matches(AREA_COLUMN
)) {
185 if (keyValue
.postfix
!= null){
186 row
.addLeveledArea(keyValue
.postfix
, value
);
188 logger
.warn("Not yet implemented");
190 } else if(keyValue
.key
.matches(LANGUAGE
)) {
191 row
.setLanguage(value
);
194 } else if(keyValue
.key
.matches(ACCESSION_NUMBER_COLUMN
)) {
195 row
.setAccessionNumber(value
);
196 } else if(keyValue
.key
.matches(BARCODE_COLUMN
)) {
197 row
.setBarcode(value
);
198 } else if(keyValue
.key
.matches(UNIT_NOTES_COLUMN
)) {
199 row
.putUnitNote(keyValue
.index
, value
);
202 } else if(keyValue
.key
.matches(FAMILY_COLUMN
)) {
203 row
.putDeterminationFamily(keyValue
.index
, value
);
204 } else if(keyValue
.key
.matches(GENUS_COLUMN
)) {
205 row
.putDeterminationGenus(keyValue
.index
, value
);
206 } else if(keyValue
.key
.matches(SPECIFIC_EPITHET_COLUMN
)) {
207 row
.putDeterminationSpeciesEpi(keyValue
.index
, value
);
208 } else if(keyValue
.key
.matches(INFRASPECIFIC_EPITHET_COLUMN
)) {
209 row
.putDeterminationInfraSpeciesEpi(keyValue
.index
, value
);
210 } else if(keyValue
.key
.matches(RANK_COLUMN
)) {
211 row
.putDeterminationRank(keyValue
.index
, value
);
212 } else if(keyValue
.key
.matches(TAXON_UUID_COLUMN
)) {
213 row
.putDeterminationTaxonUuid(keyValue
.index
, value
);
214 } else if(keyValue
.key
.matches(FULL_NAME_COLUMN
)) {
215 row
.putDeterminationFullName(keyValue
.index
, value
);
216 } else if(keyValue
.key
.matches(DETERMINATION_AUTHOR_COLUMN
)) {
217 row
.putDeterminationAuthor(keyValue
.index
, value
);
218 } else if(keyValue
.key
.matches(DETERMINATION_MODIFIER_COLUMN
)) {
219 row
.putDeterminationDeterminationModifier(keyValue
.index
, value
);
220 } else if(keyValue
.key
.matches(DETERMINATION_NOTES_COLUMN
)) {
221 row
.putDeterminationDeterminationNotes(keyValue
.index
, value
);
222 } else if(keyValue
.key
.matches(DETERMINED_BY_COLUMN
)) {
223 row
.putDeterminationDeterminedBy(keyValue
.index
, value
);
224 } else if(keyValue
.key
.matches(DETERMINED_WHEN_COLUMN
)) {
225 row
.putDeterminationDeterminedWhen(keyValue
.index
, value
);
227 } else if(keyValue
.key
.matches(COLLECTION_CODE_COLUMN
)) {
228 row
.setCollectionCode(value
);
229 } else if(keyValue
.key
.matches(COLLECTION_COLUMN
)) {
230 row
.setCollection(value
);
232 } else if(keyValue
.key
.matches(TYPE_CATEGORY_COLUMN
)) {
233 row
.putTypeCategory(keyValue
.index
, getSpecimenTypeStatus(state
, value
));
234 } else if(keyValue
.key
.matches(TYPIFIED_NAME_COLUMN
)) {
235 row
.putTypifiedName(keyValue
.index
, getTaxonName(state
, value
));
238 } else if(keyValue
.key
.matches(SOURCE_COLUMN
)) {
239 row
.putSourceReference(keyValue
.index
, getOrMakeReference(state
, value
) );
240 } else if(keyValue
.key
.matches(ID_IN_SOURCE_COLUMN
)) {
241 row
.putIdInSource(keyValue
.index
, value
);
242 } else if(keyValue
.key
.matches(EXTENSION_COLUMN
)) {
243 if (keyValue
.postfix
!= null){
244 row
.addExtension(keyValue
.postfix
, value
);
246 logger
.warn("Extension without postfix not yet implemented");
250 state
.setUnsuccessfull();
251 logger
.error("Unexpected column header " + keyValue
.originalKey
);
259 protected void firstPass(SpecimenCdmExcelImportState state
) {
260 SpecimenRow row
= state
.getCurrentRow();
263 SpecimenOrObservationType type
= SpecimenOrObservationType
.valueOf2(row
.getBasisOfRecord());
265 String message
= "%s is not a valid BasisOfRecord. 'Unknown' is used instead in line %d.";
266 message
= String
.format(message
, row
.getBasisOfRecord(), state
.getCurrentLine());
267 logger
.warn(message
);
268 type
= SpecimenOrObservationType
.DerivedUnit
;
270 DerivedUnitFacade facade
= DerivedUnitFacade
.NewInstance(type
);
273 Language lang
= Language
.DEFAULT();
274 if (StringUtils
.isNotBlank(row
.getLanguage())){
275 Language langIso
= getTermService().getLanguageByIso(row
.getLanguage());
276 if (langIso
== null){
277 String message
= "Language could not be recognized: %s. Use default language instead. Line %d.";
278 message
= String
.format(message
, langIso
, state
.getCurrentLine());
285 handleCountry(facade
, row
, state
);
286 handleAreas(facade
,row
, state
);
288 facade
.setGatheringPeriod(getTimePeriod(row
.getCollectingDate(), row
.getCollectingDateEnd()));
289 facade
.setLocality(row
.getLocality());
290 facade
.setFieldNotes(row
.getFieldNotes());
291 facade
.setFieldNumber(row
.getCollectorsNumber());
292 facade
.setEcology(row
.getEcology(), lang
);
293 facade
.setPlantDescription(row
.getPlantDescription(), lang
);
294 // facade.setSex(row.get)
295 handleExactLocation(facade
, row
, state
);
296 facade
.setCollector(getOrMakeAgent(state
, row
.getCollectors()));
297 facade
.setPrimaryCollector(getOrMakePrimaryCollector(facade
, row
.getPrimaryCollector(), state
));
298 handleAbsoluteElevation(facade
, row
, state
);
301 facade
.setBarcode(row
.getBarcode());
302 facade
.setAccessionNumber(row
.getAccessionNumber());
303 facade
.setCollection(getOrMakeCollection(state
, row
.getCollectionCode(), row
.getCollection()));
304 for (IdentifiableSource source
: row
.getSources()){
305 facade
.addSource(source
);
307 for (SpecimenTypeDesignation designation
: row
.getTypeDesignations()){
308 logger
.warn("FIXME"); //FIXME
309 // facade.innerDerivedUnit().addSpecimenTypeDesignation(designation);
311 handleDeterminations(state
, row
, facade
);
312 handleExtensions(facade
.innerDerivedUnit(),row
, state
);
313 for (String note
: row
.getUnitNotes()){
314 Annotation annotation
= Annotation
.NewInstance(note
, AnnotationType
.EDITORIAL(), Language
.DEFAULT());
315 facade
.addAnnotation(annotation
);
319 getOccurrenceService().save(facade
.innerDerivedUnit());
323 private void handleAbsoluteElevation(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
327 String altitude
= row
.getAltitude();
328 if (StringUtils
.isBlank(altitude
)){
331 // if (altitude.endsWith(".0")){
332 // altitude = altitude.substring(0, altitude.length() -2);
334 int value
= Integer
.valueOf(altitude
);
335 facade
.setAbsoluteElevation(value
);
336 } catch (NumberFormatException e
) {
337 String message
= "Absolute elevation / altitude '%s' is not an integer number in line %d";
338 message
= String
.format(message
, row
.getAltitude(), state
.getCurrentLine());
339 logger
.warn(message
);
346 String max
= row
.getAltitudeMax();
347 if (StringUtils
.isBlank(max
)){
350 // if (max.endsWith(".0")){
351 // max = max.substring(0, max.length() -2);
353 int value
= Integer
.valueOf(max
);
354 //TODO avoid unequal distance
355 int min
= facade
.getAbsoluteElevation();
356 if ( (value
- min
) % 2 == 1 ){
357 String message
= "Altitude min-max difference ist not equal. Max reduced by 1 in line %d";
358 message
= String
.format(message
, state
.getCurrentLine());
359 logger
.warn(message
);
362 facade
.setAbsoluteElevationRange(min
, value
);
363 } catch (NumberFormatException e
) {
364 String message
= "Absolute elevation / Altitude maximum '%s' is not an integer number in line %d";
365 message
= String
.format(message
, row
.getAltitudeMax(), state
.getCurrentLine());
366 logger
.warn(message
);
368 }catch (Exception e
){
369 String message
= "Error occurred when trying to write Absolute elevation / Altitude maximum '%s' in line %d";
370 message
= String
.format(message
, row
.getAltitudeMax(), state
.getCurrentLine());
371 logger
.warn(message
);
379 private void handleAreas(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
380 List
<PostfixTerm
> areas
= row
.getLeveledAreas();
382 for (PostfixTerm lArea
: areas
){
383 String description
= lArea
.term
;
384 String abbrev
= lArea
.term
;
385 NamedAreaType type
= null;
386 String key
= lArea
.postfix
+ "_" + lArea
.term
;
387 UUID areaUuid
= state
.getArea(key
);
388 NamedAreaLevel level
= state
.getPostfixLevel(lArea
.postfix
);
390 TermMatchMode matchMode
= state
.getConfig().getAreaMatchMode();
391 NamedArea area
= getNamedArea(state
, areaUuid
, lArea
.term
, description
, abbrev
, type
, level
, null, matchMode
);
392 facade
.addCollectingArea(area
);
393 if (areaUuid
== null){
394 state
.putArea(key
, area
.getUuid());
405 private void handleDeterminations(SpecimenCdmExcelImportState state
,SpecimenRow row
, DerivedUnitFacade facade
) {
406 boolean isFirstDetermination
= true;
407 DeterminationLight commonDetermination
= row
.getCommonDetermination();
408 Taxon commonTaxon
= null;
409 TaxonNameBase
<?
,?
> commonName
= null;
411 boolean hasCommonTaxonInfo
= (commonDetermination
== null) ?
false : commonDetermination
.hasTaxonInformation();
412 if (hasCommonTaxonInfo
&& commonDetermination
!= null){
413 TaxonBase
<?
> taxonBase
= null;
414 if (StringUtils
.isNotBlank(commonDetermination
.taxonUuid
)){
415 UUID taxonUuid
= UUID
.fromString(commonDetermination
.taxonUuid
);
416 taxonBase
= getTaxonService().find(taxonUuid
);
417 if (taxonBase
== null){
418 String message
= "Taxon for uuid %s not found in line %d.";
419 message
= String
.format(message
, taxonUuid
.toString(), state
.getCurrentLine());
420 logger
.warn(message
);
423 taxonBase
= findBestMatchingTaxon(state
, commonDetermination
, state
.getConfig().isCreateTaxonIfNotExists());
425 commonTaxon
= getAcceptedTaxon(taxonBase
);
426 if (taxonBase
!= null){
427 commonName
= taxonBase
.getName();
429 commonTaxon
= createTaxonFromDetermination(state
, commonDetermination
);
430 commonName
= commonTaxon
.getName();
435 for (DeterminationLight determinationLight
: row
.getDetermination()){
437 if (! hasCommonTaxonInfo
){
438 taxon
= findBestMatchingTaxon(state
, determinationLight
, state
.getConfig().isCreateTaxonIfNotExists());
443 getTaxonService().saveOrUpdate(taxon
);
444 if (state
.getConfig().isMakeIndividualAssociations() && taxon
!= null){
445 IndividualsAssociation indivAssociciation
= IndividualsAssociation
.NewInstance();
446 DerivedUnit du
= facade
.innerDerivedUnit();
447 indivAssociciation
.setAssociatedSpecimenOrObservation(du
);
448 getTaxonDescription(taxon
).addElement(indivAssociciation
);
449 Feature feature
= Feature
.INDIVIDUALS_ASSOCIATION();
450 if (facade
.getType().isPreservedSpecimen()){
451 feature
= Feature
.SPECIMEN();
452 }else if (facade
.getType().isFeatureObservation()){
453 feature
= Feature
.OBSERVATION();
455 if (state
.getConfig().isUseMaterialsExaminedForIndividualsAssociations()){
456 feature
= Feature
.MATERIALS_EXAMINED();
459 indivAssociciation
.setFeature(feature
);
461 if (state
.getConfig().isDeterminationsAreDeterminationEvent()){
462 DeterminationEvent detEvent
= makeDeterminationEvent(state
, determinationLight
, taxon
);
463 detEvent
.setPreferredFlag(isFirstDetermination
);
464 facade
.addDetermination(detEvent
);
468 if (isFirstDetermination
&& state
.getConfig().isFirstDeterminationIsStoredUnder()){
469 TaxonNameBase
<?
,?
> name
;
471 if (!hasCommonTaxonInfo
){
472 name
= findBestMatchingName(state
, determinationLight
);
474 if (commonName
== null){
475 commonName
= findBestMatchingName(state
, commonDetermination
);
480 facade
.setStoredUnder(name
);
483 isFirstDetermination
= false;
487 private Taxon
createTaxonFromDetermination( SpecimenCdmExcelImportState state
, DeterminationLight commonDetermination
) {
492 rank
= StringUtils
.isBlank(commonDetermination
.rank
) ?
null : Rank
.getRankByNameOrIdInVoc(commonDetermination
.rank
, true);
493 } catch (UnknownCdmTypeException e
) {
499 INonViralNameParser parser
= NonViralNameParserImpl
.NewInstance();
500 NomenclaturalCode nc
= state
.getConfig().getNomenclaturalCode();
501 if (StringUtils
.isNotBlank(commonDetermination
.fullName
)){
502 name
= parser
.parseFullName(commonDetermination
.fullName
, nc
, rank
);
503 if (StringUtils
.isBlank(name
.getAuthorshipCache()) && StringUtils
.isNotBlank(commonDetermination
.author
)){
504 setAuthorship(name
, commonDetermination
.author
, parser
);
508 name
= nc
.getNewTaxonNameInstance(rank
);
510 name
= TaxonNameFactory
.NewNonViralInstance(rank
);
512 if (StringUtils
.isNotBlank(commonDetermination
.genus
)){
513 name
.setGenusOrUninomial(commonDetermination
.genus
);
515 if (StringUtils
.isNotBlank(commonDetermination
.speciesEpi
)){
516 name
.setSpecificEpithet(commonDetermination
.speciesEpi
);
518 if (StringUtils
.isNotBlank(commonDetermination
.infraSpeciesEpi
)){
519 name
.setInfraSpecificEpithet(commonDetermination
.infraSpeciesEpi
);
521 if (StringUtils
.isNotBlank(commonDetermination
.author
)){
522 setAuthorship(name
, commonDetermination
.author
, parser
);
525 if (name
.getRank() == null){
526 if (name
.getInfraGenericEpithet() != null && name
.getSpecificEpithet() == null){
527 name
.setRank(Rank
.INFRAGENERICTAXON());
528 }else if (name
.getSpecificEpithet() != null && name
.getInfraSpecificEpithet() == null){
529 name
.setRank(Rank
.SPECIES());
530 }else if (name
.getInfraSpecificEpithet() != null){
531 name
.setRank(Rank
.INFRASPECIFICTAXON());
538 Reference sec
= null;
539 if (StringUtils
.isNotBlank(commonDetermination
.determinedBy
)){
540 sec
= ReferenceFactory
.newGeneric();
541 TeamOrPersonBase
<?
> determinedBy
;
542 BotanicalName dummyName
= TaxonNameBase
.NewBotanicalInstance(Rank
.SPECIES());
544 parser
.parseAuthors(dummyName
, commonDetermination
.determinedBy
);
545 determinedBy
= dummyName
.getCombinationAuthorship();
546 } catch (StringNotParsableException e
) {
547 determinedBy
= Team
.NewTitledInstance(commonDetermination
.determinedBy
, commonDetermination
.determinedBy
);
549 sec
.setAuthorship(determinedBy
);
553 Taxon taxon
= Taxon
.NewInstance(name
, sec
);
555 if (StringUtils
.isNotBlank(commonDetermination
.family
)){
556 if (name
.getRank() == null || name
.getRank().isLower(Rank
.FAMILY()) ){
557 logger
.warn("Family taxon could not be created");
569 private void setAuthorship(INonViralName name
, String author
, INonViralNameParser
<NonViralName
> parser
) {
570 if (name
instanceof BotanicalName
|| name
instanceof ZoologicalName
){
572 parser
.parseAuthors(name
, author
);
573 } catch (StringNotParsableException e
) {
574 name
.setAuthorshipCache(author
);
577 name
.setAuthorshipCache(author
);
584 * This method tries to find the best matching taxon depending on the import configuration,
585 * the taxon name information and the concept information available.
589 * @param determinationLight
590 * @param createIfNotExists
593 private Taxon
findBestMatchingTaxon(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
, boolean createIfNotExists
) {
594 NonViralName
<?
> name
= makeTaxonName(state
, determinationLight
);
596 String titleCache
= makeSearchNameTitleCache(state
, determinationLight
, name
);
598 if (! StringUtils
.isBlank(titleCache
)){
599 MatchingTaxonConfigurator matchConfigurator
= MatchingTaxonConfigurator
.NewInstance();
600 matchConfigurator
.setTaxonNameTitle(titleCache
);
601 matchConfigurator
.setIncludeSynonyms(false);
602 Taxon taxon
= getTaxonService().findBestMatchingTaxon(matchConfigurator
);
604 if(taxon
== null && createIfNotExists
){
605 logger
.info("creating new Taxon from TaxonName '" + titleCache
+"'");
606 UUID secUuid
= null; //TODO
607 Reference sec
= null;
608 if (secUuid
!= null){
609 sec
= getReferenceService().find(secUuid
);
611 taxon
= Taxon
.NewInstance(name
, sec
);
612 }else if (taxon
== null){
613 String message
= "Taxon '%s' not found in line %d";
614 message
= String
.format(message
, titleCache
, state
.getCurrentLine());
615 logger
.warn(message
);
625 * @param determinationLight
629 private String
makeSearchNameTitleCache(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
,
630 NonViralName
<?
> name
) {
631 String titleCache
= determinationLight
.fullName
;
632 if (! state
.getConfig().isPreferNameCache() || StringUtils
.isBlank(titleCache
) ){
633 String computedTitleCache
= name
.getTitleCache();
634 if (StringUtils
.isNotBlank(computedTitleCache
)){
635 titleCache
= computedTitleCache
;
644 * @param determinationLight
647 private NonViralName
<?
> makeTaxonName(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
) {
648 NonViralName
<?
> name
= TaxonNameFactory
.NewNonViralInstance(null);
649 NomenclaturalCode nc
= state
.getConfig().getNomenclaturalCode();
651 name
= (NonViralName
<?
>)nc
.getNewTaxonNameInstance(null);
653 name
.setGenusOrUninomial(determinationLight
.genus
);
654 name
.setSpecificEpithet(determinationLight
.speciesEpi
);
655 name
.setInfraSpecificEpithet(determinationLight
.infraSpeciesEpi
);
657 //FIXME bracketAuthors and teams not yet implemented!!!
658 List
<String
> authors
= new ArrayList
<String
>();
659 if (StringUtils
.isNotBlank(determinationLight
.author
)){
660 authors
.add(determinationLight
.author
);
662 TeamOrPersonBase
<?
> agent
= getOrMakeAgent(state
, authors
);
663 name
.setCombinationAuthorship(agent
);
666 if (StringUtils
.isNotBlank(determinationLight
.rank
) ){
667 name
.setRank(Rank
.getRankByNameOrIdInVoc(determinationLight
.rank
, nc
, true));
669 } catch (UnknownCdmTypeException e
) {
670 String message
= "Rank not found: %s: ";
671 message
= String
.format(message
, determinationLight
.rank
);
672 logger
.warn(message
);
674 if (StringUtils
.isBlank(name
.getInfraSpecificEpithet()) && StringUtils
.isNotBlank(name
.getSpecificEpithet() )){
675 name
.setRank(Rank
.SPECIES());
677 if (StringUtils
.isBlank(name
.getSpecificEpithet()) && StringUtils
.isNotBlank(name
.getGenusOrUninomial() )){
678 name
.setRank(Rank
.SPECIES());
680 if (StringUtils
.isBlank(name
.getTitleCache())){
682 name
.setTitleCache(determinationLight
.fullName
, true);
687 private TaxonNameBase
findBestMatchingName(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
) {
689 NonViralName
<?
> name
= makeTaxonName(state
, determinationLight
);
690 String titleCache
= makeSearchNameTitleCache(state
, determinationLight
, name
);
693 List
<TaxonNameBase
> matchingNames
= getNameService().findByName(null, titleCache
, MatchMode
.EXACT
, null, null, null, null, null).getRecords();
694 if (matchingNames
.size() > 0){
695 return matchingNames
.get(0);
696 } else if (matchingNames
.size() > 0){
697 logger
.warn("Get best matching taxon name not yet fully implemeted for specimen import");
698 return matchingNames
.get(0);
706 private DeterminationEvent
makeDeterminationEvent(SpecimenCdmExcelImportState state
, DeterminationLight determination
, Taxon taxon
) {
707 DeterminationEvent event
= DeterminationEvent
.NewInstance();
709 event
.setTaxon(taxon
);
712 TimePeriod date
= TimePeriodParser
.parseString(determination
.determinedWhen
);
713 event
.setTimeperiod(date
);
715 //FIXME bracketAuthors and teams not yet implemented!!!
716 List
<String
> authors
= new ArrayList
<String
>();
717 if (StringUtils
.isNotBlank(determination
.determinedBy
)){
718 authors
.add(determination
.determinedBy
);
720 TeamOrPersonBase
<?
> actor
= getOrMakeAgent(state
, authors
);
721 TeamOrPersonBase
<?
> secAuthor
= taxon
.getSec() == null ?
null : taxon
.getSec().getAuthorship();
722 if (actor
!= null && secAuthor
!= null && secAuthor
.getTitleCache().equals(actor
.getTitleCache()) && secAuthor
.getNomenclaturalTitle().equals(actor
.getNomenclaturalTitle())) {
726 event
.setActor(actor
);
729 if (StringUtils
.isNotBlank(determination
.modifier
)){
730 logger
.warn("DeterminationModifiers not yet implemented for specimen import");
732 // DeterminationModifier modifier = DeterminationModifier.NewInstance(term, label, labelAbbrev);
733 // determination.modifier;
735 if (StringUtils
.isNotEmpty(determination
.notes
)){
736 Annotation annotation
= Annotation
.NewInstance(determination
.notes
, AnnotationType
.EDITORIAL(), Language
.DEFAULT());
737 event
.addAnnotation(annotation
);
743 private TaxonDescription
getTaxonDescription(Taxon taxon
) {
744 TaxonDescription desc
= this.getTaxonDescription(taxon
, ! IMAGE_GALLERY
, CREATE
);
748 private TeamOrPersonBase
<?
> getOrMakeAgent(SpecimenCdmExcelImportState state
, List
<String
> agents
) {
749 if (agents
.size() == 0){
751 }else if (agents
.size() == 1){
752 return getOrMakePerson(state
, agents
.get(0));
754 return getOrMakeTeam(state
, agents
);
758 private Person
getOrMakePrimaryCollector(DerivedUnitFacade facade
, String primaryCollector
, SpecimenCdmExcelImportState state
) {
759 if (StringUtils
.isBlank(primaryCollector
)){
762 AgentBase
<?
> collector
= facade
.getCollector();
763 List
<Person
> collectors
= new ArrayList
<Person
>();
764 if (collector
.isInstanceOf(Team
.class) ){
765 Team team
= CdmBase
.deproxy(collector
, Team
.class);
766 collectors
.addAll(team
.getTeamMembers());
767 }else if (collector
.isInstanceOf(Person
.class)){
768 collectors
.add(CdmBase
.deproxy(collector
, Person
.class));
770 throw new IllegalStateException("Unknown subclass of agentbase: " + collector
.getClass().getName() );
772 for (Person person
:collectors
){
773 if (primaryCollector
.equalsIgnoreCase(person
.getTitleCache())){
776 if (primaryCollector
.equalsIgnoreCase(person
.getNomenclaturalTitle())){
780 String message
= "Primary Agent '%s' could not be determined in collector(s) in line %d";
781 message
= String
.format(message
, primaryCollector
, state
.getCurrentLine());
782 logger
.warn(message
);
786 private Team
getOrMakeTeam(SpecimenCdmExcelImportState state
, List
<String
> agents
) {
787 String key
= CdmUtils
.concat("_", agents
.toArray(new String
[0]));
789 Team result
= state
.getTeam(key
);
791 result
= Team
.NewInstance();
792 for (String member
: agents
){
793 Person person
= getOrMakePerson(state
, member
);
794 result
.addTeamMember(person
);
796 state
.putTeam(key
, result
);
801 private Person
getOrMakePerson(SpecimenCdmExcelImportState state
, String value
) {
802 Person result
= state
.getPerson(value
);
804 result
= Person
.NewInstance();
805 result
.setTitleCache(value
, true);
806 state
.putPerson(value
, result
);
811 private Reference
getOrMakeReference(SpecimenCdmExcelImportState state
, String value
) {
812 Reference result
= state
.getReference(value
);
814 result
= ReferenceFactory
.newGeneric();
815 result
.setTitleCache(value
, true);
816 state
.putReference(value
, result
);
823 private Collection
getOrMakeCollection(SpecimenCdmExcelImportState state
, String collectionCode
, String collectionString
) {
824 Collection result
= state
.getCollection(collectionCode
);
826 result
= Collection
.NewInstance();
827 result
.setCode(collectionCode
);
828 result
.setName(collectionString
);
829 state
.putCollection(collectionCode
, result
);
835 private TaxonNameBase
<?
, ?
> getTaxonName(SpecimenCdmExcelImportState state
, String name
) {
836 TaxonNameBase
<?
,?
> result
= null;
837 result
= state
.getName(name
);
841 List
<TaxonNameBase
> list
= getNameService().findByTitle(null, name
, null, null, null, null, null, null).getRecords();
842 //TODO better strategy to find best name, e.g. depending on the classification it is used in
843 if (! list
.isEmpty()){
844 result
= list
.get(0);
847 NonViralNameParserImpl parser
= NonViralNameParserImpl
.NewInstance();
848 NomenclaturalCode code
= state
.getConfig().getNomenclaturalCode();
849 result
= parser
.parseFullName(name
, code
, null);
853 state
.putName(name
, result
);
858 private SpecimenTypeDesignationStatus
getSpecimenTypeStatus(SpecimenCdmExcelImportState state
, String key
) {
859 SpecimenTypeDesignationStatus result
= null;
861 result
= state
.getTransformer().getSpecimenTypeDesignationStatusByKey(key
);
863 String message
= "Type status not recognized for %s in line %d";
864 message
= String
.format(message
, key
, state
.getCurrentLine());
865 logger
.warn(message
);
868 } catch (UndefinedTransformerMethodException e
) {
869 throw new RuntimeException("getSpecimenTypeDesignationStatusByKey not yet implemented");
876 private void handleExactLocation(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
879 ReferenceSystem refSys
= null;
880 if (StringUtils
.isNotBlank(row
.getReferenceSystem())){
881 String strRefSys
= row
.getReferenceSystem().trim().replaceAll("\\s", "");
884 refSys
= state
.getTransformer().getReferenceSystemByKey(strRefSys
);
886 //TODO we still need user defined Reference Systems here
887 refUuid
= state
.getTransformer().getReferenceSystemUuid(strRefSys
);
888 if (refUuid
== null){
889 String message
= "Unknown reference system %s in line %d";
890 message
= String
.format(message
, strRefSys
, state
.getCurrentLine());
891 logger
.warn(message
);
893 refSys
= getReferenceSystem(state
, refUuid
, strRefSys
, strRefSys
, strRefSys
, null);
896 } catch (UndefinedTransformerMethodException e
) {
897 throw new RuntimeException(e
);
905 String longitude
= row
.getLongitude();
906 String latitude
= row
.getLatitude();
907 Integer errorRadius
= null;
908 if (StringUtils
.isNotBlank(row
.getErrorRadius())){
910 errorRadius
= Integer
.valueOf(row
.getErrorRadius());
911 } catch (NumberFormatException e
) {
912 String message
= "Error radius %s could not be transformed to Integer in line %d";
913 message
= String
.format(message
, row
.getErrorRadius(), state
.getCurrentLine());
914 logger
.warn(message
);
918 facade
.setExactLocationByParsing(longitude
, latitude
, refSys
, errorRadius
);
919 } catch (ParseException e
) {
920 String message
= "Problems when parsing exact location for line %d";
921 message
= String
.format(message
, state
.getCurrentLine());
922 logger
.warn(message
);
932 * Set the current Country
933 * Search in the DB if the isoCode is known
934 * If not, search if the country name is in the DB
935 * If not, create a new Label with the Level Country
936 * @param iso: the country iso code
937 * @param fullName: the country's full name
938 * @param app: the CDM application controller
940 private void handleCountry(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
942 if (StringUtils
.isNotBlank(row
.getIsoCountry())){
943 NamedArea country
= getOccurrenceService().getCountryByIso(row
.getIsoCountry());
944 if (country
!= null){
945 facade
.setCountry(country
);
949 if (StringUtils
.isNotBlank(row
.getCountry())){
950 List
<Country
> countries
= getOccurrenceService().getCountryByName(row
.getCountry());
951 if (countries
.size() >0){
952 facade
.setCountry(countries
.get(0));
954 UUID uuid
= UUID
.randomUUID();
955 String label
= row
.getCountry();
956 String text
= row
.getCountry();
957 String labelAbbrev
= null;
958 NamedAreaType areaType
= NamedAreaType
.ADMINISTRATION_AREA();
959 NamedAreaLevel level
= NamedAreaLevel
.COUNTRY();
960 NamedArea newCountry
= this.getNamedArea(state
, uuid
, label
, text
, labelAbbrev
, areaType
, level
);
961 facade
.setCountry(newCountry
);
967 protected void secondPass(SpecimenCdmExcelImportState state
) {
968 //no second path defined yet
974 protected String
getWorksheetName() {
975 return WORKSHEET_NAME
;
979 protected boolean needsNomenclaturalCode() {
985 * @see eu.etaxonomy.cdm.io.excel.common.ExcelTaxonOrSpecimenImportBase#createDataHolderRow()
988 protected SpecimenRow
createDataHolderRow() {
989 return new SpecimenRow();
996 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
999 protected boolean doCheck(SpecimenCdmExcelImportState state
) {
1000 logger
.warn("Validation not yet implemented for " + this.getClass().getSimpleName());
1007 protected boolean isIgnore(SpecimenCdmExcelImportState state
) {
1008 return !state
.getConfig().isDoSpecimen();