minor
[cdmlib.git] / cdmlib-io / src / main / java / eu / etaxonomy / cdm / io / specimen / excel / in / SpecimenCdmExcelImport.java
index c9911ffbd960b840e612141ce30e03d319b11a64..efdb083d4a6900e472f1e653e84317803c748315 100644 (file)
@@ -11,10 +11,7 @@ package eu.etaxonomy.cdm.io.specimen.excel.in;
 
 import java.text.ParseException;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 import java.util.UUID;
 
 import org.apache.commons.lang.StringUtils;
@@ -27,20 +24,20 @@ 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.common.mapping.UndefinedTransformerMethodException;
-import eu.etaxonomy.cdm.io.excel.common.ExcelImporterBase;
+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.io.specimen.excel.in.SpecimenRow.PostfixTerm;
 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.Extension;
-import eu.etaxonomy.cdm.model.common.ExtensionType;
+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;
@@ -60,6 +57,7 @@ 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;
@@ -70,60 +68,57 @@ import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
  * @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 AREA_COLUMN = "Area";
-       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 COLLECTION_COLUMN = "Collection";
+       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 TYPE_CATEGORY_COLUMN = "TypeCategory";
-       private static final String TYPIFIED_NAME_COLUMN = "TypifiedName";
        
+       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 = "Source";
-       private static final String ID_IN_SOURCE_COLUMN = "IdInSource";
        
+       private static final String SOURCE_COLUMN = "(?i)(Source)";
+       private static final String ID_IN_SOURCE_COLUMN = "(?i)(IdInSource)";
        
-       private static final String RANK_COLUMN = "Rank";
-       private static final String FULL_NAME_COLUMN = "FullName";
-       private static final String FAMILY_COLUMN = "Family";
-       private static final String GENUS_COLUMN = "Genus";
-       private static final String SPECIFIC_EPITHET_COLUMN = "SpecificEpithet";
-       private static final String INFRASPECIFIC_EPITHET_COLUMN = "InfraSpecificEpithet";
-       private static final String DETERMINATION_AUTHOR_COLUMN = "Author";
-       private static final String DETERMINATION_MODIFIER_COLUMN = "DeterminationModifier";
-       private static final String DETERMINED_BY_COLUMN = "DeterminationBy";
-       private static final String DETERMINED_WHEN_COLUMN = "DeterminationWhen";
-       private static final String DETERMINATION_NOTES_COLUMN = "DeterminationNote";
-       private static final String EXTENSION_COLUMN = "Ext(ension)?";
        
+       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() {
@@ -131,154 +126,136 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
        }
 
        
