minor
[cdmlib.git] / cdmlib-io / src / main / java / eu / etaxonomy / cdm / io / specimen / excel / in / SpecimenCdmExcelImport.java
index 08c01479ca4f3e18be0b6fd4c6b5061fcd616872..efdb083d4a6900e472f1e653e84317803c748315 100644 (file)
@@ -10,9 +10,8 @@
 package eu.etaxonomy.cdm.io.specimen.excel.in;
 
 import java.text.ParseException;
-import java.util.HashMap;
+import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
 import java.util.UUID;
 
 import org.apache.commons.lang.StringUtils;
@@ -21,15 +20,47 @@ import org.springframework.stereotype.Component;
 
 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade.DerivedUnitType;
+import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
 import eu.etaxonomy.cdm.common.CdmUtils;
 import eu.etaxonomy.cdm.io.common.ICdmIO;
-import eu.etaxonomy.cdm.io.excel.common.ExcelImporterBase;
+import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
+import eu.etaxonomy.cdm.io.excel.common.ExcelRowBase.PostfixTerm;
+import eu.etaxonomy.cdm.io.excel.common.ExcelTaxonOrSpecimenImportBase;
+import eu.etaxonomy.cdm.io.specimen.excel.in.SpecimenRow.DeterminationLight;
+import eu.etaxonomy.cdm.model.agent.AgentBase;
+import eu.etaxonomy.cdm.model.agent.Person;
+import eu.etaxonomy.cdm.model.agent.Team;
+import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
+import eu.etaxonomy.cdm.model.common.Annotation;
+import eu.etaxonomy.cdm.model.common.AnnotationType;
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.common.IdentifiableSource;
+import eu.etaxonomy.cdm.model.common.Language;
+import eu.etaxonomy.cdm.model.common.TimePeriod;
+import eu.etaxonomy.cdm.model.description.Feature;
+import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
+import eu.etaxonomy.cdm.model.description.TaxonDescription;
 import eu.etaxonomy.cdm.model.location.NamedArea;
 import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
 import eu.etaxonomy.cdm.model.location.NamedAreaType;
 import eu.etaxonomy.cdm.model.location.ReferenceSystem;
 import eu.etaxonomy.cdm.model.location.WaterbodyOrCountry;
+import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
+import eu.etaxonomy.cdm.model.name.NonViralName;
+import eu.etaxonomy.cdm.model.name.Rank;
+import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
+import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
+import eu.etaxonomy.cdm.model.name.TaxonNameBase;
+import eu.etaxonomy.cdm.model.occurrence.Collection;
+import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;
+import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
 import eu.etaxonomy.cdm.model.reference.Reference;
+import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
+import eu.etaxonomy.cdm.model.taxon.Taxon;
+import eu.etaxonomy.cdm.model.taxon.TaxonBase;
+import eu.etaxonomy.cdm.persistence.query.MatchMode;
+import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
+import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
 
 /**
  * @author a.mueller
@@ -37,149 +68,194 @@ import eu.etaxonomy.cdm.model.reference.Reference;
  * @version 1.0
  */
 @Component
