fix AccessionNumber->Locality bug in Specimen Excel Import
[cdmlib.git] / cdmlib-io / src / main / java / eu / etaxonomy / cdm / io / specimen / excel / in / SpecimenCdmExcelImport.java
index 99473213d3eebd5b894b6eb7af2f08ba6672104f..b43d33e9b8f34e162b0986e0bef67605a054044c 100644 (file)
@@ -11,35 +11,28 @@ 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.Set;
 import java.util.UUID;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
-import sun.security.util.DerEncoder;
-
 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.CdmImportBase.TermMatchMode;
 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;
@@ -50,22 +43,29 @@ 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.location.Country;
+import eu.etaxonomy.cdm.model.name.BotanicalName;
 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.name.ZoologicalName;
 import eu.etaxonomy.cdm.model.occurrence.Collection;
-import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;
+import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
 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.StringNotParsableException;
 import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
+import eu.etaxonomy.cdm.strategy.parser.INonViralNameParser;
 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
+import eu.etaxonomy.cdm.strategy.parser.TimePeriodParser;
 
 /**
  * @author a.mueller
@@ -73,12 +73,11 @@ 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 = "(?i)(UUID)";
        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)";
@@ -89,6 +88,8 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
        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)";
@@ -117,186 +118,167 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
        private static final String ID_IN_SOURCE_COLUMN = "(?i)(IdInSource)";
        
        
-       private static final String RANK_COLUMN = "(?i)(Rank)";
-       private static final String FULL_NAME_COLUMN = "(?i)(FullName)";
-       private static final String FAMILY_COLUMN = "(?i)(Family)";
-       private static final String GENUS_COLUMN = "(?i)(Genus)";
-       private static final String SPECIFIC_EPITHET_COLUMN = "(?i)(SpecificEpi(thet)?)";
-       private static final String INFRASPECIFIC_EPITHET_COLUMN = "(?i)(InfraSpecificEpi(thet)?)";
        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 DETERMINED_WHEN_COLUMN = "(?i)(Det(ermination)?When)";
        private static final String DETERMINATION_NOTES_COLUMN = "(?i)(DeterminationNote)";
        private static final String EXTENSION_COLUMN = "(?i)(Ext(ension)?)";
        
-       private static final String IGNORE_COLUMN = "(?i)(Ignore|Not)";
-       
 
        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) {
-               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.matches(UUID_COLUMN)) {
-                       row.setUuid(UUID.fromString(value)); //VALIDATE UUID
-                       } else if(key.matches(BASIS_OF_RECORD_COLUMN)) {
-                               row.setBasisOfRecord(value);
-                       } else if(key.matches(COUNTRY_COLUMN)) {
-                               row.setCountry(value);
-                       } else if(key.matches(ISO_COUNTRY_COLUMN)) {
-                               row.setIsoCountry(value);
-                       } else if(key.matches(LOCALITY_COLUMN)) {
-                               row.setLocality(value);
-                       } else if(key.matches(FIELD_NOTES_COLUMN)) {
-                               row.setLocality(value);
-                       } else if(key.matches(ALTITUDE_COLUMN)) {
-                               row.setAltitude(value);         
-                       } else if(key.matches(ALTITUDE_MAX_COLUMN)) {
-                               row.setAltitudeMax(value);              
-                       } else if(key.matches(COLLECTOR_COLUMN)) {
-                               row.putCollector(index, value);         
-                       } else if(key.matches(ECOLOGY_COLUMN)) {
-                               row.setEcology(value);
-                       } else if(key.matches(PLANT_DESCRIPTION_COLUMN)) {
-                               row.setPlantDescription(value);         
-                       } else if(key.matches(SEX_COLUMN)) {
-                               row.setSex(value);
-                       } else if(key.matches(COLLECTION_DATE_COLUMN)) {
-                               row.setCollectingDate(value);           
-                       } else if(key.matches(COLLECTION_DATE_END_COLUMN)) {
-                               row.setCollectingDateEnd(value);                
-                       } else if(key.matches(COLLECTOR_COLUMN)) {
-                               row.putCollector(index, value); 
-                       } else if(key.matches(COLLECTORS_NUMBER_COLUMN)) {
-                               row.setCollectorsNumber(value);         
-                       } else if(key.matches(LONGITUDE_COLUMN)) {
-                               row.setLongitude(value);                
-                       } else if(key.matches(LATITUDE_COLUMN)) {
-                               row.setLatitude(value);         
-                       } else if(key.matches(REFERENCE_SYSTEM_COLUMN)) {
-                               row.setReferenceSystem(value);          
-                       } else if(key.matches(ERROR_RADIUS_COLUMN)) {
-                               row.setErrorRadius(value);              
-                       } else if(key.matches(AREA_COLUMN)) {
-                               if (postfix != null){
-                                       row.addLeveledArea(postfix, value);             
-                               }else{
-                                       logger.warn("Not yet implemented");
-                               }
-                       
-                               
-                               
-                       } else if(key.matches(ACCESSION_NUMBER_COLUMN)) {
-                               row.setLocality(value);         
-                       } else if(key.matches(BARCODE_COLUMN)) {
-                               row.setBarcode(value);          
-                       } else if(key.matches(UNIT_NOTES_COLUMN)) {
-                               row.putUnitNote(index, value);          
-                       
-                               
-                       } else if(key.matches(FAMILY_COLUMN)) {
-                               row.putDeterminationFamily(index, value);               
-                       } else if(key.matches(GENUS_COLUMN)) {
-                               row.putDeterminationGenus(index, value);                
-                       } else if(key.matches(SPECIFIC_EPITHET_COLUMN)) {
-                               row.putDeterminationSpeciesEpi(index, value);                   
-                       } else if(key.matches(INFRASPECIFIC_EPITHET_COLUMN)) {
-                               row.putDeterminationInfraSpeciesEpi(index, value);                      
-                       } else if(key.matches(RANK_COLUMN)) {
-                               row.putDeterminationRank(index, value);                 
-                       } else if(key.matches(FULL_NAME_COLUMN)) {
-                               row.putDeterminationFullName(index, value);                     
-                       } else if(key.matches(DETERMINATION_AUTHOR_COLUMN)) {
-                               row.putDeterminationAuthor(index, value);                       
-                       } else if(key.matches(DETERMINATION_MODIFIER_COLUMN)) {
-                               row.putDeterminationDeterminationModifier(index, value);                        
-                       } else if(key.matches(DETERMINATION_NOTES_COLUMN)) {
-                               row.putDeterminationDeterminationNotes(index, value);                   
-                       } else if(key.matches(DETERMINED_BY_COLUMN)) {
-                               row.putDeterminationDeterminedBy(index, value);                 
-                       } else if(key.matches(DETERMINED_WHEN_COLUMN)) {
-                               row.putDeterminationDeterminedWhen(index, 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(LANGUAGE)) {
+                       row.setLanguage(value);         
                        
-                       } else if(key.matches(COLLECTION_CODE_COLUMN)) {
-                               row.setCollectionCode(value);           
-                       } else if(key.matches(COLLECTION_COLUMN)) {
-                               row.setCollection(value);               
                        
-                       } else if(key.matches(TYPE_CATEGORY_COLUMN)) {
-                               row.putTypeCategory(index, getSpecimenTypeStatus(state, value));        
-                       } else if(key.matches(TYPIFIED_NAME_COLUMN)) {
-                               row.putTypifiedName(index, getTaxonName(state, value));         
+               } else if(keyValue.key.matches(ACCESSION_NUMBER_COLUMN)) {
+                       row.setAccessionNumber(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(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");
+                       }
                        
-                       } else if(key.matches(SOURCE_COLUMN)) {
-                               row.putSourceReference(index, getOrMakeReference(state, value));        
-                       } else if(key.matches(ID_IN_SOURCE_COLUMN)) {
-                               row.putIdInSource(index, value);                
-                       } else if(key.matches(EXTENSION_COLUMN)) {
-                               if (postfix != null){
-                                       row.addExtension(postfix, value);               
-                               }else{
-                                       logger.warn("Extension without postfix not yet implemented");
-                               }
-                               
-                       } else if(key.matches(IGNORE_COLUMN)) {
-                               logger.debug("Ignored column" + originalKey);           
-                       }else {
-                               success = false;
-                               logger.error("Unexpected column header " + originalKey);
-                       }
-       }
-       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());
+               SpecimenOrObservationType type = SpecimenOrObservationType.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;
+                       type = SpecimenOrObservationType.DerivedUnit;
                }
                DerivedUnitFacade facade = DerivedUnitFacade.NewInstance(type);
                
+               
+               Language lang = Language.DEFAULT();
+               if (StringUtils.isNotBlank(row.getLanguage())){
+                       Language langIso = getTermService().getLanguageByIso(row.getLanguage());
+                       if (langIso == null){
+                               String message = "Language could not be recognized: %s. Use default language instead. Line %d.";
+                               message = String.format(message, langIso, state.getCurrentLine());
+                       }else{
+                               lang = langIso;
+                       }
+               }
+               
                //country
                handleCountry(facade, row, state);
                handleAreas(facade,row, state);
@@ -305,14 +287,14 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                facade.setLocality(row.getLocality());
                facade.setFieldNotes(row.getFieldNotes());
                facade.setFieldNumber(row.getCollectorsNumber());
-               facade.setEcology(row.getEcology());
-               facade.setPlantDescription(row.getPlantDescription());
+               facade.setEcology(row.getEcology(), lang);
+               facade.setPlantDescription(row.getPlantDescription(), lang);
 //             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());
@@ -321,11 +303,11 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                        facade.addSource(source);
                }
                for (SpecimenTypeDesignation designation : row.getTypeDesignations()){
-                       //FIXME
+                       logger.warn("FIXME"); //FIXME
 //                     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);
@@ -333,7 +315,7 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                
                //save
                getOccurrenceService().save(facade.innerDerivedUnit());
-               return true;
+               return;
        }
 
        private void handleAbsoluteElevation(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
@@ -350,7 +332,7 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                        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";
+                       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;
@@ -392,22 +374,6 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                
        }
 
-
-       private void handleExtensions(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
-               List<PostfixTerm> extensions = row.getExtensions();
-               
-               for (PostfixTerm exType : extensions){
-                       ExtensionType extensionType = state.getPostfixExtensionType(exType.postfix);
-                       
-                       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();
                
@@ -436,20 +402,52 @@ 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();
+                       }else{
+                               commonTaxon = createTaxonFromDetermination(state, commonDetermination);
+                               commonName = commonTaxon.getName();
+                       }
+               }
+               
+               
                for (DeterminationLight determinationLight : row.getDetermination()){
-                       Taxon taxon = findBestMatchingTaxon(state, determinationLight, state.getConfig().isCreateTaxonIfNotExists());
-                       TaxonNameBase<?,?> name = findBestMatchingName(state, determinationLight);
+                       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();
+                                       DerivedUnit du = facade.innerDerivedUnit();
                                        indivAssociciation.setAssociatedSpecimenOrObservation(du);
                                        getTaxonDescription(taxon).addElement(indivAssociciation);
                                        Feature feature = Feature.INDIVIDUALS_ASSOCIATION();
-                                       if (facade.getType().equals(DerivedUnitType.Specimen)){
+                                       if (facade.getType().isPreservedSpecimen()){
                                                feature = Feature.SPECIMEN();
-                                       }else if (facade.getType().equals(DerivedUnitType.Observation)){
+                                       }else if (facade.getType().isFeatureObservation()){
                                                feature = Feature.OBSERVATION();
                                        }
                                        if (state.getConfig().isUseMaterialsExaminedForIndividualsAssociations()){
@@ -464,8 +462,19 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                                        facade.addDetermination(detEvent);
                                }
                        }
-                       if (name != null){
-                               if (isFirstDetermination && state.getConfig().isFirstDeterminationIsStoredUnder()){
+                       
+                       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);
                                }
                        }
@@ -473,6 +482,104 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                }
        }
 
+       private Taxon createTaxonFromDetermination( SpecimenCdmExcelImportState state, DeterminationLight commonDetermination) {
+               
+               //rank
+               Rank rank;
+               try {
+                       rank = StringUtils.isBlank(commonDetermination.rank) ? null : Rank.getRankByNameOrIdInVoc(commonDetermination.rank, true);
+               } catch (UnknownCdmTypeException e) {
+                       rank = null;
+               }
+
+               //name
+               NonViralName<?> name;
+               INonViralNameParser<NonViralName> parser = NonViralNameParserImpl.NewInstance();
+               NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
+               if (StringUtils.isNotBlank(commonDetermination.fullName)){
+                       name = parser.parseFullName(commonDetermination.fullName, nc, rank);
+                       if (StringUtils.isBlank(name.getAuthorshipCache()) && StringUtils.isNotBlank(commonDetermination.author)){
+                               setAuthorship(name, commonDetermination.author, parser);
+                       }
+               }else{
+                       if (nc != null){
+                               name = (NonViralName)nc.getNewTaxonNameInstance(rank);
+                       }else{
+                               name = NonViralName.NewInstance(rank);
+                       }
+                       if (StringUtils.isNotBlank(commonDetermination.genus)){
+                               name.setGenusOrUninomial(commonDetermination.genus);
+                       }
+                       if (StringUtils.isNotBlank(commonDetermination.speciesEpi)){
+                               name.setSpecificEpithet(commonDetermination.speciesEpi);
+                       }
+                       if (StringUtils.isNotBlank(commonDetermination.infraSpeciesEpi)){
+                               name.setInfraSpecificEpithet(commonDetermination.infraSpeciesEpi);
+                       }
+                       if (StringUtils.isNotBlank(commonDetermination.author)){
+                               setAuthorship(name, commonDetermination.author, parser);
+                       }
+                       //guess rank if null
+                       if (name.getRank() == null){
+                               if (name.getInfraGenericEpithet() != null && name.getSpecificEpithet() == null){
+                                       name.setRank(Rank.INFRAGENERICTAXON());
+                               }else if (name.getSpecificEpithet() != null && name.getInfraSpecificEpithet() == null){
+                                       name.setRank(Rank.SPECIES());
+                               }else if (name.getInfraSpecificEpithet() != null){
+                                       name.setRank(Rank.INFRASPECIFICTAXON());
+                               }
+                               
+                       }
+                       
+               }
+               //sec
+               Reference<?> sec = null;
+               if (StringUtils.isNotBlank(commonDetermination.determinedBy)){
+                       sec = ReferenceFactory.newGeneric();
+                       TeamOrPersonBase<?> determinedBy;
+                       BotanicalName dummyName = BotanicalName.NewInstance(Rank.SPECIES());
+                       try {
+                               parser.parseAuthors(dummyName, commonDetermination.determinedBy);
+                               determinedBy = (TeamOrPersonBase<?>)dummyName.getCombinationAuthorTeam();
+                       } catch (StringNotParsableException e) {
+                               determinedBy = Team.NewTitledInstance(commonDetermination.determinedBy, commonDetermination.determinedBy);
+                       }
+                       sec.setAuthorTeam(determinedBy);
+               }
+               
+               //taxon
+               Taxon taxon = Taxon.NewInstance(name, sec);
+
+               if (StringUtils.isNotBlank(commonDetermination.family)){
+                       if (name.getRank() == null || name.getRank().isLower(Rank.FAMILY()) ){
+                               logger.warn("Family taxon could not be created");
+                       }
+               }
+               
+               //return
+               return taxon;
+               
+       }
+
+
+
+
+       private void setAuthorship(NonViralName<?> name, String author, INonViralNameParser<NonViralName> parser) {
+               if (name.isInstanceOf(BotanicalName.class) || name.isInstanceOf(ZoologicalName.class)){
+                       try {
+                               parser.parseAuthors(name, author);
+                       } catch (StringNotParsableException e) {
+                               name.setAuthorshipCache(author);
+                       }               
+               }else{
+                       name.setAuthorshipCache(author);
+               }
+               
+       }
+
+
+
+
        /**
         * This method tries to find the best matching taxon depending on the import configuration,
         * the taxon name information and the concept information available.
@@ -520,7 +627,7 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
         * @return
         */
        private String makeSearchNameTitleCache(SpecimenCdmExcelImportState state, DeterminationLight determinationLight, 
-                               NonViralName name) {
+                               NonViralName<?> name) {
                String titleCache = determinationLight.fullName;
                if (! state.getConfig().isPreferNameCache() || StringUtils.isBlank(titleCache) ){
                        String computedTitleCache = name.getTitleCache();
@@ -537,9 +644,12 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
         * @param determinationLight
         * @return
         */
-       private NonViralName makeTaxonName(SpecimenCdmExcelImportState state, DeterminationLight determinationLight) {
-               //TODO correct type by config.nc
-               NonViralName name =NonViralName.NewInstance(null);
+       private NonViralName<?> makeTaxonName(SpecimenCdmExcelImportState state, DeterminationLight determinationLight) {
+               NonViralName<?> name = NonViralName.NewInstance(null);
+               NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
+               if (nc != null){
+                       name = (NonViralName<?>)nc.getNewTaxonNameInstance(null);
+               }
                name.setGenusOrUninomial(determinationLight.genus);
                name.setSpecificEpithet(determinationLight.speciesEpi);
                name.setInfraSpecificEpithet(determinationLight.infraSpeciesEpi);
@@ -549,13 +659,12 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                if (StringUtils.isNotBlank(determinationLight.author)){
                        authors.add(determinationLight.author);
                }
-               TeamOrPersonBase agent = (TeamOrPersonBase)getOrMakeAgent(state, authors);
+               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));
+                               name.setRank(Rank.getRankByNameOrIdInVoc(determinationLight.rank, nc, true));
                        }
                } catch (UnknownCdmTypeException e) {
                        String message = "Rank not found: %s: ";
@@ -577,7 +686,7 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
 
        private TaxonNameBase findBestMatchingName(SpecimenCdmExcelImportState state, DeterminationLight determinationLight) {
                
-               NonViralName name = makeTaxonName(state, determinationLight);
+               NonViralName<?> name = makeTaxonName(state, determinationLight);
                String titleCache = makeSearchNameTitleCache(state, determinationLight, name);
                
                //TODO
@@ -600,7 +709,7 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                event.setTaxon(taxon);
                
                //date
-               TimePeriod date = TimePeriod.parseString(determination.determinedWhen);
+               TimePeriod date = TimePeriodParser.parseString(determination.determinedWhen);
                event.setTimeperiod(date);
                //by
                //FIXME bracketAuthors and teams not yet implemented!!!
@@ -608,7 +717,12 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                if (StringUtils.isNotBlank(determination.determinedBy)){
                        authors.add(determination.determinedBy);
                }
-               AgentBase actor = getOrMakeAgent(state, authors);
+               TeamOrPersonBase<?> actor = getOrMakeAgent(state, authors);
+               TeamOrPersonBase<?> secAuthor = taxon.getSec() == null ? null : taxon.getSec().getAuthorTeam();
+               if (actor != null && secAuthor != null && secAuthor.getTitleCache().equals(actor.getTitleCache()) && secAuthor.getNomenclaturalTitle().equals(actor.getNomenclaturalTitle())) {
+                       actor = secAuthor;
+               }
+               
                event.setActor(actor);
                
                //TODO
@@ -618,8 +732,10 @@ 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;
        }
@@ -629,7 +745,7 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                return desc;
        }
 