+
+
        @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) {
-               Integer index = 0;
-               String postfix = null;
-               String indexedKey = CdmUtils.removeDuplicateWhitespace(originalKey.trim()).toString();
-               String[] split = indexedKey.split("_");
-               String key = split[0];
-               if (split.length > 1){
-                       for (int i = 1 ; i < split.length ; i++ ){
-                               String indexString = split[i];
-                               if (isInteger(indexString)){
-                                       index = Integer.valueOf(indexString);
-                               }else{
-                                       postfix = split[i];
-                               }
-                       }
-               }
-               
-               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.putCollector(index, value);         
-                       } else if(key.equalsIgnoreCase(ECOLOGY_COLUMN)) {
-                               row.setEcology(value);
-                       } else if(key.equalsIgnoreCase(PLANT_DESCRIPTION_COLUMN)) {
-                               row.setPlantDescription(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(COLLECTOR_COLUMN)) {
-                               row.putCollector(index, 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);              
-                       } else if(key.equalsIgnoreCase(AREA_COLUMN)) {
-                               if (postfix != null){
-                                       row.addLeveledArea(postfix, value);             
-                               }else{
-                                       logger.warn("Not yet implemented");
-                               }
-                       
-                               
-                               
-                       } else if(key.equalsIgnoreCase(ACCESSION_NUMBER_COLUMN)) {
-                               row.setLocality(value);         
-                       } else if(key.equalsIgnoreCase(BARCODE_COLUMN)) {
-                               row.setBarcode(value);          
-                       
-                       } else if(key.equalsIgnoreCase(FAMILY_COLUMN)) {
-                               row.putDeterminationFamily(index, value);               
-                       } else if(key.equalsIgnoreCase(GENUS_COLUMN)) {
-                               row.putDeterminationGenus(index, value);                
-                       } else if(key.equalsIgnoreCase(SPECIFIC_EPITHET_COLUMN)) {
-                               row.putDeterminationSpeciesEpi(index, value);                   
-                       } else if(key.equalsIgnoreCase(INFRASPECIFIC_EPITHET_COLUMN)) {
-                               row.putDeterminationInfraSpeciesEpi(index, value);                      
-                       } else if(key.equalsIgnoreCase(RANK_COLUMN)) {
-                               row.putDeterminationRank(index, value);                 
-                       } else if(key.equalsIgnoreCase(FULL_NAME_COLUMN)) {
-                               row.putDeterminationFullName(index, value);                     
-                       } else if(key.equalsIgnoreCase(DETERMINATION_AUTHOR_COLUMN)) {
-                               row.putDeterminationAuthor(index, value);                       
-                       } else if(key.equalsIgnoreCase(DETERMINATION_MODIFIER_COLUMN)) {
-                               row.putDeterminationDeterminationModifier(index, value);                        
-                       } else if(key.equalsIgnoreCase(DETERMINATION_NOTES_COLUMN)) {
-                               row.putDeterminationDeterminationNotes(index, value);                   
-                       } else if(key.equalsIgnoreCase(DETERMINED_BY_COLUMN)) {
-                               row.putDeterminationDeterminedBy(index, value);                 
-                       } else if(key.equalsIgnoreCase(DETERMINED_WHEN_COLUMN)) {
-                               row.putDeterminationDeterminedWhen(index, value);                       
-                       
-                       } else if(key.equalsIgnoreCase(COLLECTION_CODE_COLUMN)) {
-                               row.setCollectionCode(value);           
-                       } else if(key.equalsIgnoreCase(COLLECTION_COLUMN)) {
-                               row.setCollection(value);               
-                       
-                       } else if(key.equalsIgnoreCase(TYPE_CATEGORY_COLUMN)) {
-                               row.putTypeCategory(index, getSpecimenTypeStatus(state, value));        
-                       } else if(key.equalsIgnoreCase(TYPIFIED_NAME_COLUMN)) {
-                               row.putTypifiedName(index, getTaxonName(state, 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(key.equalsIgnoreCase(SOURCE_COLUMN)) {
-                               row.putSourceReference(index, getOrMakeReference(state, value));        
-                       } else if(key.equalsIgnoreCase(ID_IN_SOURCE_COLUMN)) {
-                               row.putIdInSource(index, value);                
-                       } else if(key.matches(EXTENSION_COLUMN)) {
-                               if (postfix != null){
-                                       row.addExtensionTypes(postfix, value);          
-                               }else{
-                                       logger.warn("Extension without postfix 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 {
-                               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;
                }
@@ -297,6 +274,8 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
 //             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
@@ -311,42 +290,88 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
 //                     facade.innerDerivedUnit().addSpecimenTypeDesignation(designation);
                }
                handleDeterminations(state, row, facade);
-               handleExtensions(facade,row, state);
-               
+               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 handleExtensions(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
-               List<PostfixTerm> extensionTypes = row.getExtensionTypes();
+       private void handleAbsoluteElevation(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
+               //altitude
+               
+               try {
+                       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
                
-               for (PostfixTerm exType : extensionTypes){
-                       ExtensionType extensionType = state.getPostfixExtensionType(exType.postfix);
+               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;
                        
-                       Extension extension = Extension.NewInstance();
-                       extension.setType(extensionType);
-                       extension.setValue(exType.term);
-                       facade.innerDerivedUnit().addExtension(extension);
                }
                
+               
        }
 
-
        private void handleAreas(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
                List<PostfixTerm> areas = row.getLeveledAreas();
                
                for (PostfixTerm lArea : areas){
-                       String description = null;
-                       String abbrev = null;
+                       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);
-                       NamedArea area = getNamedArea(state, areaUuid, lArea.term, description, abbrev, type, level);
+                       
+                       TermMatchMode matchMode = state.getConfig().getAreaMatchMode();
+                       NamedArea area = getNamedArea(state, areaUuid, lArea.term, description, abbrev, type, level, null, matchMode);
                        facade.addCollectingArea(area);
-                       if (areaUuid == null){
+                       if (areaUuid == null){ 
                                state.putArea(key, area.getUuid());
                        }
                }
@@ -360,22 +385,78 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
         */
        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 = findBestMatchingTaxon(state, determinationLight, true);
-                       getTaxonService().saveOrUpdate(taxon);
-                       TaxonNameBase<?,?> name = findBestMatchingName(state, determinationLight);
-                       if (state.getConfig().isMakeIndividualAssociations() && taxon != null){
-                               IndividualsAssociation indivAssociciation = IndividualsAssociation.NewInstance();
-                               DerivedUnitBase<?> du = facade.innerDerivedUnit();
-                               indivAssociciation.setAssociatedSpecimenOrObservation(du);
-                               getTaxonDescription(taxon).addElement(indivAssociciation);
+                       Taxon taxon;
+                       if (! hasCommonTaxonInfo){
+                               taxon = findBestMatchingTaxon(state, determinationLight, state.getConfig().isCreateTaxonIfNotExists());
+                       }else{
+                               taxon = commonTaxon;
                        }
-                       if (isFirstDetermination && state.getConfig().isFirstDeterminationIsStoredUnder()){
-                               facade.setStoredUnder(name);
+                       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 (state.getConfig().isDeterminationsAreDeterminationEvent()){
-                               DeterminationEvent detEvent = makeDeterminationEvent(state, determinationLight, taxon);
-                               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;
                }
@@ -392,23 +473,28 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
         * @return
         */
        private Taxon findBestMatchingTaxon(SpecimenCdmExcelImportState state, DeterminationLight determinationLight, boolean createIfNotExists) {
-               NonViralName name = makeTaxonName(state, determinationLight);
+               NonViralName<?> name = makeTaxonName(state, determinationLight);
                
                String titleCache = makeSearchNameTitleCache(state, determinationLight, name);
                
                if (! StringUtils.isBlank(titleCache)){
                        MatchingTaxonConfigurator matchConfigurator = MatchingTaxonConfigurator.NewInstance();
-                       matchConfigurator.setTaxonNameTitle(determinationLight.fullName);
+                       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;
+                               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 {
@@ -499,6 +585,9 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
        
        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);
@@ -518,8 +607,11 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
 //             DeterminationModifier modifier = DeterminationModifier.NewInstance(term, label, labelAbbrev);
 //             determination.modifier;
                //notes
-               Annotation annotation = Annotation.NewInstance(determination.notes, AnnotationType.EDITORIAL(), Language.DEFAULT());
-               event.addAnnotation(annotation);
+               if (StringUtils.isNotEmpty(determination.notes)){
+                       Annotation annotation = Annotation.NewInstance(determination.notes, AnnotationType.EDITORIAL(), Language.DEFAULT());
+                       event.addAnnotation(annotation);
+               }
+
                return event;
        }
 
@@ -537,6 +629,34 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                        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]));
@@ -629,26 +749,35 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
 
 
        private void handleExactLocation(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
-               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());
-                                       logger.warn(message);
+                       
+               //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 {
@@ -659,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";
@@ -668,6 +798,7 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                }
                
                
+               
        }
 
 
@@ -705,23 +836,11 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                        }
                }
        }
-               
-
-       
-       
-       protected boolean isInteger(String value){
-               try {
-                       Integer.valueOf(value);
-                       return true;
-               } catch (NumberFormatException e) {
-                       return false;
-               }
-       }
 
        @Override
-       protected boolean secondPass(SpecimenCdmExcelImportState state) {
+       protected void secondPass(SpecimenCdmExcelImportState state) {
                //no second path defined yet
-               return true;
+               return;
        }
 
 
@@ -736,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)
         */