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
.HashMap
;
15 import java
.util
.List
;
17 import java
.util
.UUID
;
19 import org
.apache
.commons
.lang
.StringUtils
;
20 import org
.apache
.log4j
.Logger
;
21 import org
.springframework
.stereotype
.Component
;
23 import sun
.security
.util
.DerEncoder
;
25 import eu
.etaxonomy
.cdm
.api
.facade
.DerivedUnitFacade
;
26 import eu
.etaxonomy
.cdm
.api
.facade
.DerivedUnitFacade
.DerivedUnitType
;
27 import eu
.etaxonomy
.cdm
.api
.service
.config
.MatchingTaxonConfigurator
;
28 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
29 import eu
.etaxonomy
.cdm
.io
.common
.CdmImportBase
.TermMatchMode
;
30 import eu
.etaxonomy
.cdm
.io
.common
.ICdmIO
;
31 import eu
.etaxonomy
.cdm
.io
.common
.mapping
.UndefinedTransformerMethodException
;
32 import eu
.etaxonomy
.cdm
.io
.excel
.common
.ExcelImporterBase
;
33 import eu
.etaxonomy
.cdm
.io
.specimen
.excel
.in
.SpecimenRow
.DeterminationLight
;
34 import eu
.etaxonomy
.cdm
.io
.specimen
.excel
.in
.SpecimenRow
.PostfixTerm
;
35 import eu
.etaxonomy
.cdm
.model
.agent
.AgentBase
;
36 import eu
.etaxonomy
.cdm
.model
.agent
.Person
;
37 import eu
.etaxonomy
.cdm
.model
.agent
.Team
;
38 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
39 import eu
.etaxonomy
.cdm
.model
.common
.Annotation
;
40 import eu
.etaxonomy
.cdm
.model
.common
.AnnotationType
;
41 import eu
.etaxonomy
.cdm
.model
.common
.Extension
;
42 import eu
.etaxonomy
.cdm
.model
.common
.ExtensionType
;
43 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
44 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
45 import eu
.etaxonomy
.cdm
.model
.common
.TimePeriod
;
46 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
47 import eu
.etaxonomy
.cdm
.model
.description
.IndividualsAssociation
;
48 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
49 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
50 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaLevel
;
51 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaType
;
52 import eu
.etaxonomy
.cdm
.model
.location
.ReferenceSystem
;
53 import eu
.etaxonomy
.cdm
.model
.location
.WaterbodyOrCountry
;
54 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalCode
;
55 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
56 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
57 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignation
;
58 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignationStatus
;
59 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
60 import eu
.etaxonomy
.cdm
.model
.occurrence
.Collection
;
61 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnitBase
;
62 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
63 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
64 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceFactory
;
65 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
66 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
67 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.UnknownCdmTypeException
;
68 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImpl
;
76 public class SpecimenCdmExcelImport
extends ExcelImporterBase
<SpecimenCdmExcelImportState
> 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 UUID_COLUMN
= "(?i)(UUID)";
82 private static final String BASIS_OF_RECORD_COLUMN
= "(?i)(BasisOfRecord)";
83 private static final String COUNTRY_COLUMN
= "(?i)(Country)";
84 private static final String AREA_COLUMN
= "(?i)(Area)";
85 private static final String ISO_COUNTRY_COLUMN
= "(?i)(ISOCountry|CountryCode)";
86 private static final String LOCALITY_COLUMN
= "(?i)(Locality)";
87 private static final String ALTITUDE_COLUMN
= "(?i)(AbsoluteElevation|Altitude)";
88 private static final String ALTITUDE_MAX_COLUMN
= "(?i)(AbsoluteElevation|Altitude)Max(imum)?";
89 private static final String COLLECTION_DATE_COLUMN
= "(?i)(CollectionDate)";
90 private static final String COLLECTION_DATE_END_COLUMN
= "(?i)(CollectionDateEnd)";
91 private static final String COLLECTOR_COLUMN
= "(?i)(Collector)";
92 private static final String LONGITUDE_COLUMN
= "(?i)(Longitude)";
93 private static final String LATITUDE_COLUMN
= "(?i)(Latitude)";
94 private static final String REFERENCE_SYSTEM_COLUMN
= "(?i)(ReferenceSystem)";
95 private static final String ERROR_RADIUS_COLUMN
= "(?i)(ErrorRadius)";
98 private static final String COLLECTORS_NUMBER_COLUMN
= "(?i)((Collectors|Field)Number)";
99 private static final String ECOLOGY_COLUMN
= "(?i)(Ecology|Habitat)";
100 private static final String PLANT_DESCRIPTION_COLUMN
= "(?i)(PlantDescription)";
101 private static final String FIELD_NOTES_COLUMN
= "(?i)(FieldNotes)";
102 private static final String SEX_COLUMN
= "(?i)(Sex)";
105 private static final String ACCESSION_NUMBER_COLUMN
= "(?i)(AccessionNumber)";
106 private static final String BARCODE_COLUMN
= "(?i)(Barcode)";
107 private static final String COLLECTION_CODE_COLUMN
= "(?i)(CollectionCode)";
108 private static final String COLLECTION_COLUMN
= "(?i)(Collection)";
109 private static final String UNIT_NOTES_COLUMN
= "(?i)((Unit)?Notes)";
112 private static final String TYPE_CATEGORY_COLUMN
= "(?i)(TypeCategory)";
113 private static final String TYPIFIED_NAME_COLUMN
= "(?i)(TypifiedName|TypeOf)";
116 private static final String SOURCE_COLUMN
= "(?i)(Source)";
117 private static final String ID_IN_SOURCE_COLUMN
= "(?i)(IdInSource)";
120 private static final String RANK_COLUMN
= "(?i)(Rank)";
121 private static final String FULL_NAME_COLUMN
= "(?i)(FullName)";
122 private static final String FAMILY_COLUMN
= "(?i)(Family)";
123 private static final String GENUS_COLUMN
= "(?i)(Genus)";
124 private static final String SPECIFIC_EPITHET_COLUMN
= "(?i)(SpecificEpi(thet)?)";
125 private static final String INFRASPECIFIC_EPITHET_COLUMN
= "(?i)(InfraSpecificEpi(thet)?)";
126 private static final String DETERMINATION_AUTHOR_COLUMN
= "(?i)(Author)";
127 private static final String DETERMINATION_MODIFIER_COLUMN
= "(?i)(DeterminationModifier)";
128 private static final String DETERMINED_BY_COLUMN
= "(?i)(DeterminationBy)";
129 private static final String DETERMINED_WHEN_COLUMN
= "(?i)(DeterminationWhen)";
130 private static final String DETERMINATION_NOTES_COLUMN
= "(?i)(DeterminationNote)";
131 private static final String EXTENSION_COLUMN
= "(?i)(Ext(ension)?)";
133 private static final String IGNORE_COLUMN
= "(?i)(Ignore|Not)";
136 public SpecimenCdmExcelImport() {
142 protected boolean analyzeRecord(HashMap
<String
, String
> record
, SpecimenCdmExcelImportState state
) {
143 boolean success
= true;
144 Set
<String
> keys
= record
.keySet();
146 SpecimenRow row
= new SpecimenRow();
147 state
.setSpecimenRow(row
);
149 for (String originalKey
: keys
) {
151 String postfix
= null;
152 String indexedKey
= CdmUtils
.removeDuplicateWhitespace(originalKey
.trim()).toString();
153 String
[] split
= indexedKey
.split("_");
154 String key
= split
[0];
155 if (split
.length
> 1){
156 for (int i
= 1 ; i
< split
.length
; i
++ ){
157 String indexString
= split
[i
];
158 if (isInteger(indexString
)){
159 index
= Integer
.valueOf(indexString
);
166 String value
= (String
) record
.get(indexedKey
);
167 if (! StringUtils
.isBlank(value
)) {
168 if (logger
.isDebugEnabled()) { logger
.debug(key
+ ": " + value
); }
169 value
= CdmUtils
.removeDuplicateWhitespace(value
.trim()).toString();
174 if (key
.matches(UUID_COLUMN
)) {
175 row
.setUuid(UUID
.fromString(value
)); //VALIDATE UUID
176 } else if(key
.matches(BASIS_OF_RECORD_COLUMN
)) {
177 row
.setBasisOfRecord(value
);
178 } else if(key
.matches(COUNTRY_COLUMN
)) {
179 row
.setCountry(value
);
180 } else if(key
.matches(ISO_COUNTRY_COLUMN
)) {
181 row
.setIsoCountry(value
);
182 } else if(key
.matches(LOCALITY_COLUMN
)) {
183 row
.setLocality(value
);
184 } else if(key
.matches(FIELD_NOTES_COLUMN
)) {
185 row
.setLocality(value
);
186 } else if(key
.matches(ALTITUDE_COLUMN
)) {
187 row
.setAltitude(value
);
188 } else if(key
.matches(ALTITUDE_MAX_COLUMN
)) {
189 row
.setAltitudeMax(value
);
190 } else if(key
.matches(COLLECTOR_COLUMN
)) {
191 row
.putCollector(index
, value
);
192 } else if(key
.matches(ECOLOGY_COLUMN
)) {
193 row
.setEcology(value
);
194 } else if(key
.matches(PLANT_DESCRIPTION_COLUMN
)) {
195 row
.setPlantDescription(value
);
196 } else if(key
.matches(SEX_COLUMN
)) {
198 } else if(key
.matches(COLLECTION_DATE_COLUMN
)) {
199 row
.setCollectingDate(value
);
200 } else if(key
.matches(COLLECTION_DATE_END_COLUMN
)) {
201 row
.setCollectingDateEnd(value
);
202 } else if(key
.matches(COLLECTOR_COLUMN
)) {
203 row
.putCollector(index
, value
);
204 } else if(key
.matches(COLLECTORS_NUMBER_COLUMN
)) {
205 row
.setCollectorsNumber(value
);
206 } else if(key
.matches(LONGITUDE_COLUMN
)) {
207 row
.setLongitude(value
);
208 } else if(key
.matches(LATITUDE_COLUMN
)) {
209 row
.setLatitude(value
);
210 } else if(key
.matches(REFERENCE_SYSTEM_COLUMN
)) {
211 row
.setReferenceSystem(value
);
212 } else if(key
.matches(ERROR_RADIUS_COLUMN
)) {
213 row
.setErrorRadius(value
);
214 } else if(key
.matches(AREA_COLUMN
)) {
215 if (postfix
!= null){
216 row
.addLeveledArea(postfix
, value
);
218 logger
.warn("Not yet implemented");
223 } else if(key
.matches(ACCESSION_NUMBER_COLUMN
)) {
224 row
.setLocality(value
);
225 } else if(key
.matches(BARCODE_COLUMN
)) {
226 row
.setBarcode(value
);
227 } else if(key
.matches(UNIT_NOTES_COLUMN
)) {
228 row
.putUnitNote(index
, value
);
231 } else if(key
.matches(FAMILY_COLUMN
)) {
232 row
.putDeterminationFamily(index
, value
);
233 } else if(key
.matches(GENUS_COLUMN
)) {
234 row
.putDeterminationGenus(index
, value
);
235 } else if(key
.matches(SPECIFIC_EPITHET_COLUMN
)) {
236 row
.putDeterminationSpeciesEpi(index
, value
);
237 } else if(key
.matches(INFRASPECIFIC_EPITHET_COLUMN
)) {
238 row
.putDeterminationInfraSpeciesEpi(index
, value
);
239 } else if(key
.matches(RANK_COLUMN
)) {
240 row
.putDeterminationRank(index
, value
);
241 } else if(key
.matches(FULL_NAME_COLUMN
)) {
242 row
.putDeterminationFullName(index
, value
);
243 } else if(key
.matches(DETERMINATION_AUTHOR_COLUMN
)) {
244 row
.putDeterminationAuthor(index
, value
);
245 } else if(key
.matches(DETERMINATION_MODIFIER_COLUMN
)) {
246 row
.putDeterminationDeterminationModifier(index
, value
);
247 } else if(key
.matches(DETERMINATION_NOTES_COLUMN
)) {
248 row
.putDeterminationDeterminationNotes(index
, value
);
249 } else if(key
.matches(DETERMINED_BY_COLUMN
)) {
250 row
.putDeterminationDeterminedBy(index
, value
);
251 } else if(key
.matches(DETERMINED_WHEN_COLUMN
)) {
252 row
.putDeterminationDeterminedWhen(index
, value
);
254 } else if(key
.matches(COLLECTION_CODE_COLUMN
)) {
255 row
.setCollectionCode(value
);
256 } else if(key
.matches(COLLECTION_COLUMN
)) {
257 row
.setCollection(value
);
259 } else if(key
.matches(TYPE_CATEGORY_COLUMN
)) {
260 row
.putTypeCategory(index
, getSpecimenTypeStatus(state
, value
));
261 } else if(key
.matches(TYPIFIED_NAME_COLUMN
)) {
262 row
.putTypifiedName(index
, getTaxonName(state
, value
));
265 } else if(key
.matches(SOURCE_COLUMN
)) {
266 row
.putSourceReference(index
, getOrMakeReference(state
, value
));
267 } else if(key
.matches(ID_IN_SOURCE_COLUMN
)) {
268 row
.putIdInSource(index
, value
);
269 } else if(key
.matches(EXTENSION_COLUMN
)) {
270 if (postfix
!= null){
271 row
.addExtension(postfix
, value
);
273 logger
.warn("Extension without postfix not yet implemented");
276 } else if(key
.matches(IGNORE_COLUMN
)) {
277 logger
.debug("Ignored column" + originalKey
);
280 logger
.error("Unexpected column header " + originalKey
);
287 protected boolean firstPass(SpecimenCdmExcelImportState state
) {
288 SpecimenRow row
= state
.getSpecimenRow();
291 DerivedUnitType type
= DerivedUnitType
.valueOf2(row
.getBasisOfRecord());
293 String message
= "%s is not a valid BasisOfRecord. 'Unknown' is used instead.";
294 message
= String
.format(message
, row
.getBasisOfRecord());
295 logger
.warn(message
);
296 type
= DerivedUnitType
.DerivedUnit
;
298 DerivedUnitFacade facade
= DerivedUnitFacade
.NewInstance(type
);
301 handleCountry(facade
, row
, state
);
302 handleAreas(facade
,row
, state
);
304 facade
.setGatheringPeriod(getTimePeriod(row
.getCollectingDate(), row
.getCollectingDateEnd()));
305 facade
.setLocality(row
.getLocality());
306 facade
.setFieldNotes(row
.getFieldNotes());
307 facade
.setFieldNumber(row
.getCollectorsNumber());
308 facade
.setEcology(row
.getEcology());
309 facade
.setPlantDescription(row
.getPlantDescription());
310 // facade.setSex(row.get)
311 handleExactLocation(facade
, row
, state
);
312 facade
.setCollector(getOrMakeAgent(state
, row
.getCollectors()));
313 handleAbsoluteElevation(facade
, row
, state
);
317 facade
.setBarcode(row
.getBarcode());
318 facade
.setAccessionNumber(row
.getAccessionNumber());
319 facade
.setCollection(getOrMakeCollection(state
, row
.getCollectionCode(), row
.getCollection()));
320 for (IdentifiableSource source
: row
.getSources()){
321 facade
.addSource(source
);
323 for (SpecimenTypeDesignation designation
: row
.getTypeDesignations()){
325 // facade.innerDerivedUnit().addSpecimenTypeDesignation(designation);
327 handleDeterminations(state
, row
, facade
);
328 handleExtensions(facade
,row
, state
);
329 for (String note
: row
.getUnitNotes()){
330 Annotation annotation
= Annotation
.NewInstance(note
, AnnotationType
.EDITORIAL(), Language
.DEFAULT());
331 facade
.addAnnotation(annotation
);
335 getOccurrenceService().save(facade
.innerDerivedUnit());
339 private void handleAbsoluteElevation(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
343 String altitude
= row
.getAltitude();
344 if (StringUtils
.isBlank(altitude
)){
347 // if (altitude.endsWith(".0")){
348 // altitude = altitude.substring(0, altitude.length() -2);
350 int value
= Integer
.valueOf(altitude
);
351 facade
.setAbsoluteElevation(value
);
352 } catch (NumberFormatException e
) {
353 String message
= "Absolute elevation / Altitude '%s' is not an integer number in line %d";
354 message
= String
.format(message
, row
.getAltitude(), state
.getCurrentLine());
355 logger
.warn(message
);
362 String max
= row
.getAltitudeMax();
363 if (StringUtils
.isBlank(max
)){
366 // if (max.endsWith(".0")){
367 // max = max.substring(0, max.length() -2);
369 int value
= Integer
.valueOf(max
);
370 //TODO avoid unequal distance
371 int min
= facade
.getAbsoluteElevation();
372 if ( (value
- min
) % 2 == 1 ){
373 String message
= "Altitude min-max difference ist not equal. Max reduced by 1 in line %d";
374 message
= String
.format(message
, state
.getCurrentLine());
375 logger
.warn(message
);
378 facade
.setAbsoluteElevationRange(min
, value
);
379 } catch (NumberFormatException e
) {
380 String message
= "Absolute elevation / Altitude maximum '%s' is not an integer number in line %d";
381 message
= String
.format(message
, row
.getAltitudeMax(), state
.getCurrentLine());
382 logger
.warn(message
);
384 }catch (Exception e
){
385 String message
= "Error occurred when trying to write Absolute elevation / Altitude maximum '%s' in line %d";
386 message
= String
.format(message
, row
.getAltitudeMax(), state
.getCurrentLine());
387 logger
.warn(message
);
396 private void handleExtensions(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
397 List
<PostfixTerm
> extensions
= row
.getExtensions();
399 for (PostfixTerm exType
: extensions
){
400 ExtensionType extensionType
= state
.getPostfixExtensionType(exType
.postfix
);
402 Extension extension
= Extension
.NewInstance();
403 extension
.setType(extensionType
);
404 extension
.setValue(exType
.term
);
405 facade
.innerDerivedUnit().addExtension(extension
);
411 private void handleAreas(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
412 List
<PostfixTerm
> areas
= row
.getLeveledAreas();
414 for (PostfixTerm lArea
: areas
){
415 String description
= lArea
.term
;
416 String abbrev
= lArea
.term
;
417 NamedAreaType type
= null;
418 String key
= lArea
.postfix
+ "_" + lArea
.term
;
419 UUID areaUuid
= state
.getArea(key
);
420 NamedAreaLevel level
= state
.getPostfixLevel(lArea
.postfix
);
422 TermMatchMode matchMode
= state
.getConfig().getAreaMatchMode();
423 NamedArea area
= getNamedArea(state
, areaUuid
, lArea
.term
, description
, abbrev
, type
, level
, null, matchMode
);
424 facade
.addCollectingArea(area
);
425 if (areaUuid
== null){
426 state
.putArea(key
, area
.getUuid());
437 private void handleDeterminations(SpecimenCdmExcelImportState state
,SpecimenRow row
, DerivedUnitFacade facade
) {
438 boolean isFirstDetermination
= true;
439 for (DeterminationLight determinationLight
: row
.getDetermination()){
440 Taxon taxon
= findBestMatchingTaxon(state
, determinationLight
, state
.getConfig().isCreateTaxonIfNotExists());
441 TaxonNameBase
<?
,?
> name
= findBestMatchingName(state
, determinationLight
);
443 getTaxonService().saveOrUpdate(taxon
);
444 if (state
.getConfig().isMakeIndividualAssociations() && taxon
!= null){
445 IndividualsAssociation indivAssociciation
= IndividualsAssociation
.NewInstance();
446 DerivedUnitBase
<?
> du
= facade
.innerDerivedUnit();
447 indivAssociciation
.setAssociatedSpecimenOrObservation(du
);
448 getTaxonDescription(taxon
).addElement(indivAssociciation
);
449 Feature feature
= Feature
.INDIVIDUALS_ASSOCIATION();
450 if (facade
.getType().equals(DerivedUnitType
.Specimen
)){
451 feature
= Feature
.SPECIMEN();
452 }else if (facade
.getType().equals(DerivedUnitType
.Observation
)){
453 feature
= Feature
.OBSERVATION();
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 facade
.setStoredUnder(name
);
472 isFirstDetermination
= false;
477 * This method tries to find the best matching taxon depending on the import configuration,
478 * the taxon name information and the concept information available.
482 * @param determinationLight
483 * @param createIfNotExists
486 private Taxon
findBestMatchingTaxon(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
, boolean createIfNotExists
) {
487 NonViralName
<?
> name
= makeTaxonName(state
, determinationLight
);
489 String titleCache
= makeSearchNameTitleCache(state
, determinationLight
, name
);
491 if (! StringUtils
.isBlank(titleCache
)){
492 MatchingTaxonConfigurator matchConfigurator
= MatchingTaxonConfigurator
.NewInstance();
493 matchConfigurator
.setTaxonNameTitle(titleCache
);
494 matchConfigurator
.setIncludeSynonyms(false);
495 Taxon taxon
= getTaxonService().findBestMatchingTaxon(matchConfigurator
);
497 if(taxon
== null && createIfNotExists
){
498 logger
.info("creating new Taxon from TaxonName '" + titleCache
+"'");
499 UUID secUuid
= null; //TODO
500 Reference
<?
> sec
= null;
501 if (secUuid
!= null){
502 sec
= getReferenceService().find(secUuid
);
504 taxon
= Taxon
.NewInstance(name
, sec
);
505 }else if (taxon
== null){
506 String message
= "Taxon '%s' not found in line %d";
507 message
= String
.format(message
, titleCache
, state
.getCurrentLine());
508 logger
.warn(message
);
518 * @param determinationLight
522 private String
makeSearchNameTitleCache(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
,
524 String titleCache
= determinationLight
.fullName
;
525 if (! state
.getConfig().isPreferNameCache() || StringUtils
.isBlank(titleCache
) ){
526 String computedTitleCache
= name
.getTitleCache();
527 if (StringUtils
.isNotBlank(computedTitleCache
)){
528 titleCache
= computedTitleCache
;
537 * @param determinationLight
540 private NonViralName
makeTaxonName(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
) {
541 //TODO correct type by config.nc
542 NonViralName name
=NonViralName
.NewInstance(null);
543 name
.setGenusOrUninomial(determinationLight
.genus
);
544 name
.setSpecificEpithet(determinationLight
.speciesEpi
);
545 name
.setInfraSpecificEpithet(determinationLight
.infraSpeciesEpi
);
547 //FIXME bracketAuthors and teams not yet implemented!!!
548 List
<String
> authors
= new ArrayList
<String
>();
549 if (StringUtils
.isNotBlank(determinationLight
.author
)){
550 authors
.add(determinationLight
.author
);
552 TeamOrPersonBase agent
= (TeamOrPersonBase
)getOrMakeAgent(state
, authors
);
553 name
.setCombinationAuthorTeam(agent
);
555 NomenclaturalCode nc
= state
.getConfig().getNomenclaturalCode();
557 if (StringUtils
.isNotBlank(determinationLight
.rank
) ){
558 name
.setRank(Rank
.getRankByNameOrAbbreviation(determinationLight
.rank
, nc
, true));
560 } catch (UnknownCdmTypeException e
) {
561 String message
= "Rank not found: %s: ";
562 message
= String
.format(message
, determinationLight
.rank
);
563 logger
.warn(message
);
565 if (StringUtils
.isBlank(name
.getInfraSpecificEpithet()) && StringUtils
.isNotBlank(name
.getSpecificEpithet() )){
566 name
.setRank(Rank
.SPECIES());
568 if (StringUtils
.isBlank(name
.getSpecificEpithet()) && StringUtils
.isNotBlank(name
.getGenusOrUninomial() )){
569 name
.setRank(Rank
.SPECIES());
571 if (StringUtils
.isBlank(name
.getTitleCache())){
573 name
.setTitleCache(determinationLight
.fullName
, true);
578 private TaxonNameBase
findBestMatchingName(SpecimenCdmExcelImportState state
, DeterminationLight determinationLight
) {
580 NonViralName name
= makeTaxonName(state
, determinationLight
);
581 String titleCache
= makeSearchNameTitleCache(state
, determinationLight
, name
);
584 List
<TaxonNameBase
> matchingNames
= getNameService().findByName(null, titleCache
, MatchMode
.EXACT
, null, null, null, null, null).getRecords();
585 if (matchingNames
.size() > 0){
586 return matchingNames
.get(0);
587 } else if (matchingNames
.size() > 0){
588 logger
.warn("Get best matching taxon name not yet fully implemeted for specimen import");
589 return matchingNames
.get(0);
597 private DeterminationEvent
makeDeterminationEvent(SpecimenCdmExcelImportState state
, DeterminationLight determination
, Taxon taxon
) {
598 DeterminationEvent event
= DeterminationEvent
.NewInstance();
600 event
.setTaxon(taxon
);
603 TimePeriod date
= TimePeriod
.parseString(determination
.determinedWhen
);
604 event
.setTimeperiod(date
);
606 //FIXME bracketAuthors and teams not yet implemented!!!
607 List
<String
> authors
= new ArrayList
<String
>();
608 if (StringUtils
.isNotBlank(determination
.determinedBy
)){
609 authors
.add(determination
.determinedBy
);
611 AgentBase actor
= getOrMakeAgent(state
, authors
);
612 event
.setActor(actor
);
615 if (StringUtils
.isNotBlank(determination
.modifier
)){
616 logger
.warn("DeterminationModifiers not yet implemented for specimen import");
618 // DeterminationModifier modifier = DeterminationModifier.NewInstance(term, label, labelAbbrev);
619 // determination.modifier;
621 Annotation annotation
= Annotation
.NewInstance(determination
.notes
, AnnotationType
.EDITORIAL(), Language
.DEFAULT());
622 event
.addAnnotation(annotation
);
627 private TaxonDescription
getTaxonDescription(Taxon taxon
) {
628 TaxonDescription desc
= this.getTaxonDescription(taxon
, ! IMAGE_GALLERY
, CREATE
);
632 private AgentBase
<?
> getOrMakeAgent(SpecimenCdmExcelImportState state
, List
<String
> agents
) {
633 if (agents
.size() == 0){
635 }else if (agents
.size() == 1){
636 return getOrMakePerson(state
, agents
.get(0));
638 return getOrMakeTeam(state
, agents
);
642 private Team
getOrMakeTeam(SpecimenCdmExcelImportState state
, List
<String
> agents
) {
643 String key
= CdmUtils
.concat("_", agents
.toArray(new String
[0]));
645 Team result
= state
.getTeam(key
);
647 result
= Team
.NewInstance();
648 for (String member
: agents
){
649 Person person
= getOrMakePerson(state
, member
);
650 result
.addTeamMember(person
);
652 state
.putTeam(key
, result
);
657 private Person
getOrMakePerson(SpecimenCdmExcelImportState state
, String value
) {
658 Person result
= state
.getPerson(value
);
660 result
= Person
.NewInstance();
661 result
.setTitleCache(value
, true);
662 state
.putPerson(value
, result
);
667 private Reference
<?
> getOrMakeReference(SpecimenCdmExcelImportState state
, String value
) {
668 Reference
<?
> result
= state
.getReference(value
);
670 result
= ReferenceFactory
.newGeneric();
671 result
.setTitleCache(value
, true);
672 state
.putReference(value
, result
);
679 private Collection
getOrMakeCollection(SpecimenCdmExcelImportState state
, String collectionCode
, String collectionString
) {
680 Collection result
= state
.getCollection(collectionCode
);
682 result
= Collection
.NewInstance();
683 result
.setCode(collectionCode
);
684 result
.setName(collectionString
);
685 state
.putCollection(collectionCode
, result
);
691 private TaxonNameBase
<?
, ?
> getTaxonName(SpecimenCdmExcelImportState state
, String name
) {
692 TaxonNameBase
<?
,?
> result
= null;
693 result
= state
.getName(name
);
697 List
<TaxonNameBase
<?
,?
>> list
= getNameService().findNamesByTitle(name
);
698 //TODO better strategy to find best name, e.g. depending on the classification it is used in
699 if (! list
.isEmpty()){
700 result
= list
.get(0);
703 NonViralNameParserImpl parser
= NonViralNameParserImpl
.NewInstance();
704 NomenclaturalCode code
= state
.getConfig().getNomenclaturalCode();
705 result
= parser
.parseFullName(name
, code
, null);
709 state
.putName(name
, result
);
714 private SpecimenTypeDesignationStatus
getSpecimenTypeStatus(SpecimenCdmExcelImportState state
, String key
) {
715 SpecimenTypeDesignationStatus result
= null;
717 result
= state
.getTransformer().getSpecimenTypeDesignationStatusByKey(key
);
719 String message
= "Type status not recognized for %s in line %d";
720 message
= String
.format(message
, key
, state
.getCurrentLine());
721 logger
.warn(message
);
724 } catch (UndefinedTransformerMethodException e
) {
725 throw new RuntimeException("getSpecimenTypeDesignationStatusByKey not yet implemented");
732 private void handleExactLocation(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
735 ReferenceSystem refSys
= null;
736 if (StringUtils
.isNotBlank(row
.getReferenceSystem())){
737 String strRefSys
= row
.getReferenceSystem().trim().replaceAll("\\s", "");
740 refSys
= state
.getTransformer().getReferenceSystemByKey(strRefSys
);
742 refUuid
= state
.getTransformer().getReferenceSystemUuid(strRefSys
);
743 if (refUuid
== null){
744 String message
= "Unknown reference system %s in line %d";
745 message
= String
.format(message
, strRefSys
, state
.getCurrentLine());
746 logger
.warn(message
);
748 refSys
= getReferenceSystem(state
, refUuid
, strRefSys
, strRefSys
, strRefSys
, null);
751 } catch (UndefinedTransformerMethodException e
) {
752 throw new RuntimeException(e
);
760 String longitude
= row
.getLongitude();
761 String latitude
= row
.getLatitude();
762 Integer errorRadius
= null;
763 if (StringUtils
.isNotBlank(row
.getErrorRadius())){
765 errorRadius
= Integer
.valueOf(row
.getErrorRadius());
766 } catch (NumberFormatException e
) {
767 String message
= "Error radius %s could not be transformed to Integer in line %d";
768 message
= String
.format(message
, row
.getErrorRadius(), state
.getCurrentLine());
769 logger
.warn(message
);
773 facade
.setExactLocationByParsing(longitude
, latitude
, refSys
, errorRadius
);
774 } catch (ParseException e
) {
775 String message
= "Problems when parsing exact location for line %d";
776 message
= String
.format(message
, state
.getCurrentLine());
777 logger
.warn(message
);
787 * Set the current Country
788 * Search in the DB if the isoCode is known
789 * If not, search if the country name is in the DB
790 * If not, create a new Label with the Level Country
791 * @param iso: the country iso code
792 * @param fullName: the country's full name
793 * @param app: the CDM application controller
795 private void handleCountry(DerivedUnitFacade facade
, SpecimenRow row
, SpecimenCdmExcelImportState state
) {
797 if (StringUtils
.isNotBlank(row
.getIsoCountry())){
798 NamedArea country
= getOccurrenceService().getCountryByIso(row
.getIsoCountry());
799 if (country
!= null){
800 facade
.setCountry(country
);
804 if (StringUtils
.isNotBlank(row
.getCountry())){
805 List
<WaterbodyOrCountry
> countries
= getOccurrenceService().getWaterbodyOrCountryByName(row
.getCountry());
806 if (countries
.size() >0){
807 facade
.setCountry(countries
.get(0));
809 UUID uuid
= UUID
.randomUUID();
810 String label
= row
.getCountry();
811 String text
= row
.getCountry();
812 String labelAbbrev
= null;
813 NamedAreaType areaType
= NamedAreaType
.ADMINISTRATION_AREA();
814 NamedAreaLevel level
= NamedAreaLevel
.COUNTRY();
815 NamedArea newCountry
= this.getNamedArea(state
, uuid
, label
, text
, labelAbbrev
, areaType
, level
);
816 facade
.setCountry(newCountry
);
824 protected boolean isInteger(String value
){
826 Integer
.valueOf(value
);
828 } catch (NumberFormatException e
) {
834 protected boolean secondPass(SpecimenCdmExcelImportState state
) {
835 //no second path defined yet
841 protected String
getWorksheetName() {
842 return WORKSHEET_NAME
;
846 protected boolean needsNomenclaturalCode() {
852 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
855 protected boolean doCheck(SpecimenCdmExcelImportState state
) {
856 logger
.warn("Validation not yet implemented for " + this.getClass().getSimpleName());
863 protected boolean isIgnore(SpecimenCdmExcelImportState state
) {
864 return !state
.getConfig().isDoSpecimen();