-public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelImportState>  implements ICdmIO<SpecimenCdmExcelImportState> {
+public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<SpecimenCdmExcelImportState, SpecimenRow>  implements ICdmIO<SpecimenCdmExcelImportState> {
        private static final Logger logger = Logger.getLogger(SpecimenCdmExcelImport.class);
 
        private static final String WORKSHEET_NAME = "Specimen";
 
-       private static final String UUID_COLUMN = "UUID";
-       private static final String BASIS_OF_RECORD_COLUMN = "BasisOfRecord";
-       private static final String COUNTRY_COLUMN = "Country";
-       private static final String ISO_COUNTRY_COLUMN = "ISOCountry";
-       private static final String LOCALITY_COLUMN = "Locality";
-       private static final String ABSOLUTE_ELEVATION_COLUMN = "AbsoluteElevation";
-       private static final String COLLECTION_DATE_COLUMN = "CollectionDate";
-       private static final String COLLECTION_DATE_END_COLUMN = "CollectionDateEnd";
-       private static final String COLLECTOR_COLUMN = "Collector";
-       private static final String LONGITUDE_COLUMN = "Longitude";
-       private static final String LATITUDE_COLUMN = "Latitude";
-       private static final String REFERENCE_SYSTEM_COLUMN = "ReferenceSystem";
-       private static final String ERROR_RADIUS_COLUMN = "ErrorRadius";
+       private static final String BASIS_OF_RECORD_COLUMN = "(?i)(BasisOfRecord)";
+       private static final String COUNTRY_COLUMN = "(?i)(Country)";
+       private static final String AREA_COLUMN = "(?i)(Area)";
+       private static final String ISO_COUNTRY_COLUMN = "(?i)(ISOCountry|CountryCode)";
+       private static final String LOCALITY_COLUMN = "(?i)(Locality)";
+       private static final String ALTITUDE_COLUMN = "(?i)(AbsoluteElevation|Altitude)";
+       private static final String ALTITUDE_MAX_COLUMN = "(?i)(AbsoluteElevation|Altitude)Max(imum)?";
+       private static final String COLLECTION_DATE_COLUMN = "(?i)(CollectionDate)";
+       private static final String COLLECTION_DATE_END_COLUMN = "(?i)(CollectionDateEnd)";
+       private static final String COLLECTOR_COLUMN = "(?i)(Collector)";
+       private static final String COLLECTORS_COLUMN = "(?i)(Collectors)";
+       private static final String PRIMARY_COLLECTOR_COLUMN = "(?i)(PrimaryCollector)";
+       private static final String LONGITUDE_COLUMN = "(?i)(Longitude)";
+       private static final String LATITUDE_COLUMN = "(?i)(Latitude)";
+       private static final String REFERENCE_SYSTEM_COLUMN = "(?i)(ReferenceSystem)";
+       private static final String ERROR_RADIUS_COLUMN = "(?i)(ErrorRadius)";
        
        
-       private static final String COLLECTORS_NUMBER_COLUMN = "CollectorsNumber";
-       private static final String ECOLOGY_COLUMN = "Ecology";
-       private static final String PLANT_DESCRIPTION_COLUMN = "PlantDescription";
-       private static final String FIELD_NOTES_COLUMN = "FieldNotes";
-       private static final String SEX_COLUMN = "Sex";
+       private static final String COLLECTORS_NUMBER_COLUMN = "(?i)((Collectors|Field)Number)";
+       private static final String ECOLOGY_COLUMN = "(?i)(Ecology|Habitat)";
+       private static final String PLANT_DESCRIPTION_COLUMN = "(?i)(PlantDescription)";
+       private static final String FIELD_NOTES_COLUMN = "(?i)(FieldNotes)";
+       private static final String SEX_COLUMN = "(?i)(Sex)";
        
        
-       private static final String ACCESSION_NUMBER_COLUMN = "AccessionNumber";
-       private static final String BARCODE_COLUMN = "Barcode";
-       private static final String COLLECTION_CODE_COLUMN = "CollectionCode";
+       private static final String ACCESSION_NUMBER_COLUMN = "(?i)(AccessionNumber)";
+       private static final String BARCODE_COLUMN = "(?i)(Barcode)";
+       private static final String COLLECTION_CODE_COLUMN = "(?i)(CollectionCode)";
+       private static final String COLLECTION_COLUMN = "(?i)(Collection)";
+       private static final String UNIT_NOTES_COLUMN = "(?i)((Unit)?Notes)";
        
-       private static final String SPECIFIC_EPITHET_COLUMN = "SpecificEpithet";
-       private static final String FAMILY_COLUMN = "Family";
-       private static final String GENUS_COLUMN = "Genus";
-       private static final String AUTHOR_COLUMN = "Author";
        
-
+       private static final String TYPE_CATEGORY_COLUMN = "(?i)(TypeCategory)";
+       private static final String TYPIFIED_NAME_COLUMN = "(?i)(TypifiedName|TypeOf)";
+       
+       
+       private static final String SOURCE_COLUMN = "(?i)(Source)";
+       private static final String ID_IN_SOURCE_COLUMN = "(?i)(IdInSource)";
+       
+       
+       private static final String DETERMINATION_AUTHOR_COLUMN = "(?i)(Author)";
+       private static final String DETERMINATION_MODIFIER_COLUMN = "(?i)(DeterminationModifier)";
+       private static final String DETERMINED_BY_COLUMN = "(?i)(DeterminationBy)";
+       private static final String DETERMINED_WHEN_COLUMN = "(?i)(DeterminationWhen)";
+       private static final String DETERMINATION_NOTES_COLUMN = "(?i)(DeterminationNote)";
+       private static final String EXTENSION_COLUMN = "(?i)(Ext(ension)?)";
+       
 
        public SpecimenCdmExcelImport() {
                super();
        }
+
        
+
+
        @Override
-       protected boolean analyzeRecord(HashMap<String, String> record, SpecimenCdmExcelImportState state) {
-               boolean success = true;
-       Set<String> keys = record.keySet();
-       
-       SpecimenRow row = new SpecimenRow();
-       state.setSpecimenRow(row);
-       
-       for (String originalKey: keys) {
-               String indexedKey = CdmUtils.removeDuplicateWhitespace(originalKey.trim()).toString();
-               String[] split = indexedKey.split("_");
-               String key = split[0];
-               if (split.length > 1){
-                       String indexString = split[1];
-                       try {
-                               Integer.valueOf(indexString);
-                               } catch (NumberFormatException e) {
-                                       String message = "Index must be integer";
-                                       logger.error(message);
-                                       continue;
-                               }
-               }
-               
-               String value = (String) record.get(indexedKey);
-               if (! StringUtils.isBlank(value)) {
-                       if (logger.isDebugEnabled()) { logger.debug(key + ": " + value); }
-                       value = CdmUtils.removeDuplicateWhitespace(value.trim()).toString();
-               }else{
-                       continue;
-               }
-               
-               
-               if (key.equalsIgnoreCase(UUID_COLUMN)) {
-                       row.setUuid(UUID.fromString(value)); //VALIDATE UUID
-                       } else if(key.equalsIgnoreCase(BASIS_OF_RECORD_COLUMN)) {
-                               row.setBasisOfRecord(value);
-                       } else if(key.equalsIgnoreCase(COUNTRY_COLUMN)) {
-                               row.setCountry(value);
-                       } else if(key.equalsIgnoreCase(ISO_COUNTRY_COLUMN)) {
-                               row.setIsoCountry(value);
-                       } else if(key.equalsIgnoreCase(LOCALITY_COLUMN)) {
-                               row.setLocality(value);
-                       } else if(key.equalsIgnoreCase(FIELD_NOTES_COLUMN)) {
-                               row.setLocality(value);
-                       } else if(key.equalsIgnoreCase(ABSOLUTE_ELEVATION_COLUMN)) {
-                               row.setAbsoluteElevation(value);                
-                       } else if(key.equalsIgnoreCase(COLLECTOR_COLUMN)) {
-                               row.setCollector(value);                
-                       } else if(key.equalsIgnoreCase(ECOLOGY_COLUMN)) {
-                               row.setEcology(value);
-                       } else if(key.equalsIgnoreCase(PLANT_DESCRIPTION_COLUMN)) {
-                               row.setCollector(value);                
-                       } else if(key.equalsIgnoreCase(SEX_COLUMN)) {
-                               row.setSex(value);
-                       } else if(key.equalsIgnoreCase(COLLECTION_DATE_COLUMN)) {
-                               row.setCollectingDate(value);           
-                       } else if(key.equalsIgnoreCase(COLLECTION_DATE_END_COLUMN)) {
-                               row.setCollectingDateEnd(value);                
-                       } else if(key.equalsIgnoreCase(COLLECTORS_NUMBER_COLUMN)) {
-                               row.setCollectorsNumber(value);         
-                       } else if(key.equalsIgnoreCase(LONGITUDE_COLUMN)) {
-                               row.setLongitude(value);                
-                       } else if(key.equalsIgnoreCase(LATITUDE_COLUMN)) {
-                               row.setLatitude(value);         
-                       } else if(key.equalsIgnoreCase(REFERENCE_SYSTEM_COLUMN)) {
-                               row.setReferenceSystem(value);          
-                       } else if(key.equalsIgnoreCase(ERROR_RADIUS_COLUMN)) {
-                               row.setErrorRadius(value);              
+       protected void analyzeSingleValue(KeyValue keyValue, SpecimenCdmExcelImportState state) {
+               SpecimenRow row = state.getCurrentRow();
+               String value = keyValue.value;
+               if(keyValue.key.matches(BASIS_OF_RECORD_COLUMN)) {
+                       row.setBasisOfRecord(value);
+               } else if(keyValue.key.matches(COUNTRY_COLUMN)) {
+                       row.setCountry(value);
+               } else if(keyValue.key.matches(ISO_COUNTRY_COLUMN)) {
+                       row.setIsoCountry(value);
+               } else if(keyValue.key.matches(LOCALITY_COLUMN)) {
+                       row.setLocality(value);
+               } else if(keyValue.key.matches(FIELD_NOTES_COLUMN)) {
+                       row.setLocality(value);
+               } else if(keyValue.key.matches(ALTITUDE_COLUMN)) {
+                       row.setAltitude(value);         
+               } else if(keyValue.key.matches(ALTITUDE_MAX_COLUMN)) {
+                       row.setAltitudeMax(value);              
+               } else if(keyValue.key.matches(COLLECTOR_COLUMN)) {
+                       row.putCollector(keyValue.index, value);                
+               } else if(keyValue.key.matches(PRIMARY_COLLECTOR_COLUMN)) {
+                       row.setPrimaryCollector(value);         
+               } else if(keyValue.key.matches(ECOLOGY_COLUMN)) {
+                       row.setEcology(value);
+               } else if(keyValue.key.matches(PLANT_DESCRIPTION_COLUMN)) {
+                       row.setPlantDescription(value);         
+               } else if(keyValue.key.matches(SEX_COLUMN)) {
+                       row.setSex(value);
+               } else if(keyValue.key.matches(COLLECTION_DATE_COLUMN)) {
+                       row.setCollectingDate(value);           
+               } else if(keyValue.key.matches(COLLECTION_DATE_END_COLUMN)) {
+                       row.setCollectingDateEnd(value);                
+               } else if(keyValue.key.matches(COLLECTORS_COLUMN)) {
+                       row.setCollectors(value);       
+               } else if(keyValue.key.matches(COLLECTOR_COLUMN)) {
+                       row.putCollector(keyValue.index, value);        
+               } else if(keyValue.key.matches(COLLECTORS_NUMBER_COLUMN)) {
+                       row.setCollectorsNumber(value);         
+               } else if(keyValue.key.matches(LONGITUDE_COLUMN)) {
+                       row.setLongitude(value);                
+               } else if(keyValue.key.matches(LATITUDE_COLUMN)) {
+                       row.setLatitude(value);         
+               } else if(keyValue.key.matches(REFERENCE_SYSTEM_COLUMN)) {
+                       row.setReferenceSystem(value);          
+               } else if(keyValue.key.matches(ERROR_RADIUS_COLUMN)) {
+                       row.setErrorRadius(value);              
+               } else if(keyValue.key.matches(AREA_COLUMN)) {
+                       if (keyValue.postfix != null){
+                               row.addLeveledArea(keyValue.postfix, value);            
+                       }else{
+                               logger.warn("Not yet implemented");
+                       }
+               
+                       
+                       
+               } else if(keyValue.key.matches(ACCESSION_NUMBER_COLUMN)) {
+                       row.setLocality(value);         
+               } else if(keyValue.key.matches(BARCODE_COLUMN)) {
+                       row.setBarcode(value);          
+               } else if(keyValue.key.matches(UNIT_NOTES_COLUMN)) {
+                       row.putUnitNote(keyValue.index, value);         
+               
                        
-                       } else if(key.equalsIgnoreCase(ACCESSION_NUMBER_COLUMN)) {
-                               row.setLocality(value);         
-                       } else if(key.equalsIgnoreCase(BARCODE_COLUMN)) {
-                               row.setBarcode(value);          
-                       } else if(key.equalsIgnoreCase(AUTHOR_COLUMN)) {
-                               row.setAuthor(value);           
-                       } else if(key.equalsIgnoreCase(FAMILY_COLUMN)) {
-                               row.setFamily(value);           
-                       } else if(key.equalsIgnoreCase(GENUS_COLUMN)) {
-                               row.setGenus(value);            
-                       } else if(key.equalsIgnoreCase(SPECIFIC_EPITHET_COLUMN)) {
-                               row.setSpecificEpithet(value);          
-                       } else if(key.equalsIgnoreCase(COLLECTION_CODE_COLUMN)) {
-                               row.setCollectionCode(value);           
-                       } else {
-                               success = false;
-                               logger.error("Unexpected column header " + key);
+               } else if(keyValue.key.matches(FAMILY_COLUMN)) {
+                       row.putDeterminationFamily(keyValue.index, value);              
+               } else if(keyValue.key.matches(GENUS_COLUMN)) {
+                       row.putDeterminationGenus(keyValue.index, value);               
+               } else if(keyValue.key.matches(SPECIFIC_EPITHET_COLUMN)) {
+                       row.putDeterminationSpeciesEpi(keyValue.index, value);                  
+               } else if(keyValue.key.matches(INFRASPECIFIC_EPITHET_COLUMN)) {
+                       row.putDeterminationInfraSpeciesEpi(keyValue.index, value);                     
+               } else if(keyValue.key.matches(RANK_COLUMN)) {
+                       row.putDeterminationRank(keyValue.index, value);                        
+               } else if(keyValue.key.matches(TAXON_UUID_COLUMN)) {
+                       row.putDeterminationTaxonUuid(keyValue.index, value);                   
+               } else if(keyValue.key.matches(FULL_NAME_COLUMN)) {
+                       row.putDeterminationFullName(keyValue.index, value);                    
+               } else if(keyValue.key.matches(DETERMINATION_AUTHOR_COLUMN)) {
+                       row.putDeterminationAuthor(keyValue.index, value);                      
+               } else if(keyValue.key.matches(DETERMINATION_MODIFIER_COLUMN)) {
+                       row.putDeterminationDeterminationModifier(keyValue.index, value);                       
+               } else if(keyValue.key.matches(DETERMINATION_NOTES_COLUMN)) {
+                       row.putDeterminationDeterminationNotes(keyValue.index, value);                  
+               } else if(keyValue.key.matches(DETERMINED_BY_COLUMN)) {
+                       row.putDeterminationDeterminedBy(keyValue.index, value);                        
+               } else if(keyValue.key.matches(DETERMINED_WHEN_COLUMN)) {
+                       row.putDeterminationDeterminedWhen(keyValue.index, value);                      
+               
+               } else if(keyValue.key.matches(COLLECTION_CODE_COLUMN)) {
+                       row.setCollectionCode(value);           
+               } else if(keyValue.key.matches(COLLECTION_COLUMN)) {
+                       row.setCollection(value);               
+               
+               } else if(keyValue.key.matches(TYPE_CATEGORY_COLUMN)) {
+                       row.putTypeCategory(keyValue.index, getSpecimenTypeStatus(state, value));       
+               } else if(keyValue.key.matches(TYPIFIED_NAME_COLUMN)) {
+                       row.putTypifiedName(keyValue.index, getTaxonName(state, value));                
+               
+               
+               } else if(keyValue.key.matches(SOURCE_COLUMN)) {
+                       row.putSourceReference(keyValue.index, getOrMakeReference(state, value));       
+               } else if(keyValue.key.matches(ID_IN_SOURCE_COLUMN)) {
+                       row.putIdInSource(keyValue.index, value);               
+               } else if(keyValue.key.matches(EXTENSION_COLUMN)) {
+                       if (keyValue.postfix != null){
+                               row.addExtension(keyValue.postfix, value);              
+                       }else{
+                               logger.warn("Extension without postfix not yet implemented");
                        }
-       }
-       return success;
+                       
+               }else {
+                       state.setUnsuccessfull();
+                       logger.error("Unexpected column header " + keyValue.originalKey);
+               }
+
+       return;
        }
 
 
        @Override
-       protected boolean firstPass(SpecimenCdmExcelImportState state) {
-               SpecimenRow row = state.getSpecimenRow();
+       protected void firstPass(SpecimenCdmExcelImportState state) {
+               SpecimenRow row = state.getCurrentRow();
                
                //basis of record
                DerivedUnitType type = DerivedUnitType.valueOf2(row.getBasisOfRecord());
                if (type == null){
-                       String message = "%s is not a valid BasisOfRecord. 'Unknown' is used instead.";
-                       message = String.format(message, row.getBasisOfRecord());
+                       String message = "%s is not a valid BasisOfRecord. 'Unknown' is used instead in line %d.";
+                       message = String.format(message, row.getBasisOfRecord(), state.getCurrentLine());
                        logger.warn(message);
                        type = DerivedUnitType.DerivedUnit;
                }
@@ -187,6 +263,7 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                
                //country
                handleCountry(facade, row, state);
+               handleAreas(facade,row, state);
                
                facade.setGatheringPeriod(getTimePeriod(row.getCollectingDate(), row.getCollectingDateEnd()));
                facade.setLocality(row.getLocality());
@@ -196,41 +273,511 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                facade.setPlantDescription(row.getPlantDescription());
 //             facade.setSex(row.get)
                handleExactLocation(facade, row, state);
+               facade.setCollector(getOrMakeAgent(state, row.getCollectors()));
+               facade.setPrimaryCollector(getOrMakePrimaryCollector(facade, row.getPrimaryCollector(), state));
+               handleAbsoluteElevation(facade, row, state);
                
                
                //derivedUnit
                facade.setBarcode(row.getBarcode());
                facade.setAccessionNumber(row.getAccessionNumber());
-               Reference<?> source = getSource(row);
-               facade.innerDerivedUnit().addSource(row.getSourceId(), null, source, null);
+               facade.setCollection(getOrMakeCollection(state, row.getCollectionCode(), row.getCollection()));
+               for (IdentifiableSource source : row.getSources()){
+                       facade.addSource(source);
+               }
+               for (SpecimenTypeDesignation designation : row.getTypeDesignations()){
+                       //FIXME
+//                     facade.innerDerivedUnit().addSpecimenTypeDesignation(designation);
+               }
+               handleDeterminations(state, row, facade);
+               handleExtensions(facade.innerDerivedUnit(),row, state);
+               for (String note : row.getUnitNotes()){
+                       Annotation annotation = Annotation.NewInstance(note, AnnotationType.EDITORIAL(), Language.DEFAULT());
+                       facade.addAnnotation(annotation);
+               }
                
                //save
                getOccurrenceService().save(facade.innerDerivedUnit());
-               return true;
+               return;
        }
 
-
-       private void handleExactLocation(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
+       private void handleAbsoluteElevation(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
+               //altitude
+               
                try {
-                       String longitude = row.getLongitude();
-                       String latitude = row.getLatitude();
-                       ReferenceSystem refSys = null;
-                       if (StringUtils.isNotBlank(row.getReferenceSystem())){
-                               String strRefSys = row.getReferenceSystem().trim().replaceAll("\\s", "").toLowerCase();
-                               //TODO move to reference system class ??
-                               if (strRefSys.equals("wgs84")){
-                                       refSys = ReferenceSystem.WGS84();
-                               }else if (strRefSys.equals("gazetteer")){
-                                       refSys = ReferenceSystem.GAZETTEER();
-                               }else if (strRefSys.equals("googleearth")){
-                                       refSys = ReferenceSystem.GOOGLE_EARTH();
-                               }else{
-                                       String message = "Reference system %s not recognized in line %d";
-                                       message = String.format(message, strRefSys, state.getCurrentLine());
+                       String altitude = row.getAltitude();
+                       if (StringUtils.isBlank(altitude)){
+                               return;
+                       }
+//                     if (altitude.endsWith(".0")){
+//                             altitude = altitude.substring(0, altitude.length() -2);
+//                     }
+                       int value = Integer.valueOf(altitude);
+                       facade.setAbsoluteElevation(value);
+               } catch (NumberFormatException e) {
+                       String message = "Absolute elevation / altitude '%s' is not an integer number in line %d";
+                       message = String.format(message, row.getAltitude(), state.getCurrentLine());
+                       logger.warn(message);
+                       return;
+               }
+               
+               //max
+               
+               try {
+                       String max = row.getAltitudeMax();
+                       if (StringUtils.isBlank(max)){
+                               return;
+                       }
+//                     if (max.endsWith(".0")){
+//                             max = max.substring(0, max.length() -2);
+//                     }
+                       int value = Integer.valueOf(max);
+                       //TODO avoid unequal distance
+                       int min = facade.getAbsoluteElevation();
+                       if ( (value - min) % 2 == 1 ){
+                               String message = "Altitude min-max difference ist not equal. Max reduced by 1 in line %d";
+                               message = String.format(message, state.getCurrentLine());
+                               logger.warn(message);
+                               value--;
+                       }
+                       facade.setAbsoluteElevationRange(min, value);
+               } catch (NumberFormatException e) {
+                       String message = "Absolute elevation / Altitude maximum '%s' is not an integer number in line %d";
+                       message = String.format(message, row.getAltitudeMax(), state.getCurrentLine());
+                       logger.warn(message);
+                       return;
+               }catch (Exception e){
+                       String message = "Error occurred when trying to write Absolute elevation / Altitude maximum '%s' in line %d";
+                       message = String.format(message, row.getAltitudeMax(), state.getCurrentLine());
+                       logger.warn(message);
+                       return;
+                       
+               }
+               
+               
+       }
+
+       private void handleAreas(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
+               List<PostfixTerm> areas = row.getLeveledAreas();
+               
+               for (PostfixTerm lArea : areas){
+                       String description = lArea.term;
+                       String abbrev = lArea.term;
+                       NamedAreaType type = null;
+                       String key = lArea.postfix + "_" + lArea.term;
+                       UUID areaUuid = state.getArea(key);
+                       NamedAreaLevel level = state.getPostfixLevel(lArea.postfix);
+                       
+                       TermMatchMode matchMode = state.getConfig().getAreaMatchMode();
+                       NamedArea area = getNamedArea(state, areaUuid, lArea.term, description, abbrev, type, level, null, matchMode);
+                       facade.addCollectingArea(area);
+                       if (areaUuid == null){ 
+                               state.putArea(key, area.getUuid());
+                       }
+               }
+       }
+
+
+       /**
+        * @param state
+        * @param row
+        * @param facade
+        */
+       private void handleDeterminations(SpecimenCdmExcelImportState state,SpecimenRow row, DerivedUnitFacade facade) {
+               boolean isFirstDetermination = true;
+               DeterminationLight commonDetermination = row.getCommonDetermination();
+               Taxon commonTaxon = null;
+               TaxonNameBase<?,?> commonName = null;
+               
+               boolean hasCommonTaxonInfo = commonDetermination == null ? false : commonDetermination.hasTaxonInformation();
+               if (hasCommonTaxonInfo && commonDetermination != null){
+                       TaxonBase<?> taxonBase = null;
+                       if (StringUtils.isNotBlank(commonDetermination.taxonUuid)){
+                               UUID taxonUuid = UUID.fromString(commonDetermination.taxonUuid);
+                               taxonBase = getTaxonService().find(taxonUuid);
+                               if (taxonBase == null){
+                                       String message = "Taxon for uuid %s not found in line %d.";
+                                       message = String.format(message, taxonUuid.toString(), state.getCurrentLine());
                                        logger.warn(message);
                                }
+                       }else{
+                               taxonBase = findBestMatchingTaxon(state, commonDetermination, state.getConfig().isCreateTaxonIfNotExists());
+                       }
+                       commonTaxon = getAcceptedTaxon(taxonBase);
+                       if (taxonBase != null){
+                               commonName = taxonBase.getName();
+                       }
+               }
+               
+               
+               for (DeterminationLight determinationLight : row.getDetermination()){
+                       Taxon taxon;
+                       if (! hasCommonTaxonInfo){
+                               taxon = findBestMatchingTaxon(state, determinationLight, state.getConfig().isCreateTaxonIfNotExists());
+                       }else{
+                               taxon = commonTaxon;
+                       }
+                       if (taxon != null){
+                               getTaxonService().saveOrUpdate(taxon);
+                               if (state.getConfig().isMakeIndividualAssociations() && taxon != null){
+                                       IndividualsAssociation indivAssociciation = IndividualsAssociation.NewInstance();
+                                       DerivedUnitBase<?> du = facade.innerDerivedUnit();
+                                       indivAssociciation.setAssociatedSpecimenOrObservation(du);
+                                       getTaxonDescription(taxon).addElement(indivAssociciation);
+                                       Feature feature = Feature.INDIVIDUALS_ASSOCIATION();
+                                       if (facade.getType().equals(DerivedUnitType.Specimen)){
+                                               feature = Feature.SPECIMEN();
+                                       }else if (facade.getType().equals(DerivedUnitType.Observation)){
+                                               feature = Feature.OBSERVATION();
+                                       }
+                                       if (state.getConfig().isUseMaterialsExaminedForIndividualsAssociations()){
+                                               feature = Feature.MATERIALS_EXAMINED();
+                                       }
+                                       
+                                       indivAssociciation.setFeature(feature);
+                               }
+                               if (state.getConfig().isDeterminationsAreDeterminationEvent()){
+                                       DeterminationEvent detEvent = makeDeterminationEvent(state, determinationLight, taxon);
+                                       detEvent.setPreferredFlag(isFirstDetermination);
+                                       facade.addDetermination(detEvent);
+                               }
+                       }
+                       
+                       if (isFirstDetermination && state.getConfig().isFirstDeterminationIsStoredUnder()){
+                               TaxonNameBase<?,?> name;
+                               
+                               if (!hasCommonTaxonInfo){
+                                       name = findBestMatchingName(state, determinationLight);
+                               }else{
+                                       if (commonName == null){
+                                               commonName = findBestMatchingName(state, commonDetermination);
+                                       }
+                                       name = commonName;
+                               }
+                               if (name != null){
+                                       facade.setStoredUnder(name);
+                               }
+                       }
+                       isFirstDetermination = false;
+               }
+       }
+
+       /**
+        * This method tries to find the best matching taxon depending on the import configuration,
+        * the taxon name information and the concept information available.
+        * 
+        * 
+        * @param state
+        * @param determinationLight
+        * @param createIfNotExists
+        * @return
+        */
+       private Taxon findBestMatchingTaxon(SpecimenCdmExcelImportState state, DeterminationLight determinationLight, boolean createIfNotExists) {
+               NonViralName<?> name = makeTaxonName(state, determinationLight);
+               
+               String titleCache = makeSearchNameTitleCache(state, determinationLight, name);
+               
+               if (! StringUtils.isBlank(titleCache)){
+                       MatchingTaxonConfigurator matchConfigurator = MatchingTaxonConfigurator.NewInstance();
+                       matchConfigurator.setTaxonNameTitle(titleCache);
+                       matchConfigurator.setIncludeSynonyms(false);
+                       Taxon taxon = getTaxonService().findBestMatchingTaxon(matchConfigurator);
+               
+                       if(taxon == null && createIfNotExists){
+                               logger.info("creating new Taxon from TaxonName '" + titleCache+"'");
+                               UUID secUuid = null; //TODO
+                               Reference<?> sec = null;
+                               if (secUuid != null){
+                                       sec = getReferenceService().find(secUuid);
+                               }
+                               taxon = Taxon.NewInstance(name, sec);
+                       }else if (taxon == null){
+                               String message = "Taxon '%s' not found in line %d";
+                               message = String.format(message, titleCache, state.getCurrentLine());
+                               logger.warn(message);
+                       }
+                       return taxon;
+               }else {
+                       return null;
+               }
+       }
+
+       /**
+        * @param state
+        * @param determinationLight
+        * @param name
+        * @return
+        */
+       private String makeSearchNameTitleCache(SpecimenCdmExcelImportState state, DeterminationLight determinationLight, 
+                               NonViralName name) {
+               String titleCache = determinationLight.fullName;
+               if (! state.getConfig().isPreferNameCache() || StringUtils.isBlank(titleCache) ){
+                       String computedTitleCache = name.getTitleCache();
+                       if (StringUtils.isNotBlank(computedTitleCache)){
+                               titleCache = computedTitleCache;
+                       }
+                       
+               }
+               return titleCache;
+       }
+
+       /**
+        * @param state
+        * @param determinationLight
+        * @return
+        */
+       private NonViralName makeTaxonName(SpecimenCdmExcelImportState state, DeterminationLight determinationLight) {
+               //TODO correct type by config.nc
+               NonViralName name =NonViralName.NewInstance(null);
+               name.setGenusOrUninomial(determinationLight.genus);
+               name.setSpecificEpithet(determinationLight.speciesEpi);
+               name.setInfraSpecificEpithet(determinationLight.infraSpeciesEpi);
+               
+               //FIXME bracketAuthors and teams not yet implemented!!!
+               List<String> authors = new ArrayList<String>();
+               if (StringUtils.isNotBlank(determinationLight.author)){
+                       authors.add(determinationLight.author);
+               }
+               TeamOrPersonBase agent = (TeamOrPersonBase)getOrMakeAgent(state, authors);
+               name.setCombinationAuthorTeam(agent);
+               
+               NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
+               try {
+                       if (StringUtils.isNotBlank(determinationLight.rank) ){
+                               name.setRank(Rank.getRankByNameOrAbbreviation(determinationLight.rank, nc, true));
+                       }
+               } catch (UnknownCdmTypeException e) {
+                       String message = "Rank not found: %s: ";
+                       message = String.format(message, determinationLight.rank);
+                       logger.warn(message);
+               }
+               if (StringUtils.isBlank(name.getInfraSpecificEpithet()) && StringUtils.isNotBlank(name.getSpecificEpithet() )){
+                       name.setRank(Rank.SPECIES());
+               }
+               if (StringUtils.isBlank(name.getSpecificEpithet()) && StringUtils.isNotBlank(name.getGenusOrUninomial() )){
+                       name.setRank(Rank.SPECIES());
+               }
+               if (StringUtils.isBlank(name.getTitleCache())){
+                       //TODO test
+                       name.setTitleCache(determinationLight.fullName, true);
+               }
+               return name;
+       }
+
+       private TaxonNameBase findBestMatchingName(SpecimenCdmExcelImportState state, DeterminationLight determinationLight) {
+               
+               NonViralName name = makeTaxonName(state, determinationLight);
+               String titleCache = makeSearchNameTitleCache(state, determinationLight, name);
+               
+               //TODO
+               List<TaxonNameBase> matchingNames = getNameService().findByName(null, titleCache, MatchMode.EXACT, null, null, null, null, null).getRecords();
+               if (matchingNames.size() > 0){
+                       return matchingNames.get(0);
+               } else if (matchingNames.size() > 0){
+                       logger.warn("Get best matching taxon name not yet fully implemeted for specimen import");
+                       return matchingNames.get(0);
+               }else{
+                       return null;    
+               }
+               
+       }
+
+       
+       private DeterminationEvent makeDeterminationEvent(SpecimenCdmExcelImportState state, DeterminationLight determination, Taxon taxon) {
+               DeterminationEvent event = DeterminationEvent.NewInstance();
+               //taxon
+               event.setTaxon(taxon);
+               
+               //date
+               TimePeriod date = TimePeriod.parseString(determination.determinedWhen);
+               event.setTimeperiod(date);
+               //by
+               //FIXME bracketAuthors and teams not yet implemented!!!
+               List<String> authors = new ArrayList<String>();
+               if (StringUtils.isNotBlank(determination.determinedBy)){
+                       authors.add(determination.determinedBy);
+               }
+               AgentBase actor = getOrMakeAgent(state, authors);
+               event.setActor(actor);
+               
+               //TODO
+               if (StringUtils.isNotBlank(determination.modifier)){
+                       logger.warn("DeterminationModifiers not yet implemented for specimen import");
+               }
+//             DeterminationModifier modifier = DeterminationModifier.NewInstance(term, label, labelAbbrev);
+//             determination.modifier;
+               //notes
+               if (StringUtils.isNotEmpty(determination.notes)){
+                       Annotation annotation = Annotation.NewInstance(determination.notes, AnnotationType.EDITORIAL(), Language.DEFAULT());
+                       event.addAnnotation(annotation);
+               }
+
+               return event;
+       }
+
+       private TaxonDescription getTaxonDescription(Taxon taxon) {
+               TaxonDescription desc = this.getTaxonDescription(taxon, ! IMAGE_GALLERY, CREATE);
+               return desc;
+       }
+
+       private AgentBase<?> getOrMakeAgent(SpecimenCdmExcelImportState state, List<String> agents) {
+               if (agents.size() == 0){
+                       return null;
+               }else if (agents.size() == 1){
+                       return getOrMakePerson(state, agents.get(0));
+               }else{
+                       return getOrMakeTeam(state, agents);
+               }
+       }
+       
+       private Person getOrMakePrimaryCollector(DerivedUnitFacade facade, String primaryCollector, SpecimenCdmExcelImportState state) {
+               if (StringUtils.isBlank(primaryCollector)){
+                       return null;
+               }
+               AgentBase<?> collector = facade.getCollector();
+               List<Person> collectors = new ArrayList<Person>();
+               if (collector.isInstanceOf(Team.class) ){
+                       Team team = CdmBase.deproxy(collector, Team.class);
+                       collectors.addAll(team.getTeamMembers());
+               }else if (collector.isInstanceOf(Person.class)){
+                       collectors.add(CdmBase.deproxy(collector, Person.class));
+               }else{
+                       throw new IllegalStateException("Unknown subclass of agentbase: " + collector.getClass().getName() );
+               }
+               for (Person person :collectors){
+                       if (primaryCollector.equalsIgnoreCase(person.getTitleCache())){
+                               return person;
+                       }
+                       if (primaryCollector.equalsIgnoreCase(person.getNomenclaturalTitle())){
+                               return person;
+                       }
+               }
+               String message = "Primary Agent '%s' could not be determined in collector(s) in line %d";
+               message = String.format(message, primaryCollector, state.getCurrentLine());
+               logger.warn(message);
+               return null;
+       }
+
+       private Team getOrMakeTeam(SpecimenCdmExcelImportState state, List<String> agents) {
+               String key = CdmUtils.concat("_", agents.toArray(new String[0]));
+               
+               Team result = state.getTeam(key);
+               if (result == null){
+                       result = Team.NewInstance();
+                       for (String member : agents){
+                               Person person = getOrMakePerson(state, member);
+                               result.addTeamMember(person);
+                       }
+                       state.putTeam(key, result);
+               }
+               return result;
+       }
+
+       private Person getOrMakePerson(SpecimenCdmExcelImportState state, String value) {
+               Person result = state.getPerson(value);
+               if (result == null){
+                       result = Person.NewInstance();
+                       result.setTitleCache(value, true);
+                       state.putPerson(value, result);
+               }
+               return result;
+       }
+
+       private Reference<?> getOrMakeReference(SpecimenCdmExcelImportState state, String value) {
+               Reference<?> result = state.getReference(value);
+               if (result == null){
+                       result = ReferenceFactory.newGeneric();
+                       result.setTitleCache(value, true);
+                       state.putReference(value, result);
+               }
+               return result;
+       }
+
+
+
+       private Collection getOrMakeCollection(SpecimenCdmExcelImportState state, String collectionCode, String collectionString) {
+               Collection result = state.getCollection(collectionCode);
+               if (result == null){
+                       result = Collection.NewInstance();
+                       result.setCode(collectionCode);
+                       result.setName(collectionString);
+                       state.putCollection(collectionCode, result);
+               }
+               return result;
+       }
+       
+
+       private TaxonNameBase<?, ?> getTaxonName(SpecimenCdmExcelImportState state, String name) {
+               TaxonNameBase<?,?> result = null;
+               result = state.getName(name);
+               if (result != null){
+                       return result;
+               }
+               List<TaxonNameBase<?,?>> list = getNameService().findNamesByTitle(name);
+               //TODO better strategy to find best name, e.g. depending on the classification it is used in
+               if (! list.isEmpty()){
+                       result = list.get(0);
+               }
+               if (result == null){
+                       NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
+                       NomenclaturalCode code = state.getConfig().getNomenclaturalCode();
+                       result = parser.parseFullName(name, code, null);
+                       
+               }
+               if (result != null){
+                       state.putName(name, result);
+               }
+               return result;
+       }
+
+       private SpecimenTypeDesignationStatus getSpecimenTypeStatus(SpecimenCdmExcelImportState state, String key)  {
+               SpecimenTypeDesignationStatus result = null;
+               try {
+                       result = state.getTransformer().getSpecimenTypeDesignationStatusByKey(key);
+                       if (result == null){
+                               String message = "Type status not recognized for %s in line %d";
+                               message = String.format(message, key, state.getCurrentLine());
+                               logger.warn(message);
+                       }
+                       return result;
+               } catch (UndefinedTransformerMethodException e) {
+                       throw new RuntimeException("getSpecimenTypeDesignationStatusByKey not yet implemented");
+               }
+               
+               
+       }
+
+
+       private void handleExactLocation(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
+                       
+               //reference system
+               ReferenceSystem refSys = null;
+               if (StringUtils.isNotBlank(row.getReferenceSystem())){
+                       String strRefSys = row.getReferenceSystem().trim().replaceAll("\\s", "");
+                       UUID refUuid;
+                       try {
+                               refSys = state.getTransformer().getReferenceSystemByKey(strRefSys);
+                               if (refSys == null){
+                                       refUuid = state.getTransformer().getReferenceSystemUuid(strRefSys);
+                                       if (refUuid == null){
+                                               String message = "Unknown reference system %s in line %d";
+                                               message = String.format(message, strRefSys, state.getCurrentLine());
+                                               logger.warn(message);
+                                       }
+                                       refSys = getReferenceSystem(state, refUuid, strRefSys, strRefSys, strRefSys, null);
+                               }
                                
+                       } catch (UndefinedTransformerMethodException e) {
+                               throw new RuntimeException(e);
                        }
+               }
+
+               
+               
+               // lat/ long /error
+               try {
+                       String longitude = row.getLongitude();
+                       String latitude = row.getLatitude();
                        Integer errorRadius = null;
                        if (StringUtils.isNotBlank(row.getErrorRadius())){
                                try {
@@ -241,6 +788,7 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                                        logger.warn(message);
                                }
                        }
+                       //all
                        facade.setExactLocationByParsing(longitude, latitude, refSys, errorRadius);
                } catch (ParseException e) {
                        String message = "Problems when parsing exact location for line %d";
@@ -250,12 +798,9 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                }
                
                
+               
        }
 
-       //      TODO
-       private Reference<?> getSource(SpecimenRow row) {
-               return null;
-       }
 
        /*
         * Set the current Country
@@ -291,16 +836,11 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                        }
                }
        }
-               
-
-       private DerivedUnitType getDerivedUnitType(String basisOfRecord) {
-               return null;
-       }
 
        @Override
-       protected boolean secondPass(SpecimenCdmExcelImportState state) {
+       protected void secondPass(SpecimenCdmExcelImportState state) {
                //no second path defined yet
-               return true;
+               return;
        }
 
 
@@ -315,6 +855,17 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
        }
        
        
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.io.excel.common.ExcelTaxonOrSpecimenImportBase#createDataHolderRow()
+        */
+       @Override
+       protected SpecimenRow createDataHolderRow() {
+               return new SpecimenRow();
+       }
+
+
+       
+       
        /* (non-Javadoc)
         * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
         */