ref #8509 improve tagged names handling in PESI export (ERMS and others)
[cdmlib-apps.git] / cdm-pesi / src / main / java / eu / etaxonomy / cdm / io / pesi / out / PesiTaxonExport.java
index 953efe5dd306f5aa8a4f9267e0d0f87cf0916c78..c1ddea0cc92650e1b62665ec8a2b70a1bf307b3d 100644 (file)
@@ -14,9 +14,8 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Types;
 import java.util.ArrayList;
-import java.util.BitSet;
+import java.util.EnumSet;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -26,19 +25,14 @@ import java.util.regex.Pattern;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
-import org.joda.time.DateTime;
-import org.joda.time.format.DateTimeFormat;
-import org.joda.time.format.DateTimeFormatter;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.TransactionStatus;
 
 import eu.etaxonomy.cdm.api.service.TaxonServiceImpl;
 import eu.etaxonomy.cdm.common.CdmUtils;
-import eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer;
 import eu.etaxonomy.cdm.io.common.Source;
 import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
 import eu.etaxonomy.cdm.io.common.mapping.out.DbConstantMapper;
-import eu.etaxonomy.cdm.io.common.mapping.out.DbExtensionMapper;
 import eu.etaxonomy.cdm.io.common.mapping.out.DbLastActionMapper;
 import eu.etaxonomy.cdm.io.common.mapping.out.DbObjectMapper;
 import eu.etaxonomy.cdm.io.common.mapping.out.DbStringMapper;
@@ -46,14 +40,12 @@ import eu.etaxonomy.cdm.io.common.mapping.out.IdMapper;
 import eu.etaxonomy.cdm.io.common.mapping.out.MethodMapper;
 import eu.etaxonomy.cdm.io.common.mapping.out.ObjectChangeMapper;
 import eu.etaxonomy.cdm.io.pesi.erms.ErmsTransformer;
-import eu.etaxonomy.cdm.model.common.Annotation;
 import eu.etaxonomy.cdm.model.common.AnnotationType;
 import eu.etaxonomy.cdm.model.common.CdmBase;
 import eu.etaxonomy.cdm.model.common.Extension;
 import eu.etaxonomy.cdm.model.common.ExtensionType;
 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
-import eu.etaxonomy.cdm.model.common.Language;
 import eu.etaxonomy.cdm.model.common.Marker;
 import eu.etaxonomy.cdm.model.common.MarkerType;
 import eu.etaxonomy.cdm.model.common.RelationshipBase;
@@ -71,6 +63,8 @@ import eu.etaxonomy.cdm.model.taxon.SynonymType;
 import eu.etaxonomy.cdm.model.taxon.Taxon;
 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
+import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
+import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
 import eu.etaxonomy.cdm.strategy.cache.HTMLTagRules;
 import eu.etaxonomy.cdm.strategy.cache.TagEnum;
 import eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy;
