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
.NamedArea
;
43 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaLevel
;
44 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaType
;
45 import eu
.etaxonomy
.cdm
.model
.location
.ReferenceSystem
;
46 import eu
.etaxonomy
.cdm
.model
.location
.Country
;
47 import eu
.etaxonomy
.cdm
.model
.name
.BotanicalName
;
48 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalCode
;
49 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
50 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
51 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignation
;
52 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignationStatus
;
53 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
54 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
55 import eu
.etaxonomy
.cdm
.model
.occurrence
.Collection
;
56 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
57 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
58 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationType
;
59 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
60 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceFactory
;
61 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
62 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
63 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
64 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.StringNotParsableException
;
65 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.UnknownCdmTypeException
;
66 import eu
.etaxonomy
.cdm
.strategy
.parser
.INonViralNameParser
;
67 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImpl
;
68 import eu
.etaxonomy
.cdm
.strategy
.parser
.TimePeriodParser
;
76 public class SpecimenCdmExcelImport
extends ExcelTaxonOrSpecimenImportBase
<SpecimenCdmExcelImportState
, SpecimenRow
> implements ICdmIO
<SpecimenCdmExcelImportState
> {
77 private static final Logger logger
= Logger
.getLogger(SpecimenCdmExcelImport
.class);
79 private static final String WORKSHEET_NAME
= "Specimen";
81 private static final String BASIS_OF_RECORD_COLUMN
= "(?i)(BasisOfRecord)";
82 private static final String COUNTRY_COLUMN
= "(?i)(Country)";
83 private static final String AREA_COLUMN
= "(?i)(Area)";
84 private static final String ISO_COUNTRY_COLUMN
= "(?i)(ISOCountry|CountryCode)";
85 private static final String LOCALITY_COLUMN
= "(?i)(Locality)";
86 private static final String ALTITUDE_COLUMN
= "(?i)(AbsoluteElevation|Altitude)";
87 private static final String ALTITUDE_MAX_COLUMN
= "(?i)(AbsoluteElevation|Altitude)Max(imum)?";
88 private static final String COLLECTION_DATE_COLUMN
= "(?i)(CollectionDate)";
89 private static final String COLLECTION_DATE_END_COLUMN
= "(?i)(CollectionDateEnd)";
90 private static final String COLLECTOR_COLUMN
= "(?i)(Collector)";
91 private static final String COLLECTORS_COLUMN
= "(?i)(Collectors)";
92 private static final String PRIMARY_COLLECTOR_COLUMN
= "(?i)(PrimaryCollector)";
93 private static final String LONGITUDE_COLUMN
= "(?i)(Longitude)";
94 private static final String LATITUDE_COLUMN
= "(?i)(Latitude)";
95 private static final String REFERENCE_SYSTEM_COLUMN
= "(?i)(ReferenceSystem)";
96 private static final String ERROR_RADIUS_COLUMN
= "(?i)(ErrorRadius)";
99 private static final String COLLECTORS_NUMBER_COLUMN
= "(?i)((Collectors|Field)Number)";
100 private static final String ECOLOGY_COLUMN
= "(?i)(Ecology|Habitat)";
101 private static final String PLANT_DESCRIPTION_COLUMN
= "(?i)(PlantDescription)";
102 private static final String FIELD_NOTES_COLUMN
= "(?i)(FieldNotes)";
103 private static final String SEX_COLUMN
= "(?i)(Sex)";
106 private static final String ACCESSION_NUMBER_COLUMN
= "(?i)(AccessionNumber)";
107 private static final String BARCODE_COLUMN
= "(?i)(Barcode)";
108 private static final String COLLECTION_CODE_COLUMN
= "(?i)(CollectionCode)";
109 private static final String COLLECTION_COLUMN
= "(?i)(Collection)";
110 private static final String UNIT_NOTES_COLUMN
= "(?i)((Unit)?Notes)";
113 private static final String TYPE_CATEGORY_COLUMN
= "(?i)(TypeCategory)";
114 private static final String TYPIFIED_NAME_COLUMN
= "(?i)(TypifiedName|TypeOf)";
117 private static final String SOURCE_COLUMN
= "(?i)(Source)";
118 private static final String ID_IN_SOURCE_COLUMN
= "(?i)(IdInSource)";
121 private static final String DETERMINATION_AUTHOR_COLUMN
= "(?i)(Author)";
122 private static final String DETERMINATION_MODIFIER_COLUMN
= "(?i)(DeterminationModifier)";
123 private static final String DETERMINED_BY_COLUMN
= "(?i)(DeterminationBy)";
124 private static final String DETERMINED_WHEN_COLUMN
= "(?i)(Det(ermination)?When)";
125 private static final String DETERMINATION_NOTES_COLUMN
= "(?i)(DeterminationNote)";
126 private static final String EXTENSION_COLUMN
= "(?i)(Ext(ension)?)";
129 public SpecimenCdmExcelImport() {
137 protected void analyzeSingleValue(KeyValue keyValue
, SpecimenCdmExcelImportState state
) {
138 SpecimenRow row
= state
.getCurrentRow();
139 String value
= keyValue
.value
;
140 if(keyValue
.key
.matches(BASIS_OF_RECORD_COLUMN
)) {
141 row
.setBasisOfRecord(value
);
142 } else if(keyValue
.key
.matches(COUNTRY_COLUMN
)) {
143 row
.setCountry(value
);
144 } else if(keyValue
.key
.matches(ISO_COUNTRY_COLUMN
)) {
145 row
.setIsoCountry(value
);
146 } else if(keyValue
.key
.matches(LOCALITY_COLUMN
)) {
147 row
.setLocality(value
);
148 } else if(keyValue
.key
.matches(FIELD_NOTES_COLUMN
)) {
149 row
.setLocality(value
);
150 } else if(keyValue
.key
.matches(ALTITUDE_COLUMN
)) {
151 row
.setAltitude(value
);
152 } else if(keyValue
.key
.matches(ALTITUDE_MAX_COLUMN
)) {
153 row
.setAltitudeMax(value
);
154 } else if(keyValue
.key
.matches(COLLECTOR_COLUMN
)) {
155 row
.putCollector(keyValue
.index
, value
);
156 } else if(keyValue
.key
.matches(PRIMARY_COLLECTOR_COLUMN
)) {
157 row
.setPrimaryCollector(value
);
158 } else if(keyValue
.key
.matches(ECOLOGY_COLUMN
)) {
159 row
.setEcology(value
);
160 } else if(keyValue
.key
.matches(PLANT_DESCRIPTION_COLUMN
)) {
161 row
.setPlantDescription(value
);
162 } else if(keyValue
.key
.matches(SEX_COLUMN
)) {
164 } else if(keyValue
.key
.matches(COLLECTION_DATE_COLUMN
)) {
165 row
.setCollectingDate(value
);
166 } else if(keyValue
.key
.matches(COLLECTION_DATE_END_COLUMN
)) {
167 row
.setCollectingDateEnd(value
);
168 } else if(keyValue
.key
.matches(COLLECTORS_COLUMN
)) {
169 row
.setCollectors(value
);
170 } else if(keyValue
.key
.matches(COLLECTOR_COLUMN
)) {
171 row
.putCollector(keyValue
.index
, value
);
172 } else if(keyValue
.key
.matches(COLLECTORS_NUMBER_COLUMN
)) {
173 row
.setCollectorsNumber(value
);
174 } else if(keyValue
.key
.matches(LONGITUDE_COLUMN
)) {
175 row
.setLongitude(value
);
176 } else if(keyValue
.key
.matches(LATITUDE_COLUMN
)) {
177 row
.setLatitude(value
);
178 } else if(keyValue
.key
.matches(REFERENCE_SYSTEM_COLUMN
)) {
179 row
.setReferenceSystem(value
);
180 } else if(keyValue
.key
.matches(ERROR_RADIUS_COLUMN
)) {
181 row
.setErrorRadius(value
);
182 } else if(keyValue
.key
.matches(AREA_COLUMN
)) {
183 if (keyValue
.postfix
!= null){
184 row
.addLeveledArea(keyValue
.postfix
, value
);
186 logger
.warn("Not yet implemented");
191 } else if(keyValue
.key
.matches(ACCESSION_NUMBER_COLUMN
)) {
192 row
.setLocality(value
);
193 } else if(keyValue
.key
.matches(BARCODE_COLUMN
)) {
194 row
.setBarcode(value
);
195 } else if(keyValue
.key
.matches(UNIT_NOTES_COLUMN
)) {
196 row
.putUnitNote(keyValue
.index
, value
);
199 } else if(keyValue
.key
.matches(FAMILY_COLUMN
)) {
200 row
.putDeterminationFamily(keyValue
.index
, value
);
201 } else if(keyValue
.key
.matches(GENUS_COLUMN
)) {
202 row
.putDeterminationGenus(keyValue
.index
, value
);
203 } else if(keyValue
.key
.matches(SPECIFIC_EPITHET_COLUMN
)) {
204 row
.putDeterminationSpeciesEpi(keyValue
.index
, value
);
205 } else if(keyValue
.key
.matches(INFRASPECIFIC_EPITHET_COLUMN
)) {
206 row
.putDeterminationInfraSpeciesEpi(keyValue
.index
, value
);
207 } else if(keyValue
.key
.matches(RANK_COLUMN
)) {
208 row
.putDeterminationRank(keyValue
.index
, value
);
209 } else if(keyValue
.key
.matches(TAXON_UUID_COLUMN
)) {
210 row
.putDeterminationTaxonUuid(keyValue
.index
, value
);
211 } else if(keyValue
.key
.matches(FULL_NAME_COLUMN
)) {
212 row
.putDeterminationFullName(keyValue
.index
, value
);
213 } else if(keyValue
.key
.matches(DETERMINATION_AUTHOR_COLUMN
)) {
214 row
.putDeterminationAuthor(keyValue
.index
, value
);
215 } else if(keyValue
.key
.matches(DETERMINATION_MODIFIER_COLUMN
)) {
216 row
.putDeterminationDeterminationModifier(keyValue
.index
, value
);
217 } else if(keyValue
.key
.matches(DETERMINATION_NOTES_COLUMN
)) {
218 row
.putDeterminationDeterminationNotes(keyValue
.index
, value
);
219 } else if(keyValue
.key
.matches(DETERMINED_BY_COLUMN
)) {
220 row
.putDeterminationDeterminedBy(keyValue
.index
, value
);
221 } else if(keyValue
.key
.matches(DETERMINED_WHEN_COLUMN
)) {
222 row
.putDeterminationDeterminedWhen(keyValue
.index
, value
);
224 } else if(keyValue
.key
.matches(COLLECTION_CODE_COLUMN
)) {
225 row
.setCollectionCode(value
);
226 } else if(keyValue
.key
.matches(COLLECTION_COLUMN
)) {
227 row
.setCollection(value
);
229 } else if(keyValue
.key
.matches(TYPE_CATEGORY_COLUMN
)) {
230 row
.putTypeCategory(keyValue
.index
, getSpecimenTypeStatus(state
, value
));
231 } else if(keyValue
.key
.matches(TYPIFIED_NAME_COLUMN
)) {
232 row
.putTypifiedName(keyValue
.index
, getTaxonName(state
, value
));
235 } else if(keyValue
.key
.matches(SOURCE_COLUMN
)) {
236 row
.putSourceReference(keyValue
.index
, getOrMakeReference(state
, value
) );
237 } else if(keyValue
.key
.matches(ID_IN_SOURCE_COLUMN
)) {
238 row
.putIdInSource(keyValue
.index
, value
);
239 } else if(keyValue
.key
.matches(EXTENSION_COLUMN
)) {
240 if (keyValue
.postfix
!= null){
241 row
.addExtension(keyValue
.postfix
, value
);
243 logger
.warn("Extension without postfix not yet implemented");
247 state
.setUnsuccessfull();
248 logger
.error("Unexpected column header " + keyValue
.originalKey
);
256 protected void firstPass(SpecimenCdmExcelImportState state
) {
257 SpecimenRow row
= state
.getCurrentRow();
260 SpecimenOrObservationType type
= SpecimenOrObservationType
.valueOf2(row
.getBasisOfRecord());
262 String message
= "%s is not a valid BasisOfRecord. 'Unknown' is used instead in line %d.";
263 message
= String
.format(message
, row
.getBasisOfRecord(), state
.getCurrentLine());
264 logger
.warn(message
);
265 type
= SpecimenOrObservationType
.DerivedUnit
;
267 DerivedUnitFacade facade
= DerivedUnitFacade
.NewInstance(type
);
270 handleCountry(facade
, row
, state
);
271 handleAreas(facade
,row
, state
);
273 facade
.setGatheringPeriod(getTimePeriod(row
.getCollectingDate(), row
.getCollectingDateEnd()));
274 facade
.setLocality(row
.getLocality());
275 facade
.setFieldNotes(row
.getFieldNotes());
276 facade
.setFieldNumber(row
.getCollectorsNumber());
277 facade
.setEcology(row
.getEcology());
278 facade
.setPlantDescription(row
.getPlantDescription());
279 // facade.setSex(row.get)
280 handleExactLocation(facade
, row
, state
);
281 facade
.setCollector(getOrMakeAgent(state
, row
.getCollectors()));
282 facade
.setPrimaryCollector(getOrMakePrimaryCollector(facade
, row
.getPrimaryCollector(), state
));
283 handleAbsoluteElevation(facade
, row
, state
);
287 facade
.setBarcode(row
.getBarcode());
288 facade
.setAccessionNumber(row
.getAccessionNumber());
289 facade
.setCollection(getOrMakeCollection(state
, row
.getCollectionCode(), row
.getCollection()));
290 for (IdentifiableSource source
: row
.getSources()){
291 facade
.addSource(source
);
293 for (SpecimenTypeDesignation designation
: row
.getTypeDesignations()){
294 logger
.warn("FIXME"); //FIXME
295 // facade.innerDerivedUnit().addSpecimenTypeDesignation(designation);
297 handleDeterminations(state
, row
, facade
);
298 handleExtensions(facade
.innerDerivedUnit(),row
, state
);
299 for (String note
: row
.getUnitNotes()){
300 Annotation annotation
= Annotation
.NewInstance(note
, AnnotationType
.EDITORIAL(), Language
.DEFAULT());
301 facade
.addAnnotation(annotation
);
305 getOccurrenceService().save(facade
.innerDerivedUnit());
309 private void handleAbsoluteElevation(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
313 String altitude
= row
.getAltitude();
314 if (StringUtils
.isBlank(altitude
)){
317 // if (altitude.endsWith(".0")){
318 // altitude = altitude.substring(0, altitude.length() -2);
320 int value
= Integer
.valueOf(altitude
);
321 facade
.setAbsoluteElevation(value
);
322 } catch (NumberFormatException e
) {
323 String message
= "Absolute elevation / altitude '%s' is not an integer number in line %d";
324 message
= String
.format(message
, row
.getAltitude(), state
.getCurrentLine());
325 logger
.warn(message
);
332 String max
= row
.getAltitudeMax();
333 if (StringUtils
.isBlank(max
)){
336 // if (max.endsWith(".0")){
337 // max = max.substring(0, max.length() -2);
339 int value
= Integer
.valueOf(max
);
340 //TODO avoid unequal distance
341 int min
= facade
.getAbsoluteElevation();
342 if ( (value
- min
) % 2 == 1 ){
343 String message
= "Altitude min-max difference ist not equal. Max reduced by 1 in line %d";
344 message
= String
.format(message
, state
.getCurrentLine());
345 logger
.warn(message
);
348 facade
.setAbsoluteElevationRange(min
, value
);
349 } catch (NumberFormatException e
) {
350 String message
= "Absolute elevation / Altitude maximum '%s' is not an integer number in line %d";
351 message
= String
.format(message
, row
.getAltitudeMax(), state
.getCurrentLine());
352 logger
.warn(message
);
354 }catch (Exception e
){
355 String message
= "Error occurred when trying to write Absolute elevation / Altitude maximum '%s' in line %d";
356 message
= String
.format(message
, row
.getAltitudeMax(), state
.getCurrentLine());
357 logger
.warn(message
);
365 private void handleAreas(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
366 List
<PostfixTerm
> areas
= row
.getLeveledAreas();
368 for (PostfixTerm lArea
: areas
){
369 String description
= lArea
.term
;
370 String abbrev
= lArea
.term
;
371 NamedAreaType type
= null;
372 String key
= lArea
.postfix
+ "_" + lArea
.term
;
373 UUID areaUuid
= state
.getArea(key
);
374 NamedAreaLevel level
= state
.getPostfixLevel(lArea
.postfix
);
376 TermMatchMode matchMode
= state
.getConfig().getAreaMatchMode();
377 NamedArea area
= getNamedArea(state
, areaUuid
, lArea
.term
, description
, abbrev
, type
, level
, null, matchMode
);
378 facade
.addCollectingArea(area
);
379 if (areaUuid
== null){
380 state
.putArea(key
, area
.getUuid());
391 private void handleDeterminations(SpecimenCdmExcelImportState state
,SpecimenRow row
, DerivedUnitFacade facade
) {
392 boolean isFirstDetermination
= true;
393 DeterminationLight commonDetermination
= row
.getCommonDetermination();
394 Taxon commonTaxon
= null;
395 TaxonNameBase
<?
,?
> commonName
= null;
397 boolean hasCommonTaxonInfo
= (commonDetermination
== null) ?
false : commonDetermination
.hasTaxonInformation();
398 if (hasCommonTaxonInfo
&& commonDetermination
!= null){
399 TaxonBase
<?
> taxonBase
= null;
400 if (StringUtils
.isNotBlank(commonDetermination
.taxonUuid
)){
401 UUID taxonUuid
= UUID
.fromString(commonDetermination
.taxonUuid
);
402 taxonBase
= getTaxonService().find(taxonUuid
);
403 if (taxonBase
== null){
404 String message
= "Taxon for uuid %s not found in line %d.";
405 message
= String
.format(message
, taxonUuid
.toString(), state
.getCurrentLine());
406 logger
.warn(message
);
409 taxonBase
= findBestMatchingTaxon(state
, commonDetermination
, state
.getConfig().isCreateTaxonIfNotExists());
411 commonTaxon
= getAcceptedTaxon(taxonBase
);
412 if (taxonBase
!= null){
413 commonName
= taxonBase
.getName();
415 commonTaxon
= createTaxonFromDetermination(state
, commonDetermination
);
416 commonName
= commonTaxon
.getName();
421 for (DeterminationLight determinationLight
: row
.getDetermination()){
423 if (! hasCommonTaxonInfo
){
424 taxon
= findBestMatchingTaxon(state
, determinationLight
, state
.getConfig().isCreateTaxonIfNotExists());
429 getTaxonService().saveOrUpdate(taxon
);
430 if (state
.getConfig().isMakeIndividualAssociations() && taxon
!= null){
431 IndividualsAssociation indivAssociciation
= IndividualsAssociation
.NewInstance();
432 DerivedUnit du
= facade
.innerDerivedUnit();
433 indivAssociciation
.setAssociatedSpecimenOrObservation(du
);
434 getTaxonDescription(taxon
).addElement(indivAssociciation
);
435 Feature feature
= Feature
.INDIVIDUALS_ASSOCIATION();
436 if (facade
.getType().isPreservedSpecimen()){
437 feature
= Feature
.SPECIMEN();
438 }else if (facade
.getType().isFeatureObservation()){
439 feature
= Feature
.OBSERVATION();
441 if (state
.getConfig().isUseMaterialsExaminedForIndividualsAssociations()){
442 feature
= Feature
.MATERIALS_EXAMINED();
445 indivAssociciation
.setFeature(feature
);
447 if (state
.getConfig().isDeterminationsAreDeterminationEvent()){
448 DeterminationEvent detEvent
= makeDeterminationEvent(state
, determinationLight
, taxon
);
449 detEvent
.setPreferredFlag(isFirstDetermination
);
450 facade
.addDetermination(detEvent
);
454 if (isFirstDetermination
&& state
.getConfig().isFirstDeterminationIsStoredUnder()){
455 TaxonNameBase
<?
,?
> name
;
457 if (!hasCommonTaxonInfo
){
458 name
= findBestMatchingName(state
, determinationLight
);
460 if (commonName
== null){
461 commonName
= findBestMatchingName(state
, commonDetermination
);
466 facade
.setStoredUnder(name
);
469 isFirstDetermination
= false;
473 private Taxon
createTaxonFromDetermination( SpecimenCdmExcelImportState state
, DeterminationLight commonDetermination
) {
478 rank
= StringUtils
.isBlank(commonDetermination
.rank
) ?
null : Rank
.getRankByNameOrIdInVoc(commonDetermination
.rank
, true);
479 } catch (UnknownCdmTypeException e
) {
484 NonViralName
<?
> name
;
485 INonViralNameParser
<NonViralName
> parser
= NonViralNameParserImpl
.NewInstance();
486 NomenclaturalCode nc
= state
.getConfig().getNomenclaturalCode();
487 if (StringUtils
.isNotBlank(commonDetermination
.fullName
)){
488 name
= parser
.parseFullName(commonDetermination
.fullName
, nc
, rank
);
489 if (StringUtils
.isBlank(name
.getAuthorshipCache()) && StringUtils
.isNotBlank(commonDetermination
.author
)){
490 setAuthorship(name
, commonDetermination
.author
, parser
);
494 name
= (NonViralName
)nc
.getNewTaxonNameInstance(rank
);
496 name
= NonViralName
.NewInstance(rank
);
498 if (StringUtils
.isNotBlank(commonDetermination
.genus
)){
499 name
.setGenusOrUninomial(commonDetermination
.genus
);
501 if (StringUtils
.isNotBlank(commonDetermination
.speciesEpi
)){
502 name
.setSpecificEpithet(commonDetermination
.speciesEpi
);
504 if (StringUtils
.isNotBlank(commonDetermination
.infraSpeciesEpi
)){
505 name
.setInfraSpecificEpithet(commonDetermination
.infraSpeciesEpi
);
507 if (StringUtils
.isNotBlank(commonDetermination
.author
)){
508 setAuthorship(name
, commonDetermination
.author
, parser
);
511 if (name
.getRank() == null){
512 if (name
.getInfraGenericEpithet() != null && name
.getSpecificEpithet() == null){
513 name
.setRank(Rank
.INFRAGENERICTAXON());
514 }else if (name
.getSpecificEpithet() != null && name
.getInfraSpecificEpithet() == null){
515 name
.setRank(Rank
.SPECIES());
516 }else if (name
.getInfraSpecificEpithet() != null){
517 name
.setRank(Rank
.INFRASPECIFICTAXON());
524 Reference
<?
> sec
= null;
525 if (StringUtils
.isNotBlank(commonDetermination
.determinedBy
)){
526 sec
= ReferenceFactory
.newGeneric();
527 TeamOrPersonBase
<?
> determinedBy
;
528 BotanicalName dummyName
= BotanicalName
.NewInstance(Rank
.SPECIES());
530 parser
.parseAuthors(dummyName
, commonDetermination
.determinedBy
);
531 determinedBy
= (TeamOrPersonBase
<?
>)dummyName
.getCombinationAuthorTeam();
532 } catch (StringNotParsableException e
) {
533 determinedBy
= Team
.NewTitledInstance(commonDetermination
.determinedBy
, commonDetermination
.determinedBy
);
535 sec
.setAuthorTeam(determinedBy
);
539 Taxon taxon
= Taxon
.NewInstance(name
, sec
);
541 if (StringUtils
.isNotBlank(commonDetermination
.family
)){
542 if (name
.getRank() == null || name
.getRank().isLower(Rank
.FAMILY()) ){
543 logger
.warn("Family taxon could not be created");
555 private void setAuthorship(NonViralName
<?
> name
, String author
, INonViralNameParser
<NonViralName
> parser
) {
556 if (name
.isInstanceOf(BotanicalName
.class) || name
.isInstanceOf(ZoologicalName
.class)){
558 parser
.parseAuthors(name
, author
);
559 } catch (StringNotParsableException e
) {
560 name
.setAuthorshipCache(author
);
563 name
.setAuthorshipCache(author
);
572 * This method tries to find the best matching taxon depending on the import configuration,
573 * the taxon name information and the concept information available.
577 * @param determinationLight
578 * @param createIfNotExists
581 private Taxon
findBestMatchingTaxon(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
, boolean createIfNotExists
) {
582 NonViralName
<?
> name
= makeTaxonName(state
, determinationLight
);
584 String titleCache
= makeSearchNameTitleCache(state
, determinationLight
, name
);
586 if (! StringUtils
.isBlank(titleCache
)){
587 MatchingTaxonConfigurator matchConfigurator
= MatchingTaxonConfigurator
.NewInstance();
588 matchConfigurator
.setTaxonNameTitle(titleCache
);
589 matchConfigurator
.setIncludeSynonyms(false);
590 Taxon taxon
= getTaxonService().findBestMatchingTaxon(matchConfigurator
);
592 if(taxon
== null && createIfNotExists
){
593 logger
.info("creating new Taxon from TaxonName '" + titleCache
+"'");
594 UUID secUuid
= null; //TODO
595 Reference
<?
> sec
= null;
596 if (secUuid
!= null){
597 sec
= getReferenceService().find(secUuid
);
599 taxon
= Taxon
.NewInstance(name
, sec
);
600 }else if (taxon
== null){
601 String message
= "Taxon '%s' not found in line %d";
602 message
= String
.format(message
, titleCache
, state
.getCurrentLine());
603 logger
.warn(message
);
613 * @param determinationLight
617 private String
makeSearchNameTitleCache(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
,
618 NonViralName
<?
> name
) {
619 String titleCache
= determinationLight
.fullName
;
620 if (! state
.getConfig().isPreferNameCache() || StringUtils
.isBlank(titleCache
) ){
621 String computedTitleCache
= name
.getTitleCache();
622 if (StringUtils
.isNotBlank(computedTitleCache
)){
623 titleCache
= computedTitleCache
;
632 * @param determinationLight
635 private NonViralName
<?
> makeTaxonName(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
) {
636 NonViralName
<?
> name
= NonViralName
.NewInstance(null);
637 NomenclaturalCode nc
= state
.getConfig().getNomenclaturalCode();
639 name
= (NonViralName
<?
>)nc
.getNewTaxonNameInstance(null);
641 name
.setGenusOrUninomial(determinationLight
.genus
);
642 name
.setSpecificEpithet(determinationLight
.speciesEpi
);
643 name
.setInfraSpecificEpithet(determinationLight
.infraSpeciesEpi
);
645 //FIXME bracketAuthors and teams not yet implemented!!!
646 List
<String
> authors
= new ArrayList
<String
>();
647 if (StringUtils
.isNotBlank(determinationLight
.author
)){
648 authors
.add(determinationLight
.author
);
650 TeamOrPersonBase
<?
> agent
= (TeamOrPersonBase
)getOrMakeAgent(state
, authors
);
651 name
.setCombinationAuthorTeam(agent
);
654 if (StringUtils
.isNotBlank(determinationLight
.rank
) ){
655 name
.setRank(Rank
.getRankByNameOrIdInVoc(determinationLight
.rank
, nc
, true));
657 } catch (UnknownCdmTypeException e
) {
658 String message
= "Rank not found: %s: ";
659 message
= String
.format(message
, determinationLight
.rank
);
660 logger
.warn(message
);
662 if (StringUtils
.isBlank(name
.getInfraSpecificEpithet()) && StringUtils
.isNotBlank(name
.getSpecificEpithet() )){
663 name
.setRank(Rank
.SPECIES());
665 if (StringUtils
.isBlank(name
.getSpecificEpithet()) && StringUtils
.isNotBlank(name
.getGenusOrUninomial() )){
666 name
.setRank(Rank
.SPECIES());
668 if (StringUtils
.isBlank(name
.getTitleCache())){
670 name
.setTitleCache(determinationLight
.fullName
, true);
675 private TaxonNameBase
findBestMatchingName(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
) {
677 NonViralName
<?
> name
= makeTaxonName(state
, determinationLight
);
678 String titleCache
= makeSearchNameTitleCache(state
, determinationLight
, name
);
681 List
<TaxonNameBase
> matchingNames
= getNameService().findByName(null, titleCache
, MatchMode
.EXACT
, null, null, null, null, null).getRecords();
682 if (matchingNames
.size() > 0){
683 return matchingNames
.get(0);
684 } else if (matchingNames
.size() > 0){
685 logger
.warn("Get best matching taxon name not yet fully implemeted for specimen import");
686 return matchingNames
.get(0);
694 private DeterminationEvent
makeDeterminationEvent(SpecimenCdmExcelImportState state
, DeterminationLight determination
, Taxon taxon
) {
695 DeterminationEvent event
= DeterminationEvent
.NewInstance();
697 event
.setTaxon(taxon
);
700 TimePeriod date
= TimePeriodParser
.parseString(determination
.determinedWhen
);
701 event
.setTimeperiod(date
);
703 //FIXME bracketAuthors and teams not yet implemented!!!
704 List
<String
> authors
= new ArrayList
<String
>();
705 if (StringUtils
.isNotBlank(determination
.determinedBy
)){
706 authors
.add(determination
.determinedBy
);
708 TeamOrPersonBase
<?
> actor
= getOrMakeAgent(state
, authors
);
709 TeamOrPersonBase
<?
> secAuthor
= taxon
.getSec() == null ?
null : taxon
.getSec().getAuthorTeam();
710 if (actor
!= null && secAuthor
!= null && secAuthor
.getTitleCache().equals(actor
.getTitleCache()) && secAuthor
.getNomenclaturalTitle().equals(actor
.getNomenclaturalTitle())) {
714 event
.setActor(actor
);
717 if (StringUtils
.isNotBlank(determination
.modifier
)){
718 logger
.warn("DeterminationModifiers not yet implemented for specimen import");
720 // DeterminationModifier modifier = DeterminationModifier.NewInstance(term, label, labelAbbrev);
721 // determination.modifier;
723 if (StringUtils
.isNotEmpty(determination
.notes
)){
724 Annotation annotation
= Annotation
.NewInstance(determination
.notes
, AnnotationType
.EDITORIAL(), Language
.DEFAULT());
725 event
.addAnnotation(annotation
);
731 private TaxonDescription
getTaxonDescription(Taxon taxon
) {
732 TaxonDescription desc
= this.getTaxonDescription(taxon
, ! IMAGE_GALLERY
, CREATE
);
736 private TeamOrPersonBase
<?
> getOrMakeAgent(SpecimenCdmExcelImportState state
, List
<String
> agents
) {
737 if (agents
.size() == 0){
739 }else if (agents
.size() == 1){
740 return getOrMakePerson(state
, agents
.get(0));
742 return getOrMakeTeam(state
, agents
);
746 private Person
getOrMakePrimaryCollector(DerivedUnitFacade facade
, String primaryCollector
, SpecimenCdmExcelImportState state
) {
747 if (StringUtils
.isBlank(primaryCollector
)){
750 AgentBase
<?
> collector
= facade
.getCollector();
751 List
<Person
> collectors
= new ArrayList
<Person
>();
752 if (collector
.isInstanceOf(Team
.class) ){
753 Team team
= CdmBase
.deproxy(collector
, Team
.class);
754 collectors
.addAll(team
.getTeamMembers());
755 }else if (collector
.isInstanceOf(Person
.class)){
756 collectors
.add(CdmBase
.deproxy(collector
, Person
.class));
758 throw new IllegalStateException("Unknown subclass of agentbase: " + collector
.getClass().getName() );
760 for (Person person
:collectors
){
761 if (primaryCollector
.equalsIgnoreCase(person
.getTitleCache())){
764 if (primaryCollector
.equalsIgnoreCase(person
.getNomenclaturalTitle())){
768 String message
= "Primary Agent '%s' could not be determined in collector(s) in line %d";
769 message
= String
.format(message
, primaryCollector
, state
.getCurrentLine());
770 logger
.warn(message
);
774 private Team
getOrMakeTeam(SpecimenCdmExcelImportState state
, List
<String
> agents
) {
775 String key
= CdmUtils
.concat("_", agents
.toArray(new String
[0]));
777 Team result
= state
.getTeam(key
);
779 result
= Team
.NewInstance();
780 for (String member
: agents
){
781 Person person
= getOrMakePerson(state
, member
);
782 result
.addTeamMember(person
);
784 state
.putTeam(key
, result
);
789 private Person
getOrMakePerson(SpecimenCdmExcelImportState state
, String value
) {
790 Person result
= state
.getPerson(value
);
792 result
= Person
.NewInstance();
793 result
.setTitleCache(value
, true);
794 state
.putPerson(value
, result
);
799 private Reference
<?
> getOrMakeReference(SpecimenCdmExcelImportState state
, String value
) {
800 Reference
<?
> result
= state
.getReference(value
);
802 result
= ReferenceFactory
.newGeneric();
803 result
.setTitleCache(value
, true);
804 state
.putReference(value
, result
);
811 private Collection
getOrMakeCollection(SpecimenCdmExcelImportState state
, String collectionCode
, String collectionString
) {
812 Collection result
= state
.getCollection(collectionCode
);
814 result
= Collection
.NewInstance();
815 result
.setCode(collectionCode
);
816 result
.setName(collectionString
);
817 state
.putCollection(collectionCode
, result
);
823 private TaxonNameBase
<?
, ?
> getTaxonName(SpecimenCdmExcelImportState state
, String name
) {
824 TaxonNameBase
<?
,?
> result
= null;
825 result
= state
.getName(name
);
829 List
<TaxonNameBase
<?
,?
>> list
= getNameService().findNamesByTitle(name
);
830 //TODO better strategy to find best name, e.g. depending on the classification it is used in
831 if (! list
.isEmpty()){
832 result
= list
.get(0);
835 NonViralNameParserImpl parser
= NonViralNameParserImpl
.NewInstance();
836 NomenclaturalCode code
= state
.getConfig().getNomenclaturalCode();
837 result
= parser
.parseFullName(name
, code
, null);
841 state
.putName(name
, result
);
846 private SpecimenTypeDesignationStatus
getSpecimenTypeStatus(SpecimenCdmExcelImportState state
, String key
) {
847 SpecimenTypeDesignationStatus result
= null;
849 result
= state
.getTransformer().getSpecimenTypeDesignationStatusByKey(key
);
851 String message
= "Type status not recognized for %s in line %d";
852 message
= String
.format(message
, key
, state
.getCurrentLine());
853 logger
.warn(message
);
856 } catch (UndefinedTransformerMethodException e
) {
857 throw new RuntimeException("getSpecimenTypeDesignationStatusByKey not yet implemented");
864 private void handleExactLocation(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
867 ReferenceSystem refSys
= null;
868 if (StringUtils
.isNotBlank(row
.getReferenceSystem())){
869 String strRefSys
= row
.getReferenceSystem().trim().replaceAll("\\s", "");
872 refSys
= state
.getTransformer().getReferenceSystemByKey(strRefSys
);
874 refUuid
= state
.getTransformer().getReferenceSystemUuid(strRefSys
);
875 if (refUuid
== null){
876 String message
= "Unknown reference system %s in line %d";
877 message
= String
.format(message
, strRefSys
, state
.getCurrentLine());
878 logger
.warn(message
);
880 refSys
= getReferenceSystem(state
, refUuid
, strRefSys
, strRefSys
, strRefSys
, null);
883 } catch (UndefinedTransformerMethodException e
) {
884 throw new RuntimeException(e
);
892 String longitude
= row
.getLongitude();
893 String latitude
= row
.getLatitude();
894 Integer errorRadius
= null;
895 if (StringUtils
.isNotBlank(row
.getErrorRadius())){
897 errorRadius
= Integer
.valueOf(row
.getErrorRadius());
898 } catch (NumberFormatException e
) {
899 String message
= "Error radius %s could not be transformed to Integer in line %d";
900 message
= String
.format(message
, row
.getErrorRadius(), state
.getCurrentLine());
901 logger
.warn(message
);
905 facade
.setExactLocationByParsing(longitude
, latitude
, refSys
, errorRadius
);
906 } catch (ParseException e
) {
907 String message
= "Problems when parsing exact location for line %d";
908 message
= String
.format(message
, state
.getCurrentLine());
909 logger
.warn(message
);
919 * Set the current Country
920 * Search in the DB if the isoCode is known
921 * If not, search if the country name is in the DB
922 * If not, create a new Label with the Level Country
923 * @param iso: the country iso code
924 * @param fullName: the country's full name
925 * @param app: the CDM application controller
927 private void handleCountry(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
929 if (StringUtils
.isNotBlank(row
.getIsoCountry())){
930 NamedArea country
= getOccurrenceService().getCountryByIso(row
.getIsoCountry());
931 if (country
!= null){
932 facade
.setCountry(country
);
936 if (StringUtils
.isNotBlank(row
.getCountry())){
937 List
<Country
> countries
= getOccurrenceService().getCountryByName(row
.getCountry());
938 if (countries
.size() >0){
939 facade
.setCountry(countries
.get(0));
941 UUID uuid
= UUID
.randomUUID();
942 String label
= row
.getCountry();
943 String text
= row
.getCountry();
944 String labelAbbrev
= null;
945 NamedAreaType areaType
= NamedAreaType
.ADMINISTRATION_AREA();
946 NamedAreaLevel level
= NamedAreaLevel
.COUNTRY();
947 NamedArea newCountry
= this.getNamedArea(state
, uuid
, label
, text
, labelAbbrev
, areaType
, level
);
948 facade
.setCountry(newCountry
);
954 protected void secondPass(SpecimenCdmExcelImportState state
) {
955 //no second path defined yet
961 protected String
getWorksheetName() {
962 return WORKSHEET_NAME
;
966 protected boolean needsNomenclaturalCode() {
972 * @see eu.etaxonomy.cdm.io.excel.common.ExcelTaxonOrSpecimenImportBase#createDataHolderRow()
975 protected SpecimenRow
createDataHolderRow() {
976 return new SpecimenRow();
983 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
986 protected boolean doCheck(SpecimenCdmExcelImportState state
) {
987 logger
.warn("Validation not yet implemented for " + this.getClass().getSimpleName());
994 protected boolean isIgnore(SpecimenCdmExcelImportState state
) {
995 return !state
.getConfig().isDoSpecimen();