-       private AgentBase<?> getOrMakeAgent(SpecimenCdmExcelImportState state, List<String> agents) {
+       private TeamOrPersonBase<?> getOrMakeAgent(SpecimenCdmExcelImportState state, List<String> agents) {
                if (agents.size() == 0){
                        return null;
                }else if (agents.size() == 1){
@@ -638,6 +754,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]));
@@ -739,6 +883,7 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                        try {
                                refSys = state.getTransformer().getReferenceSystemByKey(strRefSys);
                                if (refSys == null){
+                                       //TODO we still need user defined Reference Systems here
                                        refUuid = state.getTransformer().getReferenceSystemUuid(strRefSys);
                                        if (refUuid == null){
                                                String message = "Unknown reference system %s in line %d";
@@ -802,7 +947,7 @@ public class SpecimenCdmExcelImport  extends ExcelImporterBase<SpecimenCdmExcelI
                        }
                }
                if (StringUtils.isNotBlank(row.getCountry())){
-                       List<WaterbodyOrCountry> countries = getOccurrenceService().getWaterbodyOrCountryByName(row.getCountry());
+                       List<Country> countries = getOccurrenceService().getCountryByName(row.getCountry());
                        if (countries.size() >0){
                                facade.setCountry(countries.get(0));
                        }else{
@@ -817,23 +962,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;
        }
 
 
@@ -848,6 +981,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)
         */