@@ -104,30 +98,24 @@ public class PesiTaxonExport extends PesiExportBase {
 
        private static final String pluralString = "Taxa";
        private static final String parentPluralString = "Taxa";
+       private static final String pluralStringNames = "Names";
 
-       private PreparedStatement parentTaxonFk_TreeIndex_KingdomFkStmt;
+//     private PreparedStatement parentTaxonFk_TreeIndex_KingdomFkStmts;
        private PreparedStatement parentTaxonFkStmt;
        private PreparedStatement rankTypeExpertsUpdateStmt;
        private PreparedStatement rankUpdateStmt;
        private Integer kingdomFk;
-       private AnnotationType treeIndexAnnotationType;
+
        private static ExtensionType lastActionExtensionType;
        private static ExtensionType lastActionDateExtensionType;
        private static ExtensionType expertNameExtensionType;
        private static ExtensionType speciesExpertNameExtensionType;
        private static ExtensionType cacheCitationExtensionType;
+
        public static TaxonNameDefaultCacheStrategy zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
        public static TaxonNameDefaultCacheStrategy nonViralNameStrategy = TaxonNameDefaultCacheStrategy.NewInstance();
        private static int currentTaxonId;
 
-       protected AnnotationType getTreeIndexAnnotationType() {
-               return treeIndexAnnotationType;
-       }
-
-       protected void setTreeIndexAnnotationType(AnnotationType treeIndexAnnotationType) {
-               this.treeIndexAnnotationType = treeIndexAnnotationType;
-       }
-
        enum NamePosition {
                beginning,
                end,
@@ -179,8 +167,7 @@ public class PesiTaxonExport extends PesiExportBase {
                        success &= doPhase01(state, mapping, additionalSourceMapping);
 
                        //"PHASE 1b: Handle names without taxa ...
-                       success &= doNames(state, additionalSourceMapping);
-
+                       success &= doPhase01b_Names(state, additionalSourceMapping);
 
                        // 2nd Round: Add ParentTaxonFk to each taxon
                        success &= doPhase02(state);
@@ -191,7 +178,6 @@ public class PesiTaxonExport extends PesiExportBase {
                        // 4nd Round: Add TreeIndex to each taxon
                        success &= doPhase04(state);
 
-
                        //"PHASE 5: Creating Inferred Synonyms...
                        success &= doPhase05(state, mapping, synonymRelMapping);
 
@@ -210,19 +196,19 @@ public class PesiTaxonExport extends PesiExportBase {
 
 
        private void initPreparedStatements(PesiExportState state) throws SQLException {
-               initTreeIndexStatement(state);
+//             initTreeIndexStatement(state);
                initRankExpertsUpdateStmt(state);
                initRankUpdateStatement(state);
 
                initParentFkStatement(state);
        }
 
-       // Prepare TreeIndex-And-KingdomFk-Statement
-       private void initTreeIndexStatement(PesiExportState state) throws SQLException {
-               Connection connection = state.getConfig().getDestination().getConnection();
-               String parentTaxonFk_TreeIndex_KingdomFkSql = "UPDATE Taxon SET ParentTaxonFk = ?, TreeIndex = ? WHERE TaxonId = ?";
-               parentTaxonFk_TreeIndex_KingdomFkStmt = connection.prepareStatement(parentTaxonFk_TreeIndex_KingdomFkSql);
-       }
+//     // Prepare TreeIndex-And-KingdomFk-Statement
+//     private void initTreeIndexStatement(PesiExportState state) throws SQLException {
+//             Connection connection = state.getConfig().getDestination().getConnection();
+//             String parentTaxonFk_TreeIndex_KingdomFkSql = "UPDATE Taxon SET ParentTaxonFk = ?, TreeIndex = ? WHERE TaxonId = ?";
+//             parentTaxonFk_TreeIndex_KingdomFkStmt = connection.prepareStatement(parentTaxonFk_TreeIndex_KingdomFkSql);
+//     }
 
        // Prepare TreeIndex-And-KingdomFk-Statement
        private void initParentFkStatement(PesiExportState state) throws SQLException {
@@ -249,9 +235,9 @@ public class PesiTaxonExport extends PesiExportBase {
        }
 
        private boolean doPhase01(PesiExportState state, PesiExportMapping mapping, PesiExportMapping additionalSourceMapping){
-               int count = 0;
+
+           int count = 0;
                int pastCount = 0;
-               List<TaxonBase> list;
                boolean success = true;
                // Get the limit for objects to save within a single transaction.
                int limit = state.getConfig().getLimitSave();
@@ -266,6 +252,7 @@ public class PesiTaxonExport extends PesiExportBase {
         }
 
                int partitionCount = 0;
+               List<TaxonBase<?>> list;
                while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)) != null   ) {
 
                        logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
@@ -275,7 +262,6 @@ public class PesiTaxonExport extends PesiExportBase {
                                TaxonName taxonName = taxon.getName();
 
                                TaxonName nvn = CdmBase.deproxy(taxonName);
-//                             System.err.println(nvn.getTitleCache());
                                if (! nvn.isProtectedTitleCache()){
                                        nvn.setTitleCache(null, false);
                                }
@@ -292,7 +278,7 @@ public class PesiTaxonExport extends PesiExportBase {
                                if (nvn.getRank().equals(Rank.KINGDOM())){
                                    if(taxon.isInstanceOf(Taxon.class)){
                                        String treeIndex = ((Taxon)taxon).getTaxonNodes().iterator().next().treeIndex();
-                                       Integer kingdomId = PesiTransformer.pesiKingdomMap.get(nvn.getGenusOrUninomial());
+                                       Integer kingdomId = PesiTransformer.pesiKingdomId(nvn.getGenusOrUninomial());
                                        state.getTreeIndexKingdomMap().put(treeIndex, kingdomId);
                                    }else{
                                        logger.warn("Kingdom taxon is not of class Taxon but " + taxon.getClass().getSimpleName() + ": " + nvn.getGenusOrUninomial());
@@ -310,62 +296,57 @@ public class PesiTaxonExport extends PesiExportBase {
                                }
 
                                //TODO switch on again, leads to some warnings in ERMS for taxa of not correctly handled kingdoms
-//                             validatePhaseOne(taxon, nvn);
+                               validatePhaseOne(taxon, nvn);
                        }
 
                        // Commit transaction
                        commitTransaction(txStatus);
-                       logger.debug("Committed transaction.");
                        logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 01)");
                        pastCount = count;
-                       /*logger.warn("Taking snapshot at the end of the loop of phase 1 of taxonExport");
-                       //ProfilerController.memorySnapshot();
-                       */
-                       // Start transaction
+
+                       // Start new transaction
                        txStatus = startTransaction(true);
                        if (logger.isDebugEnabled()) {
                 logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
             }
 
                }
-               logger.info("No " + pluralString + " left to fetch.");
+               logger.debug("No " + pluralString + " left to fetch.");
 
                // Commit transaction
                commitTransaction(txStatus);
                txStatus = null;
-               logger.debug("Committed transaction.");
-               if (logger.isDebugEnabled()){
-                       logger.debug("Taking snapshot at the end of phase 1 of taxonExport");
-//                     ProfilerController.memorySnapshot();
-               }
+
                return success;
        }
 
-
        private void validatePhaseOne(TaxonBase<?> taxon, TaxonName taxonName) {
 
            // Check whether some rules are violated
-               NomenclaturalCode nomenclaturalCode = taxonName.getNameType();
                String genusOrUninomial = taxonName.getGenusOrUninomial();
                String specificEpithet = taxonName.getSpecificEpithet();
                String infraSpecificEpithet = taxonName.getInfraSpecificEpithet();
                String infraGenericEpithet = taxonName.getInfraGenericEpithet();
-               Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
+               Rank rank =  taxonName.getRank();
 
-               if (rankFk == null) {
-                       logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
-               } else {
+               //as kingdomFk can not be defined in Phase 01 the below code was switched to use the CDM rank.
+               //This may be changed if we move validation to Phase03 or later
+//             Integer rankFk = getRankFk(taxonName, taxonName.getNameType());
+//             if (rankFk == null) {
+//                     logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
+//             } else {
 
                        // Check whether infraGenericEpithet is set correctly
                        // 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
                        // 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
 
                        int ancestorLevel = 0;
-                       if (taxonName.getRank().equals(Rank.SUBSPECIES())) {
+                       if (rank == null){
+                           logger.warn("PhaseOne validation: Taxon name has no rank: " + taxonName.getTitleCache());
+                       }else if (rank.equals(Rank.SUBSPECIES())) {
                                // The accepted taxon two rank levels above should be of rank subgenus
                                ancestorLevel  = 2;
-                       }
-                       if (taxonName.getRank().equals(Rank.SPECIES())) {
+                       }else if (rank.equals(Rank.SPECIES())) {
                                // The accepted taxon one rank level above should be of rank subgenus
                                ancestorLevel = 1;
                        }
@@ -379,21 +360,23 @@ public class PesiTaxonExport extends PesiExportBase {
                                }
                        }
 
-                       if (infraGenericEpithet == null && rankFk.intValue() == 190) {
-                               logger.warn("InfraGenericEpithet was not determined although it should exist for rank 190: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
-                       }
-                       if (specificEpithet != null && rankFk.intValue() < 216) {
-                               logger.warn("SpecificEpithet was determined for rank " + rankFk + " although it should only exist for ranks higher or equal to 220: TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
-                       }
-                       if (infraSpecificEpithet != null && rankFk.intValue() < 225) {
-                               String message = "InfraSpecificEpithet '" +infraSpecificEpithet + "' was determined for rank " + rankFk + " although it should only exist for ranks higher or equal to 230: "  + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")";
-                               if (StringUtils.isNotBlank(infraSpecificEpithet)){
-                                       logger.warn(message);
-                               }else{
-                                       logger.warn(message);
-                               }
+                       if (rank != null){
+                           if (infraGenericEpithet == null && rank.isInfraGenericButNotSpeciesGroup()) {
+                               logger.warn("InfraGenericEpithet was not determined although it should exist for infra generic names: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
+                           }
+                           if (specificEpithet != null && (rank.isInfraGenericButNotSpeciesGroup()||rank.isGenus()||rank.isSupraGeneric())) {
+                               logger.warn("SpecificEpithet was determined for rank " + rank.getTitleCache() + " although it should only exist for species aggregates, species or infraspecific taxa: TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
+                           }
+                           if (infraSpecificEpithet != null && !rank.isInfraSpecific()) {
+                               String message = "InfraSpecificEpithet '" +infraSpecificEpithet + "' was determined for rank " + rank.getTitleCache() + " although it should only exist for rank species and higher: "  + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")";
+                               if (StringUtils.isNotBlank(infraSpecificEpithet)){
+                                   logger.warn(message);
+                               }else{
+                                   logger.warn(message);
+                               }
+                           }
                        }
-               }
+//             }
                if (infraSpecificEpithet != null && specificEpithet == null) {
                        logger.warn("An infraSpecificEpithet was determined, but a specificEpithet was not determined: "  + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
                }
@@ -404,8 +387,6 @@ public class PesiTaxonExport extends PesiExportBase {
 
        /**
         * 2nd Round: Add ParentTaxonFk to each taxon and add Biota if not exists
-        * @param state
-        * @return
         */
        private boolean doPhase02(PesiExportState state) {
                int count = 0;
@@ -416,8 +397,6 @@ public class PesiTaxonExport extends PesiExportBase {
                        return success;
                }
 
-               List<Taxon> list;
-
                // Get the limit for objects to save within a single transaction.
                int limit = state.getConfig().getLimitSave();
 
@@ -429,6 +408,7 @@ public class PesiTaxonExport extends PesiExportBase {
                int partitionCount = 0;
 
 //             ProfilerController.memorySnapshot();
+               List<Taxon> list;
                while ((list = getNextTaxonPartition(Taxon.class, limit, partitionCount++, null)) != null   ) {
 
                        if(logger.isDebugEnabled()) {
@@ -438,7 +418,7 @@ public class PesiTaxonExport extends PesiExportBase {
                                for (TaxonNode node : taxon.getTaxonNodes()){
                                        doCount(count++, modCount, pluralString);
                                        TaxonNode parentNode = node.getParent();
-                                       if (parentNode != null && parentNode.getTaxon() != null){  //new root node handling requires has root taxon with taxon == null
+                                       if (parentNode != null && isPesiTaxon(parentNode.getTaxon())){ //exclude root taxa and unpublished parents (relevant for "Valueless" parent for E+M Rubus taxa). Usually a parent should not be unpublished
                                                int childId = state.getDbId( taxon);
                                                int parentId = state.getDbId(parentNode.getTaxon());
                                                success &= invokeParentTaxonFk(parentId, childId);
@@ -456,7 +436,7 @@ public class PesiTaxonExport extends PesiExportBase {
                            logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
                        }
                }
-               logger.info("No " + pluralString + " left to fetch.");
+               logger.debug("No " + pluralString + " left to fetch.");
 
                // Commit transaction
                commitTransaction(txStatus);
@@ -466,8 +446,6 @@ public class PesiTaxonExport extends PesiExportBase {
 
        /**
         * Inserts the Biota Taxon if not yet exists.
-        * @param state
-        * @throws SQLException
         */
        private void insertBiota(PesiExportState state) {
                try {
@@ -484,144 +462,6 @@ public class PesiTaxonExport extends PesiExportBase {
                }
        }
 
-       // 4th round: Add TreeIndex to each taxon
-       private boolean doPhase04(PesiExportState state) {
-               boolean success = true;
-
-               logger.info("PHASE 4: Make TreeIndex ... ");
-
-               //TODO test if possible to move to phase 02
-               String sql = " UPDATE Taxon SET ParentTaxonFk = (SELECT TaxonId FROM Taxon WHERE RankFk = 0) " +
-                               " WHERE (RankFk = 10) and TaxonStatusFk = 1 ";
-               state.getConfig().getDestination().update(sql);
-
-               state.getConfig().getDestination().update("EXEC dbo.recalculateallstoredpaths");
-
-               logger.info("PHASE 4: Make TreeIndex DONE");
-
-               return success;
-
-       }
-
-
-//     // 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
-//     private boolean doPhase02_OLD(PesiExportState state) {
-//             boolean success = true;
-//             boolean includeUnpublished = false;
-//             if (! state.getConfig().isDoTreeIndex()){
-//                     logger.info ("Ignore PHASE 2: ParentTaxonFk and TreeIndex");
-//                     return success;
-//             }
-//
-//             List<Classification> classificationList = null;
-//             logger.info("PHASE 2: Add ParenTaxonFk and TreeIndex...");
-//
-//             // Specify starting ranks for tree traversing
-//             rankList.add(Rank.KINGDOM());
-//             rankList.add(Rank.GENUS());
-//
-//             // Specify where to stop traversing (value) when starting at a specific Rank (key)
-//             rank2endRankMap.put(Rank.GENUS(), null); // Since NULL does not match an existing Rank, traverse all the way down to the leaves
-//             rank2endRankMap.put(Rank.KINGDOM(), Rank.GENUS()); // excludes rank genus
-//
-//             StringBuffer treeIndex = new StringBuffer();
-//
-//             // Retrieve list of classifications
-//             TransactionStatus txStatus = startTransaction(true);
-//             logger.info("Started transaction for parentFk and treeIndex. Fetching all classifications...");
-//             classificationList = getClassificationService().listClassifications(null, 0, null, null);
-//             commitTransaction(txStatus);
-//             logger.debug("Committed transaction.");
-//
-//             logger.info("Fetched " + classificationList.size() + " classification(s).");
-//
-//             setTreeIndexAnnotationType(getAnnotationType(uuidTreeIndex, "TreeIndex", "TreeIndex", "TI"));
-//             List<TaxonNode> rankSpecificRootNodes;
-//             for (Classification classification : classificationList) {
-//                     for (Rank rank : rankList) {
-//
-//                             txStatus = startTransaction(true);
-//                             logger.info("Started transaction to fetch all rootNodes specific to Rank " + rank.getLabel() + " ...");
-//
-//                             rankSpecificRootNodes = getClassificationService().listRankSpecificRootNodes(classification,
-//                                     null, rank, includeUnpublished, null, null, null);
-//                             logger.info("Fetched " + rankSpecificRootNodes.size() + " RootNodes for Rank " + rank.getLabel());
-//
-//                             commitTransaction(txStatus);
-//                             logger.debug("Committed transaction.");
-//
-//                             for (TaxonNode rootNode : rankSpecificRootNodes) {
-//                                     txStatus = startTransaction(false);
-//                                     Rank endRank = rank2endRankMap.get(rank);
-//                                     if (endRank != null) {
-//                                             logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till Rank " + endRank.getLabel() + " ...");
-//                                     } else {
-//                                             logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till leaves are reached ...");
-//                                     }
-//
-//                                     TaxonNode newNode = getTaxonNodeService().load(rootNode.getUuid());
-//
-//                                     if (isPesiTaxon(newNode.getTaxon())){
-//                                             TaxonNode parentNode = newNode.getParent();
-//                                             if (rank.equals(Rank.KINGDOM())) {
-//                                                     treeIndex = new StringBuffer();
-//                                                     treeIndex.append("#");
-//                                             } else {
-//                                                     // Get treeIndex from parentNode
-//                                                     if (parentNode != null) {
-//                                                             boolean annotationFound = false;
-//                                                             Set<Annotation> annotations = parentNode.getAnnotations();
-//                                                             for (Annotation annotation : annotations) {
-//                                                                     AnnotationType annotationType = annotation.getAnnotationType();
-//                                                                     if (annotationType != null && annotationType.equals(getTreeIndexAnnotationType())) {
-//                                                                             treeIndex = new StringBuffer(CdmUtils.Nz(annotation.getText()));
-//                                                                             annotationFound = true;
-//     //                                                                      logger.error("treeIndex: " + treeIndex);
-//                                                                             break;
-//                                                                     }
-//                                                             }
-//                                                             if (!annotationFound) {
-//                                                                     // This should not happen because it means that the treeIndex was not set correctly as an annotation to parentNode
-//                                                                     logger.error("TreeIndex could not be read from annotation of TaxonNode: " + parentNode.getUuid() + ", Taxon: " + parentNode.getTaxon().getUuid());
-//                                                                     treeIndex = new StringBuffer();
-//                                                                     treeIndex.append("#");
-//                                                             }
-//                                                     } else {
-//                                                             // TreeIndex could not be determined, but it's unclear how to proceed to generate a correct treeIndex if the parentNode is NULL
-//                                                             logger.error("ParentNode for RootNode is NULL. TreeIndex could not be determined: " + newNode.getUuid());
-//                                                             treeIndex = new StringBuffer(); // This just prevents growing of the treeIndex in a wrong manner
-//                                                             treeIndex.append("#");
-//                                                     }
-//                                             }
-//                                             nomenclaturalCode = newNode.getTaxon().getName().getNameType();
-//                                             kingdomFk = PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
-//                                             traverseTree(newNode, parentNode, treeIndex, endRank, state);
-//                                             parentNode =null;
-//                                     }else{
-//                                             logger.debug("Taxon is not a PESI taxon: " + newNode.getTaxon().getUuid());
-//                                     }
-//
-//                                     newNode = null;
-//
-//                                     try {
-//                                             commitTransaction(txStatus);
-//                                             logger.debug("Committed transaction.");
-//                                     } catch (Exception e) {
-//                                             logger.error(e.getMessage());
-//                                             e.printStackTrace();
-//                                     }
-//
-//                             }
-//                             rankSpecificRootNodes = null;
-//                     }
-//
-//             }
-//
-//             logger.warn("Taking snapshot at the end of phase 2 of taxonExport");
-//             //ProfilerController.memorySnapshot();
-//             return success;
-//     }
-
        //PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...
        private boolean doPhase03(PesiExportState state) {
                int count = 0;
@@ -631,10 +471,12 @@ public class PesiTaxonExport extends PesiExportBase {
                        logger.info ("Ignore PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
                        return success;
                }
+
+               addValuelessTaxonToKingdomMap(state);
+
                // Get the limit for objects to save within a single transaction.
                int limit = state.getConfig().getLimitSave();
 
-               List<TaxonBase> list;
                logger.info("PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
                // Be sure to add rank information, KingdomFk, TypeNameFk, expertFk and speciesExpertFk to every taxonName
 
@@ -644,6 +486,8 @@ public class PesiTaxonExport extends PesiExportBase {
             logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
         }
                int partitionCount = 0;
+               @SuppressWarnings("rawtypes")
+        List<TaxonBase> list;
                while ((list = getNextTaxonPartition(TaxonBase.class, limit, partitionCount++, null)) != null) {
 
                        if (logger.isDebugEnabled()) {
@@ -660,15 +504,10 @@ public class PesiTaxonExport extends PesiExportBase {
                                doCount(count++, modCount, pluralString);
                                Integer typeNameFk = getTypeNameFk(taxonName, state);
                                Integer kingdomFk = findKingdomIdFromTreeIndex(taxon, state);
-                                //       PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
-
-                               //TODO why are expertFks needed? (Andreas M.)
-//                             if (expertFk != null || speciesExpertFk != null) {
-                                   NomenclaturalCode nomCode = taxonName.getNameType();
-                                   //is there a reason why we do pass nomCode separately? Before nomCode was class variable, but not clear why and when it was set
-                                       invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, nomCode, state.getDbId(taxon),
-                                                       typeNameFk, kingdomFk, state);
-//                             }
+                               Integer rankFk = getRankFk(taxonName, kingdomFk);
+
+                           invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, state.getDbId(taxon),
+                                               typeNameFk, kingdomFk, rankFk, state);
                        }
 
                        // Commit transaction
@@ -683,7 +522,7 @@ public class PesiTaxonExport extends PesiExportBase {
                 logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
             }
                }
-               logger.info("No " + pluralString + " left to fetch.");
+               logger.debug("No " + pluralString + " left to fetch.");
 
                // Commit transaction
                commitTransaction(txStatus);
@@ -696,19 +535,56 @@ public class PesiTaxonExport extends PesiExportBase {
                return success;
        }
 
-    private static Integer findKingdomIdFromTreeIndex(TaxonBase<?> taxonBase,PesiExportState state) {
+    private void addValuelessTaxonToKingdomMap(PesiExportState state) {
+        TransactionStatus txStatus = startTransaction();
+        Taxon valuelessTaxon = (Taxon)getTaxonService().find(PesiTransformer.uuidTaxonValuelessEuroMed);
+        if (valuelessTaxon != null){
+            String treeIndex = valuelessTaxon.getTaxonNodes().iterator().next().treeIndex();
+            Integer kingdomId = PesiTransformer.pesiKingdomId("Plantae");
+            state.getTreeIndexKingdomMap().put(treeIndex, kingdomId);
+        }
+        commitTransaction(txStatus);
+    }
+
+    // 4th round: Add TreeIndex to each taxon
+    private boolean doPhase04(PesiExportState state) {
+        boolean success = true;
+
+        logger.info("PHASE 4: Make TreeIndex ... ");
+
+        //TODO test if possible to move to phase 02
+        String sql = " UPDATE Taxon SET ParentTaxonFk = (SELECT TaxonId FROM Taxon WHERE RankFk = 0) " +
+                " WHERE (RankFk = 10) and TaxonStatusFk = 1 ";
+        state.getConfig().getDestination().update(sql);
+
+        state.getConfig().getDestination().update("EXEC dbo.recalculateallstoredpaths");
+
+        logger.info("PHASE 4: Make TreeIndex DONE");
+
+        return success;
+    }
+
+    private static Integer findKingdomIdFromTreeIndex(TaxonBase<?> taxonBase, PesiExportState state) {
         Taxon taxon;
         if (taxonBase instanceof Synonym){
             taxon = ((Synonym) taxonBase).getAcceptedTaxon();
         }else{
-            taxon = (Taxon)taxonBase;
+            taxon = checkPseudoOrRelatedTaxon((Taxon)taxonBase);
         }
-        if (taxon != null){
+        if (taxon == null){
+            NomenclaturalCode nomenclaturalCode = taxonBase.getName().getNameType();
+            logger.warn("Taxon is synonym with no accepted taxon attached: " + taxonBase.getTitleCache() + ". The kingdom is taken from the nomenclatural code: " + PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode) );
+            return PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
+        } else{
             Set<TaxonNode> nodes = taxon.getTaxonNodes();
-            if (nodes.size()>1){
-                logger.warn("The taxon has more then 1 taxon node: " + taxon.getTitleCache() + ". Take arbitrary one.");
-            }
-            if (!nodes.isEmpty()){
+            if (nodes.isEmpty()){
+                NomenclaturalCode nomenclaturalCode = taxon.getName().getNameType();
+                logger.warn("The taxon has no nodes: " + taxon.getTitleCache() + ". The kingdom is taken from the nomenclatural code: " + PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
+                return PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
+            } else {
+                if (nodes.size()>1){
+                    logger.warn("The taxon has more then 1 taxon node: " + taxon.getTitleCache() + ". Take arbitrary one.");
+                }
                 String treeIndex = nodes.iterator().next().treeIndex();
 
                 Pattern pattern = Pattern.compile("#t[0-9]+#([0-9]+#){3}");
@@ -717,12 +593,14 @@ public class PesiTaxonExport extends PesiExportBase {
                 if(matcher.find()) {
                     String treeIndexKingdom = matcher.group(0);
                     kingdomID = state.getTreeIndexKingdomMap().get(treeIndexKingdom);
-                }else{
+                }
+                if (kingdomID == null){
                     pattern = Pattern.compile("#t[0-9]+#([0-9]+#){2}");
                     matcher = pattern.matcher(treeIndex);
                     if(matcher.find()) {
                         String treeIndexKingdom = matcher.group(0);
-                        kingdomID = state.getTreeIndexKingdomMap().get(treeIndexKingdom);
+                        Map<String, Integer> map = state.getTreeIndexKingdomMap();
+                        kingdomID = map.get(treeIndexKingdom);
                     }
                 }
                 if(Rank.DOMAIN().equals(taxon.getName().getRank())){
@@ -732,18 +610,60 @@ public class PesiTaxonExport extends PesiExportBase {
                     logger.warn("Kingdom could not be defined for treeindex " + treeIndex);
                 }
                 return kingdomID;
-            } else {
-                NomenclaturalCode nomenclaturalCode = taxon.getName().getNameType();
-                logger.warn("The taxon has no nodes: " + taxon.getTitleCache() + ". The kingdom is taken from the nomenclatural code: " + PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
-                return PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
             }
-        } else{
-            NomenclaturalCode nomenclaturalCode = taxonBase.getName().getNameType();
-            logger.warn("Taxon is synonym with no accepted taxon attached: " + taxonBase.getTitleCache() + ". The kingdom is taken from the nomenclatural code: " + PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode) );
-            return PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
         }
     }
 
+    private static Taxon checkPseudoOrRelatedTaxon(Taxon taxon) {
+        if (!taxon.getTaxonNodes().isEmpty()){
+            return taxon;
+        }else if(hasPseudoTaxonRelationship(taxon)){
+            return acceptedPseudoTaxon(taxon);
+        }else if(isMisappliedNameOrProParteSynonym(taxon)){
+            return acceptedTaxonConcept(taxon);
+        }else{
+            return taxon;
+        }
+    }
+
+    private static Taxon acceptedPseudoTaxon(Taxon taxon) {
+        for (TaxonRelationship rel : taxon.getRelationsFromThisTaxon()){
+            if (TaxonRelationshipType.pseudoTaxonUuids().contains(rel.getType().getUuid())){
+                return rel.getToTaxon();
+            }
+        }
+        return taxon;
+    }
+
+    private static Taxon acceptedTaxonConcept(Taxon taxon) {
+       for (TaxonRelationship rel : taxon.getRelationsFromThisTaxon()){
+            if (TaxonRelationshipType.misappliedNameUuids().contains(rel.getType().getUuid())||
+                    TaxonRelationshipType.proParteOrPartialSynonymUuids().contains(rel.getType().getUuid())){
+                return rel.getToTaxon();
+            }
+        }
+        return taxon;
+    }
+
+    private static boolean hasPseudoTaxonRelationship(Taxon taxon) {
+        for (TaxonRelationship rel : taxon.getRelationsFromThisTaxon()){
+            if (TaxonRelationshipType.pseudoTaxonUuids().contains(rel.getType().getUuid())){
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isMisappliedNameOrProParteSynonym(Taxon taxon) {
+        for (TaxonRelationship rel : taxon.getRelationsFromThisTaxon()){
+            if (TaxonRelationshipType.misappliedNameUuids().contains(rel.getType().getUuid())||
+                    TaxonRelationshipType.proParteOrPartialSynonymUuids().contains(rel.getType().getUuid())){
+                return true;
+            }
+        }
+        return false;
+    }
+
     // "PHASE 5: Creating Inferred Synonyms..."
        private boolean doPhase05(PesiExportState state, PesiExportMapping mapping, PesiExportMapping synRelMapping) {
                int count;
@@ -774,9 +694,9 @@ public class PesiTaxonExport extends PesiExportBase {
                if (logger.isDebugEnabled()) {
             logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
         }
-               List<TaxonBase> taxonList = null;
 
-               while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SPECIES(), pageSize, pageNumber)).size() > 0) {
+               List<TaxonBase> taxonList = null;
+               while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SPECIES(), pageSize, pageNumber, null)).size() > 0) {
 
                    Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
 
@@ -796,8 +716,7 @@ public class PesiTaxonExport extends PesiExportBase {
                        // Save Rank Data and KingdomFk for inferred synonyms
                        for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
                            TaxonName taxonName = inferredSynonymsDataToBeSaved.get(taxonFk);
-                NomenclaturalCode nomCode = taxonName.getNameType(); //nomCode was class variable before, not sure if this was important
-                invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomCode, taxonFk, kingdomFk, state);
+                invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), taxonFk, kingdomFk, state);
                        }
 
                        // Start transaction
@@ -810,7 +729,7 @@ public class PesiTaxonExport extends PesiExportBase {
                        pageNumber++;
                }
                taxonList = null;
-               while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SUBSPECIES(), pageSize, pageNumber)).size() > 0) {
+               while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SUBSPECIES(), pageSize, pageNumber, null)).size() > 0) {
                        Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
 
                        logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
@@ -827,8 +746,7 @@ public class PesiTaxonExport extends PesiExportBase {
                        // Save Rank Data and KingdomFk for inferred synonyms
                        for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
                            TaxonName taxonName = inferredSynonymsDataToBeSaved.get(taxonFk);
-                           NomenclaturalCode nomCode = taxonName.getNameType(); //nomCode was class variable before, not sure if this was important
-                               invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomCode, taxonFk, kingdomFk, state);
+                           invokeRankDataAndKingdomFk(taxonName, taxonFk, kingdomFk, state);
                        }
 
                        // Start transaction
@@ -856,15 +774,6 @@ public class PesiTaxonExport extends PesiExportBase {
                return success;
        }
 
-       /**
-        * @param state
-        * @param mapping
-        * @param synRelMapping
-        * @param currentTaxonId
-        * @param taxonList
-        * @param inferredSynonymsDataToBeSaved
-        * @return
-        */
        private Map<Integer, TaxonName> createInferredSynonymsForTaxonList(PesiExportState state,
                        PesiExportMapping mapping, PesiExportMapping synRelMapping,      List<TaxonBase> taxonList) {
 
@@ -971,13 +880,10 @@ public class PesiTaxonExport extends PesiExportBase {
                return inferredSynonymsDataToBeSaved;
        }
 
-
        /**
-        * Handles names that do not appear in taxa
-        * @param state
-        * @param mapping
+        * Handles names that do not appear in taxa.
         */
-       private boolean doNames(PesiExportState state, PesiExportMapping additionalSourceMapping)  throws SQLException {
+       private boolean doPhase01b_Names(PesiExportState state, PesiExportMapping additionalSourceMapping) {
 
                boolean success = true;
                if (! state.getConfig().isDoPureNames()){
@@ -990,7 +896,6 @@ public class PesiTaxonExport extends PesiExportBase {
                        mapping.initialize(state);
                        int count = 0;
                        int pastCount = 0;
-                       List<TaxonName> list;
                        success = true;
                        // Get the limit for objects to save within a single transaction.
                        int limit = state.getConfig().getLimitSave();
@@ -1001,9 +906,10 @@ public class PesiTaxonExport extends PesiExportBase {
                        logger.info("Started new transaction for Pure Names. Fetching some " + pluralString + " (max: " + limit + ") ...");
 
                        int partitionCount = 0;
+                       List<TaxonName> list;
                        while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null   ) {
 
-                               logger.info("Fetched " + list.size() + " names without taxa. Exporting...");
+                               logger.debug("Fetched " + list.size() + pluralStringNames + " without taxa. Exporting...");
                                for (TaxonName taxonName : list) {
                                        doCount(count++, modCount, pluralString);
                                        success &= mapping.invoke(taxonName);
@@ -1016,14 +922,14 @@ public class PesiTaxonExport extends PesiExportBase {
                                // Commit transaction
                                commitTransaction(txStatus);
                                logger.debug("Committed transaction.");
-                               logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
+                               logger.info("Exported " + (count - pastCount) + " " + pluralStringNames + ". Total: " + count + ". Partition: " + partitionCount);
                                pastCount = count;
 
                                // Start transaction
                                txStatus = startTransaction(true);
-                               logger.info("Started new transaction for PureNames. Fetching some " + pluralString + " (max: " + limit + ") ...");
+                               logger.debug("Started new transaction for PureNames. Fetching some " + pluralString + " (max: " + limit + ") ...");
                        }
-                       logger.info("No " + pluralString + " left to fetch.");
+                       logger.debug("No " + pluralString + " left to fetch.");
 
                        // Commit transaction
                        commitTransaction(txStatus);
@@ -1126,121 +1032,7 @@ public class PesiTaxonExport extends PesiExportBase {
                return annotationType;
        }
 
-       /**
-        * Traverses the classification recursively and stores determined values for every Taxon.
-        * @param childNode The {@link TaxonNode TaxonNode} to process.
-        * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
-        * @param treeIndex The TreeIndex at the current level.
-        * @param fetchLevel Rank to stop fetching at.
-        * @param state The {@link PesiExportState PesiExportState}.
-        */
-       private void traverseTree(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, Rank fetchLevel, PesiExportState state) {
-               // Traverse all branches from this childNode until specified fetchLevel is reached.
-               StringBuffer localTreeIndex = new StringBuffer(treeIndex);
-               Taxon childTaxon = childNode.getTaxon();
-               if (childTaxon != null) {
-                       if (isPesiTaxon(childTaxon)){
-                               Integer taxonId = state.getDbId(childTaxon);
-                               TaxonName childName = childTaxon.getName();
-                               if (taxonId != null) {
-                                       Rank childRank = childName.getRank();
-                                       if (childRank != null) {
-                                               if (! childRank.equals(fetchLevel)) {
-
-                                                       localTreeIndex.append(taxonId + "#");
-
-                                                       saveData(childNode, parentNode, localTreeIndex, state, taxonId);
-
-                                                       // Store treeIndex as annotation for further use
-                                                       Annotation annotation = Annotation.NewInstance(localTreeIndex.toString(), getTreeIndexAnnotationType(), Language.DEFAULT());
-                                                       childNode.addAnnotation(annotation);
-
-                                                       for (TaxonNode newNode : childNode.getChildNodes()) {
-                                                               if (newNode.getTaxon() != null && isPesiTaxon(newNode.getTaxon())){
-                                                                       traverseTree(newNode, childNode, localTreeIndex, fetchLevel, state);
-                                                               }
-                                                       }
-
-                                               } else {
-       //                                              logger.debug("Target Rank " + fetchLevel.getLabel() + " reached");
-                                                       return;
-                                               }
-                                       } else {
-                                               logger.error("Rank is NULL. FetchLevel can not be checked: " + childName.getUuid() + " (" + childName.getTitleCache() + ")");
-                                       }
-                               } else {
-                                       logger.error("Taxon can not be found in state: " + childTaxon.getUuid() + " (" + childTaxon.getTitleCache() + ")");
-                               }
-                       }else{
-                               if (logger.isDebugEnabled()){
-                                       logger.debug("Taxon is not a PESI taxon: " + childTaxon.getUuid());
-                               }
-                       }
-               } else {
-                       logger.error("Taxon is NULL for TaxonNode: " + childNode.getUuid());
-               }
-       }
-
-       /**
-        * Stores values in database for every recursive round.
-        * @param childNode The {@link TaxonNode TaxonNode} to process.
-        * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
-        * @param treeIndex The TreeIndex at the current level.
-        * @param state The {@link PesiExportState PesiExportState}.
-        * @param currentTaxonFk The TaxonFk to store the values for.
-        */
-       private void saveData(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, PesiExportState state, Integer currentTaxonFk) {
-               // We are differentiating kingdoms by the nomenclatural code for now.
-               // This needs to be handled in a better way as soon as we know how to differentiate between more kingdoms.
-               Taxon childTaxon = childNode.getTaxon();
-               if (isPesiTaxon(childTaxon)) {
-                       TaxonBase<?> parentTaxon = null;
-                       if (parentNode != null) {
-                               parentTaxon = parentNode.getTaxon();
-                       }
-
-                       invokeParentTaxonFkAndTreeIndex(state.getDbId(parentTaxon), currentTaxonFk,     treeIndex);
-               }
-       }
-
-       /**
-        * Inserts values into the Taxon database table.
-        *
-        * @param taxonName The {@link TaxonNameBase TaxonName}.
-        * @param state The {@link PesiExportState PesiExportState}.
-        * @param stmt The prepared statement.
-        * @return Whether save was successful or not.
-        */
-       protected boolean invokeParentTaxonFkAndTreeIndex(Integer parentTaxonFk, Integer currentTaxonFk, StringBuffer treeIndex) {
-               try {
-                       if (parentTaxonFk != null) {
-                               parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(1, parentTaxonFk);
-                       } else {
-                               parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(1, null);
-                       }
-
-                       if (treeIndex != null) {
-                               parentTaxonFk_TreeIndex_KingdomFkStmt.setString(2, treeIndex.toString());
-                       } else {
-                               parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(2, null);
-                       }
-
-                       if (currentTaxonFk != null) {
-                               parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(3, currentTaxonFk);
-                       } else {
-                               parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(3, null);
-                       }
-
-                       parentTaxonFk_TreeIndex_KingdomFkStmt.executeUpdate();
-                       return true;
-               } catch (SQLException e) {
-                       logger.error("ParentTaxonFk (" + (parentTaxonFk ==null? "-":parentTaxonFk) + ") and TreeIndex could not be inserted into database for taxon "+ (currentTaxonFk == null? "-" :currentTaxonFk) + ": " + e.getMessage());
-                       e.printStackTrace();
-                       return false;
-               }
-       }
-
-       protected boolean invokeParentTaxonFk(Integer parentId, Integer childId) {
+       private boolean invokeParentTaxonFk(Integer parentId, Integer childId) {
                try {
                        parentTaxonFkStmt.setInt(1, parentId);
                        parentTaxonFkStmt.setInt(2, childId);
@@ -1264,16 +1056,18 @@ public class PesiTaxonExport extends PesiExportBase {
         * @param kindomFk The KingdomFk.
         * @return Whether save was successful or not.
         */
-       private boolean invokeRankDataAndKingdomFk(TaxonName taxonName, NomenclaturalCode nomenclaturalCode, Integer taxonFk, Integer kingdomFk, PesiExportState state) {
-               try {
-                       Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
+       private boolean invokeRankDataAndKingdomFk(TaxonName taxonName,
+               Integer taxonFk, Integer kingdomFk, PesiExportState state) {
+
+           try {
+                       Integer rankFk = getRankFk(taxonName, kingdomFk);
                        if (rankFk != null) {
                                rankUpdateStmt.setInt(1, rankFk);
                        } else {
                                rankUpdateStmt.setObject(1, null);
                        }
 
-                       String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
+                       String rankCache = getRankCache(taxonName, kingdomFk, state);
                        if (rankCache != null) {
                                rankUpdateStmt.setString(2, rankCache);
                        } else {
@@ -1308,26 +1102,25 @@ public class PesiTaxonExport extends PesiExportBase {
         * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
         * @param taxonFk The TaxonFk to store the values for.
         * @param typeNameFk The TypeNameFk.
+        * @param rankFk
         * @param state
         * @param kindomFk The KingdomFk.
         * @param expertFk The ExpertFk.
         * @param speciesExpertFk The SpeciesExpertFk.
         * @return Whether save was successful or not.
         */
-       private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonName taxonName, NomenclaturalCode nomenclaturalCode,
-                       Integer taxonFk, Integer typeNameFk, Integer kingdomFk, PesiExportState state) {
+       private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonName taxonName,
+                       Integer taxonFk, Integer typeNameFk, Integer kingdomFk, Integer rankFk, PesiExportState state) {
 
-           Integer rankFk = null;
            try {
                        int index = 1;
-                       rankFk = getRankFk(taxonName, nomenclaturalCode);
                        if (rankFk != null) {
                                rankTypeExpertsUpdateStmt.setInt(index++, rankFk);
                        } else {
                                rankTypeExpertsUpdateStmt.setObject(index++, null);
                        }
 
-                       String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
+                       String rankCache = getRankCache(taxonName, kingdomFk, state);
                        if (rankCache != null) {
                                rankTypeExpertsUpdateStmt.setString(index++, rankCache);
                        } else {
@@ -1368,11 +1161,13 @@ public class PesiTaxonExport extends PesiExportBase {
                        rankTypeExpertsUpdateStmt.executeUpdate();
                        return true;
                } catch (SQLException e) {
-                       logger.error("Data could not be inserted into database: " + e.getMessage() + "; rankFk = " + rankFk + "; kingdomFk = " + kingdomFk );
+                   String name = taxonName == null? null:taxonName.getTitleCache();
+                       logger.error("Data could not be inserted into database: " + e.getMessage() + "; rankFk = " + rankFk + "; kingdomFk = " + kingdomFk  + "; taxonFk = "+ taxonFk  + "; typeNameFk = "  + typeNameFk + "; name = " + name);
                        e.printStackTrace();
                        return false;
                } catch (Exception e) {
-                       logger.error("Some exception occurred: " + e.getMessage() + "; rankFk = " + rankFk + "; kingdomFk = " + kingdomFk);
+                   String name = taxonName == null? null:taxonName.getTitleCache();
+            logger.error("Some exception occurred: " + e.getMessage() + "; rankFk = " + rankFk + "; kingdomFk = " + kingdomFk  + "; taxonFk = "+ taxonFk + "; typeNameFk = " + typeNameFk + "; name = " + name);
                        e.printStackTrace();
                        return false;
                }
@@ -1384,64 +1179,24 @@ public class PesiTaxonExport extends PesiExportBase {
         * @return Whether the delete operation was successful or not.
         */
        protected boolean doDelete(PesiExportState state) {
-               PesiExportConfigurator pesiConfig = state.getConfig();
 
-               String sql;
-               Source destination =  pesiConfig.getDestination();
-
-               // Clear Taxon
-               sql = "DELETE FROM " + dbTableName;
-               destination.update(sql);
-               return true;
-       }
+               Source destination =  state.getConfig().getDestination();
 
-       /**
-        * Creates the kingdom fk.
-        * @param taxonName
-        * @return
-        */
-       @SuppressWarnings("unused")  //used by mapper
-       private static Integer getKingdomFk(TaxonName taxonName){
-               return PesiTransformer.nomenclaturalCode2Kingdom(taxonName.getNameType());
-       }
+               String[] tables = new String[]{"AdditionalTaxonSource","CommonNameSource","CommonName",
+                       "Image","NoteSource","Note","OccurrenceSource","Occurrence","RelTaxon","Taxon"};
 
-       /**
-        * Creates the parent fk.
-        * @param taxonName
-        * @return
-        */
-       @SuppressWarnings("unused")  //used by mapper
-       private static Integer getParentTaxonFk(TaxonBase<?> taxonBase, PesiExportState state){
-               if (taxonBase.isInstanceOf(Taxon.class)){
-                       Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
-                       if (! isMisappliedName(taxon)){
-                               Set<TaxonNode> nodes = taxon.getTaxonNodes();
-                               if (nodes.size() == 0){
-                                       if (taxon.getName().getRank().isLower(Rank.KINGDOM())){
-                                               logger.warn("Accepted taxon has no parent. " + taxon.getTitleCache() + ", " +  taxon.getUuid());
-                                       }
-                               }else if (nodes.size() > 1){
-                                       logger.warn("Taxon has more than 1 node attached. This is not supported by PESI export." +  taxon.getTitleCache() + ", " +  taxon.getUuid());
-                               }else{
-                                       Taxon parent =nodes.iterator().next().getParent().getTaxon();
-                                       return state.getDbId(parent);
-                               }
-                       }
+               for(String table : tables){
+                   String sql = "DELETE FROM " + table;
+                   destination.update(sql);
                }
-               return null;
-       }
 
-       /**
-        * Returns the rankFk for the taxon name based on the names nomenclatural code.
-        * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
-        * @param taxonName
-        * @return
-        */
-       @SuppressWarnings("unused")  //used by mapper
-       private static Integer getRankFk(TaxonName taxonName) {
-               return getRankFk(taxonName, taxonName.getNameType());
+               return true;
        }
 
+       private static Integer getRankFk(TaxonName taxonName, NomenclaturalCode nomenclaturalCode) {
+           Integer kingdomId = PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
+           return getRankFk(taxonName, kingdomId);
+       }
 
        /**
         * Returns the <code>RankFk</code> attribute.
@@ -1450,19 +1205,17 @@ public class PesiTaxonExport extends PesiExportBase {
         * @return The <code>RankFk</code> attribute.
         * @see MethodMapper
         */
-       private static Integer getRankFk(TaxonName taxonName, NomenclaturalCode nomenclaturalCode) {
+       private static Integer getRankFk(TaxonName taxonName, Integer kingdomId) {
                Integer result = null;
                try {
-                       if (nomenclaturalCode != null) {
-                               if (taxonName != null) {
-                                       if (taxonName.getRank() == null) {
-                                               logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
-                                       } else {
-                                               result = PesiTransformer.rank2RankId(taxonName.getRank(), PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
-                                       }
-                                       if (result == null) {
-                                               logger.warn("Rank could not be determined for PESI-Kingdom-Id " + PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode) + " and TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
-                                       }
+                       if (taxonName != null) {
+                               if (taxonName.getRank() == null) {
+                                       logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
+                               } else {
+                                       result = PesiTransformer.rank2RankId(taxonName.getRank(), kingdomId);
+                               }
+                               if (result == null) {
+                                       logger.warn("Rank could not be determined for PESI-Kingdom-Id " + kingdomId + " and TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
                                }
                        }
                } catch (Exception e) {
@@ -1471,41 +1224,32 @@ public class PesiTaxonExport extends PesiExportBase {
                return result;
        }
 
-       /**
-        * Returns the rank cache for the taxon name based on the names nomenclatural code.
-        * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
-        */
        @SuppressWarnings("unused")  //used by mapper
-       private static String getRankCache(TaxonName taxonName, PesiExportState state) {
-               return getRankCache(taxonName, taxonName.getNameType(), state);
+    private static String getRankCache(TaxonName taxonName, PesiExportState state) {
+           List<TaxonNode> nodes = getTaxonNodes(taxonName);
+           Integer kingdomId;
+           if (nodes == null||nodes.isEmpty()){
+               kingdomId = getKingdomFk(taxonName);
+           }else{
+               //should not happen, method exists only pure names
+               kingdomId = findKingdomIdFromTreeIndex(nodes.iterator().next().getTaxon(), state);
+           }
+        return getRankCache(taxonName, kingdomId, state);
        }
 
-       /**
-        * Returns the <code>RankCache</code> attribute.
-        * @param taxonName The {@link TaxonNameBase TaxonName}.
-        * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
-        * @param state
-        * @return The <code>RankCache</code> attribute.
-        * @see MethodMapper
-        */
-       private static String getRankCache(TaxonName taxonName, NomenclaturalCode nomenclaturalCode, PesiExportState state) {
-           List<TaxonNode> nodes = getTaxonNodes(taxonName);
+       private static String getRankCache(TaxonName taxonName, Integer kingdomFk, PesiExportState state) {
            if (Rank.DOMAIN().equals(taxonName.getRank())){
             return state.getTransformer().getCacheByRankAndKingdom(Rank.DOMAIN(), null);
-        }else if (!nodes.isEmpty()) {
-            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), findKingdomIdFromTreeIndex(nodes.iterator().next().getTaxon(), state)); //PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
-        }else if (nomenclaturalCode != null){
-            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
+        }else if (kingdomFk != null) {
+            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), kingdomFk);
+        }else if (taxonName.getNameType() != null){
+            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), PesiTransformer.nomenclaturalCode2Kingdom(taxonName.getNameType()));
         }else{
-                       logger.warn("No nomenclatural code defined for name " + taxonName.getUuid());
+                       logger.warn("No kingdom ID could be defined for name " + taxonName.getUuid());
                        return null;
                }
        }
 
-       /**
-     * @param taxonName
-     * @return
-     */
     private static List<TaxonNode> getTaxonNodes(TaxonName taxonName) {
         List<TaxonNode> result = new ArrayList<>();
         for (TaxonBase<?> tb:taxonName.getTaxonBases()){
@@ -1516,7 +1260,7 @@ public class PesiTaxonExport extends PesiExportBase {
             }else{
                 taxon = CdmBase.deproxy(tb, Synonym.class).getAcceptedTaxon();
             }
-            if (taxon != null){
+            if (isPesiTaxon(taxon)){
                 for (TaxonNode node : taxon.getTaxonNodes()){
                     result.add(node);
                 }
@@ -1525,21 +1269,33 @@ public class PesiTaxonExport extends PesiExportBase {
         return result;
     }
 
+//    @SuppressWarnings("unused")  //used by pure name mapper and by getRankFk
+    private static Integer getKingdomFk(TaxonName taxonName){
+        EnumSet<PesiSource> origin = getSources(taxonName);
+        if (origin.size() == 1 && origin.contains(PesiSource.EM)){
+            //maybe simply replace by
+            //return PesiTransformer.KINGDOM_PLANTAE;
+            return PesiTransformer.nomenclaturalCode2Kingdom(taxonName.getNameType());
+        }else{
+            logger.warn("getKingdomFk not yet implemented for non-EuroMed pure names");
+            return null;
+        }
+    }
+
     /**
-        * Returns the <code>DisplayName</code> attribute.
-        * @param taxon The {@link TaxonBase Taxon}.
-        * @return The <code>DisplayName</code> attribute.
-        * @see MethodMapper
-        */
-       @SuppressWarnings("unused")  //used by Mapper
-       private static String getDisplayName(TaxonBase<?> taxon) {
-               TaxonName taxonName = taxon.getName();
-               String result = getDisplayName(taxonName);
-               if (isMisappliedName(taxon)){
-                       result = result + " " + getAuthorString(taxon);
-               }
-               return result;
-       }
+     * Returns the rankFk for the taxon name based on the names nomenclatural code.
+     * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
+     */
+    @SuppressWarnings("unused")  //used by pure name mapper
+    private static Integer getRankFk(TaxonName taxonName) {
+        EnumSet<PesiSource> origin = getSources(taxonName);
+        if (origin.size() == 1 && origin.contains(PesiSource.EM)){
+            return getRankFk(taxonName, getKingdomFk(taxonName));
+        }else{
+            logger.warn("getRankFk not yet implemented for non-EuroMed pure names");
+            return null;
+        }
+    }
 
        /**
         * Returns the <code>AuthorString</code> attribute.
@@ -1550,61 +1306,87 @@ public class PesiTaxonExport extends PesiExportBase {
        //used by mapper
        protected static String getAuthorString(TaxonBase<?> taxon) {
                try {
-                       String result = null;
-                       boolean isNonViralName = false;
-                       String authorshipCache = null;
-                       TaxonName taxonName = taxon.getName();
-                       if (taxonName != null && taxonName.isNonViral()){
-                               authorshipCache = taxonName.getAuthorshipCache();
-                               isNonViralName = true;
-                       }
-                       result = authorshipCache;
-
-                       // For a misapplied names there are special rules
-                       if (isMisappliedName(taxon)){
-                               if (taxon.getSec() != null){
-                                       String secTitle = taxon.getSec().getTitleCache();
-                                       if (! secTitle.startsWith("auct")){
-                                               secTitle = "sensu " + secTitle;
-                                       }else if (secTitle.equals("auct")){  //may be removed once the title cache is generated correctly for references with title auct. #
-                                               secTitle = "auct.";
-                                       }
-                                       return secTitle;
-                               }else if (StringUtils.isBlank(authorshipCache)) {
-                                       // Set authorshipCache to "auct."
-                                       result = PesiTransformer.AUCT_STRING;
-                               }else{
-                                       result = PesiTransformer.AUCT_STRING;
-//                                     result = authorshipCache;
-                               }
-                       }
+                   // For misapplied names there are special rules
+            if (isMisappliedName(taxon)){
+                return getMisappliedNameAuthorship(taxon);
+            }else{
+                boolean isNonViralName = false;
+                String authorshipCache = null;
+                TaxonName taxonName = taxon.getName();
+                if (taxonName != null && taxonName.isNonViral()){
+                    authorshipCache = taxonName.getAuthorshipCache();
+                    isNonViralName = true;
+                }
+                String result = authorshipCache;
 
-                       if (taxonName == null){
-                               logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
-                       }else if (! isNonViralName){
-                               logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
-                       }
+                if (taxonName == null){
+                    logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
+                }else if (! isNonViralName){
+                    logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
+                }
 
-                       if (StringUtils.isBlank(result)) {
-                               return null;
-                       } else {
-                               return result;
-                       }
+                if (StringUtils.isBlank(result)) {
+                    return null;
+                } else {
+                    return result;
+                }
+            }
                } catch (Exception e) {
                        e.printStackTrace();
                        return null;
                }
        }
 
-       /**
-        * Returns the <code>DisplayName</code> attribute.
-        * @param taxonName The {@link TaxonNameBase TaxonName}.
-        * @return The <code>DisplayName</code> attribute.
-        * @see MethodMapper
-        */
-        //used by Mapper
-       private static String getDisplayName(TaxonName taxonName) {
-               // TODO: extension?
+       private static String getMisappliedNameAuthorship(TaxonBase<?> taxon){
+        String result;
+           String relAppendedPhrase = taxon.getAppendedPhrase();
+        Reference sec = taxon.getSec();
+        String secTitle = sec != null ? sec.getTitleCache(): null;
+        if(relAppendedPhrase == null && sec == null) {
+            result = "auct.";
+        }else if (relAppendedPhrase != null && sec == null){
+            result = relAppendedPhrase;
+        }else if (relAppendedPhrase == null && sec != null){
+            result = "sensu " + secTitle;
+        }else{  //append!=null && sec!=null
+            result = relAppendedPhrase + " " + secTitle;
+        }
+        String authorship = taxon.getName().getAuthorshipCache();
+        if (isNotBlank(authorship)){
+            result += ", non " + authorship;
+        }
+        return result;
+       }
+
+    /**
+     * Returns the <code>DisplayName</code> attribute.
+     * @param taxon The {@link TaxonBase Taxon}.
+     * @return The <code>DisplayName</code> attribute.
+     * @see MethodMapper
+     */
+    //used by Mapper
+    private static String getDisplayName(TaxonBase<?> taxon) {
+        boolean isMisapplied = isMisappliedName(taxon);
+        TaxonName taxonName = taxon.getName();
+        String result = getDisplayName(taxonName, isMisapplied);
+        if (isMisapplied){
+            result = result + " " + getMisappliedNameAuthorship(taxon);
+        }
+        return result;
+    }
+
+    /**
+     * Returns the <code>DisplayName</code> attribute.
+     * @param taxonName The {@link TaxonNameBase TaxonName}.
+     * @return The <code>DisplayName</code> attribute.
+     * @see MethodMapper
+     */
+    @SuppressWarnings("unused")  //used by Mapper
+    private static String getDisplayName(TaxonName taxonName) {
+        return getDisplayName(taxonName, false);
+    }
+
+       private static String getDisplayName(TaxonName taxonName, boolean useNameCache) {
                if (taxonName == null) {
                        return null;
                }else{
@@ -1615,63 +1397,93 @@ public class PesiTaxonExport extends PesiExportBase {
                                        addRule(TagEnum.nomStatus, "@status@");
 
                        String result;
-                       if (getSources(taxonName).get(PesiTransformer.SOURCE_ERMS)){
-                           result = cacheStrategy.getTitleCache(taxonName, tagRules);  //according to SQL script (also in ERMS sources are not abbreviated)
-                       }else if (getSources(taxonName).get(PesiTransformer.SOURCE_EM)){
-                           result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
+                       if (useNameCache){
+                result = cacheStrategy.getNameCache(taxonName, tagRules);
                        }else{
-                           //TODO define for FE + IF and for multiple sources
-                           result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
+                           EnumSet<PesiSource> sources = getSources(taxonName);
+                           if (sources.contains(PesiSource.ERMS)){
+                               result = cacheStrategy.getTitleCache(taxonName, tagRules);  //according to SQL script (also in ERMS sources are not abbreviated)
+                           }else if (sources.contains(PesiSource.FE) || sources.contains(PesiSource.IF)){
+                               //TODO define for FE + IF and for multiple sources
+                               result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
+                           }else if (sources.contains(PesiSource.EM)){
+                               result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
+                           }else{
+                               logger.warn("Source not yet handled");
+                               result = cacheStrategy.getTitleCache(taxonName, tagRules);
+                           }
+                           result = replaceTagForInfraSpecificMarkerForProtectedTitleCache(taxonName, result);
+                           result = result.replaceAll("(, ?)?\\<@status@\\>.*\\</@status@\\>", "").trim();
                        }
-                       return result.replaceAll(",?\\<@status@\\>.*\\</@status@\\>", "");
+            return result;
                }
        }
 
-       @SuppressWarnings("unused")
-       private static String getGUID(TaxonName taxonName) {
-               UUID uuid = taxonName.getUuid();
-               String result = "NameUUID:" + uuid.toString();
-               return result;
-       }
-
-
        /**
         * Returns the <code>WebShowName</code> attribute for a taxon.
-        * @param taxonName The {@link TaxonNameBase TaxonName}.
+        * See {@link #getWebShowName(TaxonName)} for further explanations.
+        * @param taxon The {@link TaxonBase taxon}.
         * @return The <code>WebShowName</code> attribute.
+        * @see #getWebShowName(TaxonName)
+        * @see #getDisplayName(TaxonBase)
+        * @see #getFullName(TaxonBase)
         * @see MethodMapper
        */
        @SuppressWarnings("unused")
        private static String getWebShowName(TaxonBase<?> taxon) {
-               TaxonName taxonName = taxon.getName();
-               String result = getWebShowName(taxonName);
-               if (isMisappliedName(taxon)){
-                       result = result + " " + getAuthorString(taxon);
-               }
-               return result;
+           if (isMisappliedName(taxon)){
+               //for misapplications the webshowname is the same as the displayname as they do not show the nom.ref. in displayname
+               return getDisplayName(taxon);
+           }else{
+               TaxonName taxonName = taxon.getName();
+               return getWebShowName(taxonName);
+           }
        }
 
        /**
-        * Returns the <code>WebShowName</code> attribute.
+        * Returns the <code>WebShowName</code> attribute for a name. The
+        * <code>WebShowName</code> is like fullName but with
+        * tagged (<i>) name part. It is also similar to
+        * <code>DisplayName</code> but for titleCache not fullTitleCache.
+        * For misapplications it slightly differs (see {@link #getWebShowName(TaxonBase)} )
+        *
         * @param taxonName The {@link TaxonNameBase TaxonName}.
         * @return The <code>WebShowName</code> attribute.
+        * @see #getDisplayName(TaxonName)
+        * @see #getFullName(TaxonName)
+        * @see #getWebShowName(TaxonBase)
         * @see MethodMapper
         */
        private static String getWebShowName(TaxonName taxonName) {
-               //TODO extensions?
                if (taxonName == null) {
                        return null;
                }else{
-                       INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
+                   taxonName = CdmBase.deproxy(taxonName);
+            INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
 
                        HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
                        String result = cacheStrategy.getTitleCache(taxonName, tagRules);
+                       result = replaceTagForInfraSpecificMarkerForProtectedTitleCache(taxonName, result);
                        return result;
                }
        }
 
+    private static String replaceTagForInfraSpecificMarkerForProtectedTitleCache(TaxonName taxonName, String result) {
+        if (taxonName.isProtectedTitleCache()||taxonName.isProtectedNameCache()){
+            if (!taxonName.isAutonym()){
+                result = result
+                        .replace(" subsp. ", "</i> subsp. <i>")
+                        .replace(" var. ", "</i> var. <i>")
+                        .replace(" subvar. ", "</i> subvar. <i>")
+                        .replace(" f. ", "</i> f. <i>")
+                        .replace(" subf. ", "</i> subf. <i>")  //does this exist?
+                        ;
+            }
+        }
+        return result;
+    }
 
-       /**
+    /**
         * Returns the <code>WebSearchName</code> attribute.
         * @param taxonName The {@link NonViralName NonViralName}.
         * @return The <code>WebSearchName</code> attribute.
@@ -1685,6 +1497,16 @@ public class PesiTaxonExport extends PesiExportBase {
                return result;
        }
 
+    @SuppressWarnings("unused")     //used by mapper
+    private static String getFullName(TaxonBase<?> taxon) {
+        if (isMisappliedName(taxon)){
+            String result = getCacheStrategy(taxon.getName()).getNameCache(taxon.getName());
+            result = result + " " + getMisappliedNameAuthorship(taxon);
+            return result;
+        }else{
+            return getFullName(taxon.getName());
+        }
+    }
 
        /**
         * Returns the <code>FullName</code> attribute.
@@ -1692,29 +1514,34 @@ public class PesiTaxonExport extends PesiExportBase {
         * @return The <code>FullName</code> attribute.
         * @see MethodMapper
         */
-       @SuppressWarnings("unused")
+    //used by mapper
        private static String getFullName(TaxonName taxonName) {
                //TODO extensions?
                String result = getCacheStrategy(taxonName).getTitleCache(taxonName);
-               Iterator<Taxon> taxa = taxonName.getTaxa().iterator();
-               if (taxonName.getTaxa().size() >0){
-                       if (taxonName.getTaxa().size() == 1){
-                               Taxon taxon = taxa.next();
-                               if (isMisappliedName(taxon)){
-                                       result = result + " " + getAuthorString(taxon);
-                               }
-                               taxon = null;
-                       }
-               }
+               //misapplied names are now handled differently in getFullName(TaxonBase)
+//             Iterator<Taxon> taxa = taxonName.getTaxa().iterator();
+//             if (taxonName.getTaxa().size() >0){
+//                     if (taxonName.getTaxa().size() == 1){
+//                             Taxon taxon = taxa.next();
+//                             if (isMisappliedName(taxon)){
+//                                     result = result + " " + getAuthorString(taxon);
+//                             }
+//                     }
+//             }
                return result;
        }
 
+    @SuppressWarnings("unused")
+    private static String getGUID(TaxonName taxonName) {
+        UUID uuid = taxonName.getUuid();
+        String result = "NameUUID:" + uuid.toString();
+        return result;
+    }
+
+    static boolean isFirstAbbrevTitle = true;
        /**
         * Returns the SourceNameCache for the AdditionalSource table
-        * @param taxonName
-        * @return
         */
-       static boolean isFirstAbbrevTitle = true;
        @SuppressWarnings("unused")
        private static String getSourceNameCache(TaxonName taxonName) {
                if (taxonName != null){
@@ -1731,32 +1558,10 @@ public class PesiTaxonExport extends PesiExportBase {
                return null;
        }
 
-
-
-       /**
-        * Returns the <code>FullName</code> attribute.
-        * @param taxon The {@link TaxonBase taxon}.
-        * @return The <code>FullName</code> attribute.
-        * @see MethodMapper
-        */
-       /*@SuppressWarnings("unused")
-       private static String getFullName(TaxonBase taxon) {
-               //TODO extensions?
-               TaxonNameBase name = taxon.getName();
-               String result = getFullName(name);
-               if (isMisappliedName(taxon)){
-                       result = result + " " + getAuthorString(taxon);
-               }
-
-               return result;
-       }
-*/
-
        /**
         * Returns the nomenclatural reference which is the reference
         * including the detail (microreference).
-        * @param taxonName The {@link TaxonNameBase TaxonName}.
-        * @return The <code>AuthorString</code> attribute.
+        * @param taxonName The {@link TaxonName taxon name}.
         * @see MethodMapper
         */
        @SuppressWarnings("unused")
@@ -1766,21 +1571,20 @@ public class PesiTaxonExport extends PesiExportBase {
                        return null;
                }
                String result = null;
-               BitSet sources = getSources(taxonName);
-               int len = sources.length();
-               if(sources.get(PesiTransformer.SOURCE_EM)){
+               EnumSet<PesiSource> sources = getSources(taxonName);
+               if(sources.contains(PesiSource.EM)){
                    if (! ref.isProtectedAbbrevTitleCache()){
                        ref.setAbbrevTitleCache(null, false);  //to remove a false cache
                    }
                    result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
-               }else if(sources.get(PesiTransformer.SOURCE_FE)||sources.get(PesiTransformer.SOURCE_IF) ){
+               }else if(sources.contains(PesiSource.FE)||sources.contains(PesiSource.IF) ){
             //TODO still need to check if correct for FE + IF
                    if (! ref.isProtectedAbbrevTitleCache()){
                 ref.setAbbrevTitleCache(null, false);  //to remove a false cache
             }
             result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
             return result;   // according to SQL script
-               }else if(sources.get(PesiTransformer.SOURCE_ERMS)) {
+               }else if(sources.contains(PesiSource.ERMS)) {
             //result = null; //according to SQL script
                }else{
                    logger.warn("Source not yet supported");
@@ -1788,7 +1592,6 @@ public class PesiTaxonExport extends PesiExportBase {
                return result;
        }
 
-
        /**
         * Returns the <code>NameStatusFk</code> attribute.
         * @param taxonName The {@link TaxonNameBase TaxonName}.
@@ -1799,9 +1602,9 @@ public class PesiTaxonExport extends PesiExportBase {
        private static Integer getNameStatusFk(TaxonName taxonName) {
                Integer result = null;
 
-               NomenclaturalStatus state = getNameStatus(taxonName);
-               if (state != null) {
-                       result = PesiTransformer.nomStatus2nomStatusFk(state.getType());
+               NomenclaturalStatus status = getNameStatus(taxonName);
+               if (status != null) {
+                       result = PesiTransformer.nomStatus2nomStatusFk(status.getType());
                }
                return result;
        }
@@ -1823,16 +1626,21 @@ public class PesiTaxonExport extends PesiExportBase {
                return result;
        }
 
-
        private static NomenclaturalStatus getNameStatus(TaxonName taxonName) {
                try {
                        if (taxonName != null) {
                            Set<NomenclaturalStatus> states = taxonName.getStatus();
-                               if (states.size() == 1) {
-                                       NomenclaturalStatus status = states.iterator().next();
+                               if (states.size() >= 1) {
+                                   if (states.size() > 1) {
+                                       String statusStr = null;
+                                       for (NomenclaturalStatus status: states){
+                                           statusStr = CdmUtils.concat(",", statusStr, status.getType()== null? null:status.getType().getTitleCache());
+                                       }
+                                       //a known case is ad43508a-8a10-480a-8519-2a76de2c0a0f (Labiatae Juss.) from E+M
+                           logger.warn("This TaxonName has more than one nomenclatural status. This may happen in very rare cases but is not handled by the PESI data warehouse. Taxon name: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")Status:" + statusStr);
+                       }
+                                   NomenclaturalStatus status = states.iterator().next();
                                        return status;
-                               } else if (states.size() > 1) {
-                                       logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
                                }
                        }
                } catch (Exception e) {
@@ -1840,6 +1648,7 @@ public class PesiTaxonExport extends PesiExportBase {
                }
                return null;
        }
+
        /**
         * Returns the <code>TaxonStatusFk</code> attribute.
         * @param taxonName The {@link TaxonNameBase TaxonName}.
@@ -1848,21 +1657,12 @@ public class PesiTaxonExport extends PesiExportBase {
         * @see MethodMapper
         */
        private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
-               Integer result = null;
-
                try {
-                       if (isMisappliedName(taxon)) {
-                               Synonym synonym = Synonym.NewInstance(null, null);
-
-                               // This works as long as only the instance is important to differentiate between TaxonStatus.
-                               result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
-                       } else {
-                               result = PesiTransformer.taxonBase2statusFk(taxon);
-                       }
+                       return PesiTransformer.taxonBase2statusFk(taxon);
                } catch (Exception e) {
                        e.printStackTrace();
+                       return null;
                }
-               return result;
        }
 
        /**
@@ -1878,6 +1678,18 @@ public class PesiTaxonExport extends PesiExportBase {
                return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
        }
 
+    /**
+     * Returns the <code>TaxonFk1</code> attribute. It corresponds to a CDM <code>TaxonRelationship</code>.
+     * @param relationship The {@link RelationshipBase Relationship}.
+     * @param state The {@link PesiExportState PesiExportState}.
+     * @return The <code>TaxonFk1</code> attribute.
+     * @see MethodMapper
+     */
+    @SuppressWarnings("unused")
+    private static Integer getSynonym(Synonym synonym, PesiExportState state) {
+         return state.getDbId(synonym);
+    }
+
        /**
         * Returns the <code>TypeNameFk</code> attribute.
         * @param taxonName The {@link TaxonNameBase TaxonName}.
@@ -1894,7 +1706,11 @@ public class PesiTaxonExport extends PesiExportBase {
                                if (nameTypeDesignation != null) {
                                        TaxonName typeName = nameTypeDesignation.getTypeName();
                                        if (typeName != null) {
-                                           result = state.getDbId(typeName);
+                                           if (typeName.getTaxonBases().isEmpty()){
+                                               logger.warn("Type name does not belong to a taxon and therefore is not expected to be a European taxon. Type name not added. Type name: " + typeName.getTitleCache() + ", typified name: " + taxonName.getTitleCache());
+                                           }else{
+                                               result = state.getDbId(typeName);
+                                           }
                                        }
                                }
                        } else if (nameTypeDesignations.size() > 1) {
@@ -1943,11 +1759,10 @@ public class PesiTaxonExport extends PesiExportBase {
         * @see MethodMapper
         */
        private static Integer getQualityStatusFk(TaxonName taxonName) {
-               BitSet sources = getSources(taxonName);
+           EnumSet<PesiSource> sources = getSources(taxonName);
                return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
        }
 
-
        /**
         * Returns the <code>QualityStatusCache</code> attribute.
         * @param taxonName The {@link TaxonNameBase TaxonName}.
@@ -1967,22 +1782,21 @@ public class PesiTaxonExport extends PesiExportBase {
         * @return The <code>TypeDesignationStatusFk</code> attribute.
         * @see MethodMapper
         */
-       @SuppressWarnings("unused")
+       //TODO seems not to be used
        private static Integer getTypeDesignationStatusFk(TaxonName taxonName) {
                Integer result = null;
 
                try {
-               if (taxonName != null) {
-                       Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
-                       if (typeDesignations.size() == 1) {
-                               Object obj = typeDesignations.iterator().next().getTypeStatus();
-                               NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
-                               result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
-                       } else if (typeDesignations.size() > 1) {
-                               logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
-                       }
-               }
-
+               if (taxonName != null) {
+                       Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
+                       if (typeDesignations.size() == 1) {
+                               Object obj = typeDesignations.iterator().next().getTypeStatus();
+                               NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
+                               result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
+                       } else if (typeDesignations.size() > 1) {
+                               logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
+                       }
+               }
                } catch (Exception e) {
                        e.printStackTrace();
                }
@@ -1995,22 +1809,21 @@ public class PesiTaxonExport extends PesiExportBase {
         * @return The <code>TypeDesignationStatusCache</code> attribute.
         * @see MethodMapper
         */
-       @SuppressWarnings("unused")
+       //TODO seems not to be used
        private static String getTypeDesignationStatusCache(TaxonName taxonName) {
                String result = null;
 
                try {
-               if (taxonName != null) {
-                       Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
-                       if (typeDesignations.size() == 1) {
-                               Object obj = typeDesignations.iterator().next().getTypeStatus();
-                               NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
-                               result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
-                       } else if (typeDesignations.size() > 1) {
-                               logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
-                       }
-               }
-
+               if (taxonName != null) {
+                       Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
+                       if (typeDesignations.size() == 1) {
+                               Object obj = typeDesignations.iterator().next().getTypeStatus();
+                               NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
+                               result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
+                       } else if (typeDesignations.size() > 1) {
+                               logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
+                       }
+               }
                } catch (Exception e) {
                        e.printStackTrace();
                }
@@ -2065,7 +1878,7 @@ public class PesiTaxonExport extends PesiExportBase {
         * @see MethodMapper
         */
        @SuppressWarnings("unused")
-       private static String getIdInSource(IdentifiableEntity taxonName) {
+       private static String getIdInSource(IdentifiableEntity<?> taxonName) {
                String result = null;
 
                try {
@@ -2080,7 +1893,7 @@ public class PesiTaxonExport extends PesiExportBase {
                                Reference ref = source.getCitation();
                                UUID refUuid = ref.getUuid();
                                String idInSource = source.getIdInSource();
-                               if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
+                               if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed)){
                                        result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
                                }else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
                                        result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
@@ -2089,7 +1902,7 @@ public class PesiTaxonExport extends PesiExportBase {
                                }else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
                                        result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
                                }else{
-                                       if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
+                                       if (logger.isDebugEnabled()){logger.debug("Not a PESI source");}
                                }
 
                                String sourceIdNameSpace = source.getIdNamespace();
@@ -2124,7 +1937,7 @@ public class PesiTaxonExport extends PesiExportBase {
         * @param taxonName The {@link TaxonNameBase TaxonName}.
         * @return The idInSource.
         */
-       private static String getIdInSourceOnly(IdentifiableEntity identEntity) {
+       private static String getIdInSourceOnly(IdentifiableEntity<?> identEntity) {
                String result = null;
 
                // Get the sources first
@@ -2200,18 +2013,44 @@ public class PesiTaxonExport extends PesiExportBase {
                String result = "";
                //TODO implement anew for taxa
                try {
-                       BitSet sources = getSources(taxon);
+                       EnumSet<PesiSource> sources = getSources(taxon);
+                       //TODO what if 2 sources? In PESI 2014 they were pipe separated
+                       //TODO why does ERMS use accessed through eu-nomen, while E+M uses accessed through E+M
                        if (sources.isEmpty()) {
 //                             logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
-                       } else if (sources.get(PesiTransformer.SOURCE_ERMS)) {
-                               Set<Extension> extensions = taxon.getExtensions();
+                       } else if (sources.contains(PesiSource.ERMS)) {
+                               //TODO check if correct, compare with PESI 2014
+                           Set<Extension> extensions = taxon.getExtensions();
                                for (Extension extension : extensions) {
                                        if (extension.getType().equals(cacheCitationExtensionType)) {
                                                result = extension.getValue();
                                        }
                                }
+                       } else if (sources.contains(PesiSource.EM)) {
+                           //TODO
+                           boolean isMisapplied = isMisappliedName(taxon);
+                           boolean isProParteSyn = isProParteOrPartialSynonym(taxon);
+                           Reference sec = null;
+                           if(!isMisapplied && !isProParteSyn){
+                               sec = taxon.getSec();
+                           }else if (isMisapplied){
+                               sec = getAcceptedTaxonForMisappliedName(taxon).getSec();
+                           }else if (isProParteSyn){
+                    sec = getAcceptedTaxonForProParteSynonym(taxon).getSec();
+                }
+                           if (sec == null){
+                               logger.warn("Sec could not be defined for taxon " + taxon.getTitleCache()+ "; " + taxon.getUuid());
+                           }
+                           String author = sec == null? "" : sec.getTitleCache();
+                           String webShowName = isMisapplied? getDisplayName(taxon):getWebShowName(taxonName);  //for misapplied we need also the sensu and non author part, for ordinary names name + author is enough
+                           String accessed = ". Accessed through: Euro+Med PlantBase at https://www.europlusmed.org/cdm_dataportal/taxon/";
+                           result = CdmUtils.removeTrailingDot(author)
+                                   + ". " + CdmUtils.removeTrailingDot(webShowName)
+                                   + accessed + taxon.getUuid();
                        } else {
-                               String expertName = getExpertName(taxon);
+                               //TODO check for IF + FE
+
+                           String expertName = getExpertName(taxon);
                                String webShowName = getWebShowName(taxonName);
 
                                // idInSource only
@@ -2258,89 +2097,41 @@ public class PesiTaxonExport extends PesiExportBase {
         * @return The <code>OriginalDB</code> attribute.
         * @see MethodMapper
         */
-       @SuppressWarnings("unused")
-       private static String getOriginalDB(IdentifiableEntity identifiableEntity) {
-               BitSet sources  = getSources(identifiableEntity);
+//     @SuppressWarnings("unused")
+       private static String getOriginalDB(IdentifiableEntity<?> identifiableEntity) {
+               EnumSet<PesiSource> sources  = getSources(identifiableEntity);
                return PesiTransformer.getOriginalDbBySources(sources);
        }
 
-       /**
-        * Returns the <code>LastAction</code> attribute.
-        * @param taxonName The {@link TaxonNameBase TaxonName}.
-        * @return The <code>LastAction</code> attribute.
-        * @see MethodMapper
-        */
-       @SuppressWarnings("unused")
-       private static String getLastAction(IdentifiableEntity<?> identEntity) {
-               String result = null;
-               try {
-               Set<Extension> extensions = identEntity.getExtensions();
-               for (Extension extension : extensions) {
-                       if (extension.getType().equals(lastActionExtensionType)) {
-                               result = extension.getValue();
-                       }
-               }
-               } catch (Exception e) {
-                       e.printStackTrace();
-               }
-               return result;
-       }
-
-       /**
-        * Returns the <code>LastActionDate</code> attribute.
-        * @param taxonName The {@link TaxonNameBase TaxonName}.
-        * @return The <code>LastActionDate</code> attribute.
-        * @see MethodMapper
-        */
-       @SuppressWarnings({ "unused" })
-       private static DateTime getLastActionDate(IdentifiableEntity identEntity) {
-               DateTime result = null;
-               try {
-                       Set<Extension> extensions = identEntity.getExtensions();
-                       for (Extension extension : extensions) {
-                               if (extension.getType().equals(lastActionDateExtensionType)) {
-                                       String dateTime = extension.getValue();
-                                       if (dateTime != null) {
-                                               DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
-                                               result = formatter.parseDateTime(dateTime);
-                                       }
-                               }
-                       }
-               } catch (Exception e) {
-                       e.printStackTrace();
-               }
-               return result;
-       }
-
        /**
         * Returns the <code>ExpertName</code> attribute.
         * @param taxonName The {@link TaxonNameBase TaxonName}.
         * @return The <code>ExpertName</code> attribute.
         * @see MethodMapper
         */
-       @SuppressWarnings("unused")
-       private static String getExpertName(TaxonBase<?> taxonName) {
-               String result = null;
+       @SuppressWarnings("unused")  //for some reason it is also called by getCacheCitation
+       private static String getExpertName(TaxonBase<?> taxon) {
                try {
-               Set<Extension> extensions = taxonName.getExtensions();
-               for (Extension extension : extensions) {
-                       if (extension.getType().equals(expertNameExtensionType)) {
-                               result = extension.getValue();
-                       }
-               }
+                   String result = null;
+               if(expertNameExtensionType!=null){  //some databases do not have this extension type
+                   Set<Extension> extensions = taxon.getExtensions();
+                   for (Extension extension : extensions) {
+                       if (extension.getType().equals(expertNameExtensionType)) {
+                           result = extension.getValue();
+                       }
+                   }
+               }
+               if (getPesiSources(taxon).contains(PesiSource.EM)){
+                return taxon.getSec().getTitleCache();
+            }
+            return null;
                } catch (Exception e) {
                        e.printStackTrace();
+                       return null;
                }
-               return result;
        }
 
-       /**
-        * Returns the <code>ExpertFk</code> attribute.
-        * @param taxonName The {@link TaxonNameBase TaxonName}.
-        * @param state The {@link PesiExportState PesiExportState}.
-        * @return The <code>ExpertFk</code> attribute.
-        * @see MethodMapper
-        */
+       //TODO change to ExpertGUID
        private static Integer getExpertFk(Reference reference, PesiExportState state) {
                Integer result = state.getDbId(reference);
                return result;
@@ -2353,19 +2144,24 @@ public class PesiTaxonExport extends PesiExportBase {
         * @see MethodMapper
         */
        @SuppressWarnings("unused")
-       private static String getSpeciesExpertName(TaxonBase<?> taxonName) {
-               String result = null;
+       private static String getSpeciesExpertName(TaxonBase<?> taxon) {
                try {
-               Set<Extension> extensions = taxonName.getExtensions();
-               for (Extension extension : extensions) {
-                       if (extension.getType().equals(speciesExpertNameExtensionType)) {
-                               result = extension.getValue();
-                       }
-               }
+               Set<Extension> extensions = taxon.getExtensions();
+               if(speciesExpertNameExtensionType != null){  //some databases do not have this extension type
+                for (Extension extension : extensions) {
+                               if (extension.getType().equals(speciesExpertNameExtensionType)) {
+                                       return extension.getValue();
+                               }
+                       }
+               }
+               if (getPesiSources(taxon).contains(PesiSource.EM)){
+                   return taxon.getSec().getTitleCache();
+            }
+               return null;
                } catch (Exception e) {
                        e.printStackTrace();
+                       return null;
                }
-               return result;
        }
 
        /**
@@ -2375,6 +2171,7 @@ public class PesiTaxonExport extends PesiExportBase {
         * @return The <code>SpeciesExpertFk</code> attribute.
         * @see MethodMapper
         */
+       //TODO should be changed to SpeciesExpertGUID
        private static Integer getSpeciesExpertFk(Reference reference, PesiExportState state) {
                Integer result = state.getDbId(reference);
                return result;
@@ -2408,7 +2205,8 @@ public class PesiTaxonExport extends PesiExportBase {
        private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
                return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
        }
-    @SuppressWarnings("unused")
+
+    //TODO still in use?
     private static String getSynonymTypeCache(Synonym synonym, PesiExportState state) {
         String result = null;
         NomenclaturalCode code = null;
@@ -2422,6 +2220,7 @@ public class PesiTaxonExport extends PesiExportBase {
         return result;
     }
 
+// ********************************** MAPPINGS ********************************/
 
        /**
         * Returns the CDM to PESI specific export mappings.
@@ -2439,7 +2238,8 @@ public class PesiTaxonExport extends PesiExportBase {
 
                mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
                mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
-               mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misapllied Names are handled differently
+               mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because misapplied names are handled differently
+               mapping.addMapper(MethodMapper.NewInstance("FullName", this));    //For Taxon because misapplied names are handled differently
                mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
 
                // DisplayName
@@ -2454,13 +2254,13 @@ public class PesiTaxonExport extends PesiExportBase {
                mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
 
                //experts
-               ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtSpeciesExpertName);
-               mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
+//             mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
+               mapping.addMapper(MethodMapper.NewInstance("SpeciesExpertName", this, TaxonBase.class));
+//             ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
+//             mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
+               mapping.addMapper(MethodMapper.NewInstance("ExpertName", this, TaxonBase.class));
 
-               ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
-               mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
-
-//             mapping.addMapper(MethodMapper.NewInstance("ParentTaxonFk", this, TaxonBase.class, PesiExportState.class));  //by AM, doesn't work, FK exception
+               //ParentTaxonFk handled in Phase02 now
                mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
 
                addNameMappers(mapping);
@@ -2479,14 +2279,13 @@ public class PesiTaxonExport extends PesiExportBase {
 
                mapping.addMapper(IdMapper.NewInstance("TaxonId"));
 
-               //              mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
-
                mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonName.class));
                mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonName.class));
                mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonName.class, PesiExportState.class));
-               mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
-               mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
+               mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER, PesiTransformer.T_STATUS_UNACCEPTED));
+               mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR, state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
                mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
+               mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonName.class));
                mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonName.class));
                mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonName.class));
 
@@ -2497,36 +2296,35 @@ public class PesiTaxonExport extends PesiExportBase {
                mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
 
                addNameMappers(mapping);
-               //TODO add author mapper, TypeNameFk
-
                return mapping;
        }
 
-
        private void addNameMappers(PesiExportMapping mapping) {
+
+           //epithets
                mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
                mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
                mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
                mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
 
+               //full name
 //             mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
                mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonName.class));
 
-//             mapping.addMapper(DbStringMapper.NewInstance("TitleCache", "FullName"));    //does not work as we need other cache strategy
-               mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonName.class));
-
-
+               //nom ref
                mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonName.class));
 
+               //status
                mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonName.class));
                mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonName.class, PesiExportState.class));
-               mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
-               //TODO TypeNameFk
-
-               //quality status
                mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonName.class));
                mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonName.class, PesiExportState.class));
 
+               //types
+               mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
+               //TypeNameFk handled in Phase3
+
+               //supplemental
                mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
                mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
 
@@ -2534,19 +2332,6 @@ public class PesiTaxonExport extends PesiExportBase {
 
        }
 
-    /**
-     * Returns the <code>TaxonFk1</code> attribute. It corresponds to a CDM <code>TaxonRelationship</code>.
-     * @param relationship The {@link RelationshipBase Relationship}.
-     * @param state The {@link PesiExportState PesiExportState}.
-     * @return The <code>TaxonFk1</code> attribute.
-     * @see MethodMapper
-     */
-    @SuppressWarnings("unused")
-    private static Integer getSynonym(Synonym synonym, PesiExportState state) {
-         return state.getDbId(synonym);
-    }
-
-
        private PesiExportMapping getSynRelMapping() {
                PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
                logger.warn("SynRelMapping currently not implemented. Needs to be checked");