View Javadoc

1   // $Id$
2   /**
3   * Copyright (C) 2009 EDIT
4   * European Distributed Institute of Taxonomy 
5   * http://www.e-taxonomy.eu
6   * 
7   * The contents of this file are subject to the Mozilla Public License Version 1.1
8   * See LICENSE.TXT at the top of this package for the full license terms.
9   */
10  package eu.etaxonomy.cdm.io.pesi.out;
11  
12  import java.sql.Connection;
13  import java.sql.PreparedStatement;
14  import java.sql.ResultSet;
15  import java.sql.SQLException;
16  import java.util.ArrayList;
17  import java.util.Date;
18  import java.util.HashMap;
19  import java.util.List;
20  import java.util.Set;
21  import java.util.UUID;
22  
23  import org.apache.log4j.Logger;
24  import org.joda.time.DateTime;
25  import org.joda.time.format.DateTimeFormat;
26  import org.joda.time.format.DateTimeFormatter;
27  import org.springframework.stereotype.Component;
28  import org.springframework.transaction.TransactionStatus;
29  
30  import eu.etaxonomy.cdm.common.CdmUtils;
31  import eu.etaxonomy.cdm.io.berlinModel.out.mapper.DbExtensionMapper;
32  import eu.etaxonomy.cdm.io.berlinModel.out.mapper.IdMapper;
33  import eu.etaxonomy.cdm.io.berlinModel.out.mapper.MethodMapper;
34  import eu.etaxonomy.cdm.io.common.Source;
35  import eu.etaxonomy.cdm.io.erms.ErmsTransformer;
36  import eu.etaxonomy.cdm.model.agent.Team;
37  import eu.etaxonomy.cdm.model.common.Annotation;
38  import eu.etaxonomy.cdm.model.common.AnnotationType;
39  import eu.etaxonomy.cdm.model.common.CdmBase;
40  import eu.etaxonomy.cdm.model.common.Extension;
41  import eu.etaxonomy.cdm.model.common.ExtensionType;
42  import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
43  import eu.etaxonomy.cdm.model.common.IdentifiableSource;
44  import eu.etaxonomy.cdm.model.common.Language;
45  import eu.etaxonomy.cdm.model.name.NameRelationship;
46  import eu.etaxonomy.cdm.model.name.NameRelationshipType;
47  import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
48  import eu.etaxonomy.cdm.model.name.NameTypeDesignationStatus;
49  import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
50  import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
51  import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
52  import eu.etaxonomy.cdm.model.name.NonViralName;
53  import eu.etaxonomy.cdm.model.name.Rank;
54  import eu.etaxonomy.cdm.model.name.TaxonNameBase;
55  import eu.etaxonomy.cdm.model.name.ZoologicalName;
56  import eu.etaxonomy.cdm.model.reference.ReferenceBase;
57  import eu.etaxonomy.cdm.model.taxon.Synonym;
58  import eu.etaxonomy.cdm.model.taxon.Taxon;
59  import eu.etaxonomy.cdm.model.taxon.TaxonBase;
60  import eu.etaxonomy.cdm.model.taxon.TaxonNode;
61  import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
62  import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
63  import eu.etaxonomy.cdm.model.taxon.TaxonomicTree;
64  import eu.etaxonomy.cdm.persistence.query.MatchMode;
65  
66  /**
67   * The export class for {@link eu.etaxonomy.cdm.model.name.TaxonNameBase TaxonNames}.<p>
68   * Inserts into DataWarehouse database table <code>Taxon</code>.
69   * It is divided into four phases:<p><ul>
70   * <li>Phase 1:	Export of all {@link eu.etaxonomy.cdm.model.name.TaxonNameBase TaxonNames} except some data exported in the following phases.
71   * <li>Phase 2:	Export of additional data: ParenTaxonFk and TreeIndex.
72   * <li>Phase 3:	Export of additional data: Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk.
73   * <li>Phase 4:	Export of Inferred Synonyms.</ul>
74   * @author e.-m.lee
75   * @date 23.02.2010
76   *
77   */
78  @Component
79  @SuppressWarnings("unchecked")
80  public class PesiTaxonExport extends PesiExportBase {
81  	private static final Logger logger = Logger.getLogger(PesiTaxonExport.class);
82  	private static final Class<? extends CdmBase> standardMethodParameter = TaxonNameBase.class;
83  
84  	private static int modCount = 1000;
85  	private static final String dbTableName = "Taxon";
86  	private static final String pluralString = "Taxa";
87  	private static final String parentPluralString = "Taxa";
88  	private PreparedStatement parentTaxonFk_TreeIndex_KingdomFkStmt;
89  	private PreparedStatement sqlStmt;
90  	private PreparedStatement rankSqlStmt;
91  	private NomenclaturalCode nomenclaturalCode;
92  	private Integer kingdomFk;
93  	private HashMap<Rank, Rank> rankMap = new HashMap<Rank, Rank>();
94  	private List<Rank> rankList = new ArrayList<Rank>();
95  	private static final UUID uuidTreeIndex = UUID.fromString("28f4e205-1d02-4d3a-8288-775ea8413009");
96  	private AnnotationType treeIndexAnnotationType;
97  	private static ExtensionType lastActionExtensionType;
98  	private static ExtensionType lastActionDateExtensionType;
99  	private static ExtensionType expertNameExtensionType;
100 	private static ExtensionType speciesExpertNameExtensionType;
101 	private static ExtensionType cacheCitationExtensionType;
102 	private static ExtensionType expertUserIdExtensionType;
103 	private static ExtensionType speciesExpertUserIdExtensionType;
104 	
105 	/**
106 	 * @return the treeIndexAnnotationType
107 	 */
108 	protected AnnotationType getTreeIndexAnnotationType() {
109 		return treeIndexAnnotationType;
110 	}
111 
112 	/**
113 	 * @param treeIndexAnnotationType the treeIndexAnnotationType to set
114 	 */
115 	protected void setTreeIndexAnnotationType(AnnotationType treeIndexAnnotationType) {
116 		this.treeIndexAnnotationType = treeIndexAnnotationType;
117 	}
118 
119 	enum NamePosition {
120 		beginning,
121 		end,
122 		between,
123 		alone,
124 		nowhere
125 	}
126 
127 	public PesiTaxonExport() {
128 		super();
129 	}
130 
131 	/* (non-Javadoc)
132 	 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getStandardMethodParameter()
133 	 */
134 	@Override
135 	public Class<? extends CdmBase> getStandardMethodParameter() {
136 		return standardMethodParameter;
137 	}
138 
139 	/* (non-Javadoc)
140 	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
141 	 */
142 	@Override
143 	protected boolean doCheck(PesiExportState state) {
144 		boolean result = true;
145 		return result;
146 	}
147 
148 	/* (non-Javadoc)
149 	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
150 	 */
151 	@Override
152 	protected boolean doInvoke(PesiExportState state) {
153 		try {
154 			logger.error("*** Started Making " + pluralString + " ...");
155 
156 			// Prepare TreeIndex-And-KingdomFk-Statement
157 			Connection connection = state.getConfig().getDestination().getConnection();
158 			String parentTaxonFk_TreeIndex_KingdomFkSql = "UPDATE Taxon SET ParentTaxonFk = ?, TreeIndex = ? WHERE TaxonId = ?"; 
159 			parentTaxonFk_TreeIndex_KingdomFkStmt = connection.prepareStatement(parentTaxonFk_TreeIndex_KingdomFkSql);
160 			
161 			String sql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ?, " +
162 					"ExpertFk = ?, SpeciesExpertFk = ? WHERE TaxonId = ?";
163 			sqlStmt = connection.prepareStatement(sql);
164 			
165 			String rankSql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, KingdomFk = ? WHERE TaxonId = ?";
166 			rankSqlStmt = connection.prepareStatement(rankSql);
167 			
168 			// Get the limit for objects to save within a single transaction.
169 			int limit = state.getConfig().getLimitSave();
170 
171 			// Stores whether this invoke was successful or not.
172 			boolean success = true;
173 	
174 			// PESI: Clear the database table Taxon.
175 			doDelete(state);
176 			
177 			// Get specific mappings: (CDM) Taxon -> (PESI) Taxon
178 			PesiExportMapping mapping = getMapping();
179 	
180 			// Initialize the db mapper
181 			mapping.initialize(state);
182 
183 			// Find extensionTypes
184 			lastActionExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionUuid);
185 			lastActionDateExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionDateUuid);
186 			expertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
187 			speciesExpertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
188 			cacheCitationExtensionType = (ExtensionType)getTermService().find(PesiTransformer.cacheCitationUuid);
189 			expertUserIdExtensionType = (ExtensionType)getTermService().find(PesiTransformer.expertUserIdUuid);
190 			speciesExpertUserIdExtensionType = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertUserIdUuid);
191 
192 			int count = 0;
193 			int pastCount = 0;
194 			TransactionStatus txStatus = null;
195 			List<TaxonNameBase> list = null;
196 			
197 			logger.error("PHASE 1: Export Taxa...");
198 			// Start transaction
199 			txStatus = startTransaction(true);
200 			logger.error("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
201 			while ((list = getNameService().list(null, limit, count, null, null)).size() > 0) {
202 
203 				logger.error("Fetched " + list.size() + " " + pluralString + ". Exporting...");
204 				for (TaxonNameBase taxonName : list) {
205 					doCount(count++, modCount, pluralString);
206 					success &= mapping.invoke(taxonName);
207 					
208 					// Check whether some rules are violated
209 					nomenclaturalCode = PesiTransformer.getNomenclaturalCode(taxonName);
210 					String genusOrUninomial = getGenusOrUninomial(taxonName);
211 					String specificEpithet = getSpecificEpithet(taxonName);
212 					String infraSpecificEpithet = getInfraSpecificEpithet(taxonName);
213 					String infraGenericEpithet = getInfraGenericEpithet(taxonName);
214 					Integer rank = getRankFk(taxonName, nomenclaturalCode);
215 					
216 					if (rank == null) {
217 						logger.error("Rank was not determined: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
218 					} else {
219 						
220 						// Check whether infraGenericEpithet is set correctly
221 						// 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
222 						// 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
223 						
224 						int ancestorLevel = 0;
225 						if (taxonName.getRank().equals(Rank.SUBSPECIES())) {
226 							// The accepted taxon two rank levels above should be of rank subgenus
227 							ancestorLevel  = 2;
228 						}
229 						if (taxonName.getRank().equals(Rank.SPECIES())) {
230 							// The accepted taxon one rank level above should be of rank subgenus
231 							ancestorLevel = 1;
232 						}
233 						if (ancestorLevel > 0) {
234 							if (ancestorOfSpecificRank(taxonName, ancestorLevel, Rank.SUBGENUS())) {
235 								// The child (species or subspecies) of this parent (subgenus) has to have an infraGenericEpithet
236 								if (infraGenericEpithet == null) {
237 									logger.error("InfraGenericEpithet does not exist even though it should for: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
238 									// maybe the taxon could be named here
239 								}
240 							}
241 						}
242 						
243 						if (infraGenericEpithet == null && rank.intValue() == 190) {
244 							logger.error("InfraGenericEpithet was not determined although it should exist for rank 190: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
245 						}
246 						if (specificEpithet != null && rank.intValue() < 220) {
247 							logger.error("SpecificEpithet was determined for rank " + rank + " although it should only exist for ranks higher or equal to 220: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
248 						}
249 						if (infraSpecificEpithet != null && rank.intValue() < 230) {
250 							logger.error("InfraSpecificEpithet was determined for rank " + rank + " although it should only exist for ranks higher or equal to 230: "  + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
251 						}
252 					}
253 					if (infraSpecificEpithet != null && specificEpithet == null) {
254 						logger.error("An infraSpecificEpithet was determined, but a specificEpithet was not determined: "  + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
255 					}
256 					if (genusOrUninomial == null) {
257 						logger.error("GenusOrUninomial was not determined: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
258 					}
259 					
260 				}
261 
262 				// Commit transaction
263 				commitTransaction(txStatus);
264 				logger.error("Committed transaction.");
265 				logger.error("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
266 				pastCount = count;
267 
268 				// Start transaction
269 				txStatus = startTransaction(true);
270 				logger.error("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
271 			}
272 			if (list.size() == 0) {
273 				logger.error("No " + pluralString + " left to fetch.");
274 			}
275 			// Commit transaction
276 			commitTransaction(txStatus);
277 			logger.error("Committed transaction.");
278 
279 			count = 0;
280 			pastCount = 0;
281 			List<TaxonomicTree> taxonomicTreeList = null;
282 			// 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
283 			logger.error("PHASE 2: Add ParenTaxonFk and TreeIndex...");
284 			
285 			// Specify starting ranks for tree traversing
286 			rankList.add(Rank.KINGDOM());
287 			rankList.add(Rank.GENUS());
288 
289 			// Specify where to stop traversing (value) when starting at a specific Rank (key)
290 			rankMap.put(Rank.GENUS(), null); // Since NULL does not match an existing Rank, traverse all the way down to the leaves
291 			rankMap.put(Rank.KINGDOM(), Rank.GENUS()); // excludes rank genus
292 			
293 			StringBuffer treeIndex = new StringBuffer();
294 			
295 			// Retrieve list of Taxonomic Trees
296 			txStatus = startTransaction(true);
297 			logger.error("Started transaction. Fetching all Taxonomic Trees...");
298 			taxonomicTreeList = getTaxonTreeService().listTaxonomicTrees(null, 0, null, null);
299 			commitTransaction(txStatus);
300 			logger.error("Committed transaction.");
301 
302 			logger.error("Fetched " + taxonomicTreeList.size() + " Taxonomic Tree.");
303 
304 			setTreeIndexAnnotationType(getAnnotationType(uuidTreeIndex, "TreeIndex", "", "TI"));
305 			
306 			for (TaxonomicTree taxonomicTree : taxonomicTreeList) {
307 				for (Rank rank : rankList) {
308 					
309 					txStatus = startTransaction(true);
310 					logger.error("Started transaction to fetch all rootNodes specific to Rank " + rank.getLabel() + " ...");
311 
312 					List<TaxonNode> rankSpecificRootNodes = getTaxonTreeService().loadRankSpecificRootNodes(taxonomicTree, rank, null);
313 					logger.error("Fetched " + rankSpecificRootNodes.size() + " RootNodes for Rank " + rank.getLabel());
314 
315 					commitTransaction(txStatus);
316 					logger.error("Committed transaction.");
317 
318 					for (TaxonNode rootNode : rankSpecificRootNodes) {
319 						txStatus = startTransaction(false);
320 						Rank endRank = rankMap.get(rank);
321 						if (endRank != null) {
322 							logger.error("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till Rank " + endRank.getLabel() + " ...");
323 						} else {
324 							logger.error("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till leaves are reached ...");
325 						}
326 
327 						TaxonNode newNode = getTaxonNodeService().load(rootNode.getUuid());
328 
329 						TaxonNode parentNode = newNode.getParent();
330 						if (rank.equals(Rank.KINGDOM())) {
331 							treeIndex = new StringBuffer();
332 							treeIndex.append("#");
333 						} else {
334 							// Get treeIndex from parentNode
335 							if (parentNode != null) {
336 								boolean annotationFound = false;
337 								Set<Annotation> annotations = parentNode.getAnnotations();
338 								for (Annotation annotation : annotations) {
339 									AnnotationType annotationType = annotation.getAnnotationType();
340 									if (annotationType != null && annotationType.equals(getTreeIndexAnnotationType())) {
341 										treeIndex = new StringBuffer(CdmUtils.Nz(annotation.getText()));
342 										annotationFound = true;
343 //										logger.error("treeIndex: " + treeIndex);
344 										break;
345 									}
346 								}
347 								if (!annotationFound) {
348 									// This should not happen because it means that the treeIndex was not set correctly as an annotation to parentNode
349 									logger.error("TreeIndex could not be read from annotation of this TaxonNode: " + parentNode.getUuid());
350 									treeIndex = new StringBuffer();
351 									treeIndex.append("#");
352 								}
353 							} else {
354 								// TreeIndex could not be determined, but it's unclear how to proceed to generate a correct treeIndex if the parentNode is NULL
355 								logger.error("ParentNode for RootNode is NULL. TreeIndex could not be determined: " + newNode.getUuid());
356 								treeIndex = new StringBuffer(); // This just prevents growing of the treeIndex in a wrong manner
357 								treeIndex.append("#");
358 							}
359 						}
360 						
361 						nomenclaturalCode = PesiTransformer.getNomenclaturalCode(newNode.getTaxon().getName());
362 						kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
363 						traverseTree(newNode, parentNode, treeIndex, rankMap.get(rank), state);
364 						
365 						commitTransaction(txStatus);
366 						logger.error("Committed transaction.");
367 
368 					}
369 				}
370 			}
371 
372 			logger.error("PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
373 			// Be sure to add rank information, KingdomFk, TypeNameFk, expertFk and speciesExpertFk to every taxonName
374 			
375 			// Start transaction
376 			List<ReferenceBase> referenceList;
377 			txStatus = startTransaction(true);
378 			logger.error("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
379 			while ((list = getNameService().list(null, limit, count, null, null)).size() > 0) {
380 
381 				logger.error("Fetched " + list.size() + " " + pluralString + ". Exporting...");
382 				for (TaxonNameBase taxonName : list) {
383 
384 					// Determine expertFk
385 					Integer expertFk = null;
386 					String expertUserId = getExpertUserId(taxonName);
387 					if (expertUserId != null) {
388 
389 						// The expertUserId was stored in the field 'title' of the corresponding Reference during FaEu import
390 						referenceList = getReferenceService().listByReferenceTitle(null, expertUserId, MatchMode.EXACT, null, null, null, null, null);
391 						if (referenceList.size() == 1) {
392 							expertFk  = getExpertFk(referenceList.iterator().next(), state);
393 						} else if (referenceList.size() > 1) {
394 							logger.error("Found more than one match using listByReferenceTitle() searching for a Reference with this expertUserId as title: " + expertUserId);
395 						} else if (referenceList.size() == 0) {
396 							logger.error("Found no match using listByReferenceTitle() searching for a Reference with this expertUserId as title: " + expertUserId);
397 						}
398 					} else {
399 						logger.error("ExpertName is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
400 					}
401 
402 					// Determine speciesExpertFk
403 					Integer speciesExpertFk = null;
404 					String speciesExpertUserId = getSpeciesExpertUserId(taxonName);
405 					if (speciesExpertUserId != null) {
406 						
407 						// The speciesExpertUserId was stored in the field 'title' of the corresponding Reference during FaEu import
408 						referenceList = getReferenceService().listByReferenceTitle(null, speciesExpertUserId, MatchMode.EXACT, null, null, null, null, null);
409 						if (referenceList.size() == 1) {
410 							speciesExpertFk  = getSpeciesExpertFk(referenceList.iterator().next(), state);
411 						} else if (referenceList.size() > 1) {
412 							logger.error("Found more than one match using listByTitle() searching for a Reference with this speciesExpertUserId as title: " + speciesExpertUserId);
413 						} else if (referenceList.size() == 0) {
414 							logger.error("Found no match using listByReferenceTitle() searching for a Reference with this speciesExpertUserId as title: " + speciesExpertUserId);
415 						}
416 					} else {
417 						logger.error("SpeciesExpertName is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
418 					}
419 
420 
421 					doCount(count++, modCount, pluralString);
422 					Integer typeNameFk = getTypeNameFk(taxonName, state);
423 					if (expertFk != null || speciesExpertFk != null) {
424 						invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, nomenclaturalCode, state.getDbId(taxonName), typeNameFk, kingdomFk,
425 								expertFk, speciesExpertFk);
426 					}
427 				}
428 
429 				// Commit transaction
430 				commitTransaction(txStatus);
431 				logger.error("Committed transaction.");
432 				logger.error("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
433 				pastCount = count;
434 
435 				// Start transaction
436 				txStatus = startTransaction(true);
437 				logger.error("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
438 			}
439 			if (list.size() == 0) {
440 				logger.error("No " + pluralString + " left to fetch.");
441 			}
442 			// Commit transaction
443 			commitTransaction(txStatus);
444 			logger.error("Committed transaction.");
445 			
446 			
447 			// Create inferred synonyms for accepted taxa
448 			logger.error("PHASE 4: Creating Inferred Synonyms...");
449 
450 			// Determine the count of elements in datawarehouse database table Taxon
451 			Integer currentTaxonId = determineTaxonCount(state);
452 			currentTaxonId++;
453 
454 			count = 0;
455 			pastCount = 0;
456 			int pageSize = limit;
457 			int pageNumber = 1;
458 			String inferredSynonymPluralString = "Inferred Synonyms";
459 			
460 			// Start transaction
461 			TaxonomicTree taxonTree = null;
462 			Taxon acceptedTaxon = null;
463 			txStatus = startTransaction(true);
464 			logger.error("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
465 			List<TaxonBase> taxonList = null;
466 			List<Synonym> inferredSynonyms = null;
467 			while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", null, pageSize, pageNumber)).size() > 0) {
468 				HashMap<Integer, TaxonNameBase> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase>();
469 
470 				logger.error("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
471 				for (TaxonBase taxonBase : taxonList) {
472 
473 					if (taxonBase.isInstanceOf(Taxon.class)) { // this should always be the case since we should have fetched accepted taxon only, but you never know...
474 						acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
475 						TaxonNameBase taxonName = acceptedTaxon.getName();
476 						
477 						if (taxonName.isInstanceOf(ZoologicalName.class)) {
478 							nomenclaturalCode  = PesiTransformer.getNomenclaturalCode(taxonName);
479 							kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
480 	
481 							Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
482 							TaxonNode singleNode = null;
483 							if (taxonNodes.size() > 0) {
484 								// Determine the taxonomicTree of the current TaxonNode
485 								singleNode = taxonNodes.iterator().next();
486 								if (singleNode != null) {
487 									taxonTree = singleNode.getTaxonomicTree();
488 								} else {
489 									logger.error("A TaxonNode belonging to this accepted Taxon is NULL: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() +")");
490 								}
491 							} else {
492 								// TaxonomicTree could not be determined directly from this TaxonNode
493 								// The stored taxonomicTree from another TaxonNode is used. It's a simple, but not a failsafe fallback solution.
494 								if (taxonTree == null) {
495 									logger.error("TaxonomicTree could not be determined directly from this TaxonNode: " + singleNode.getUuid() + "). " +
496 											"This taxonomicTree stored from another TaxonNode is used: " + taxonTree.getTitleCache());
497 								}
498 							}
499 							
500 							if (taxonTree != null) {
501 								inferredSynonyms  = getTaxonService().createAllInferredSynonyms(taxonTree, acceptedTaxon);
502 	
503 //								inferredSynonyms = getTaxonService().createInferredSynonyms(taxonTree, acceptedTaxon, SynonymRelationshipType.INFERRED_GENUS_OF());
504 								if (inferredSynonyms != null) {
505 									for (Synonym synonym : inferredSynonyms) {
506 										TaxonNameBase synonymName = synonym.getName();
507 										if (synonymName != null) {
508 											
509 											// Both Synonym and its TaxonName have no valid Id yet
510 											synonym.setId(currentTaxonId++);
511 											synonymName.setId(currentTaxonId++);
512 											
513 											doCount(count++, modCount, inferredSynonymPluralString);
514 											success &= mapping.invoke(synonymName);
515 											
516 											// Add Rank Data and KingdomFk to hashmap for later saving
517 											inferredSynonymsDataToBeSaved.put(synonymName.getId(), synonymName);
518 										} else {
519 											logger.error("TaxonName of this Synonym is NULL: " + synonym.getUuid() + " (" + synonym.getTitleCache() + ")");
520 										}
521 									}
522 								}
523 							} else {
524 								logger.error("TaxonomicTree is NULL. Inferred Synonyms could not be created for this Taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() + ")");
525 							}
526 						} else {
527 //							logger.error("TaxonName is not a ZoologicalName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
528 						}
529 					} else {
530 						logger.error("This TaxonBase is not a Taxon even though it should be: " + taxonBase.getUuid() + " (" + taxonBase.getTitleCache() + ")");
531 					}
532 				}
533 
534 				// Commit transaction
535 				commitTransaction(txStatus);
536 				logger.error("Committed transaction.");
537 				logger.error("Exported " + (count - pastCount) + " " + inferredSynonymPluralString + ". Total: " + count);
538 				pastCount = count;
539 				
540 				// Save Rank Data and KingdomFk for inferred synonyms
541 				for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
542 					invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomenclaturalCode, taxonFk, kingdomFk);
543 				}
544 
545 				// Start transaction
546 				txStatus = startTransaction(true);
547 				logger.error("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
548 				
549 				// Increment pageNumber
550 				pageNumber++;
551 			}
552 			if (taxonList.size() == 0) {
553 				logger.error("No " + parentPluralString + " left to fetch.");
554 			}
555 			// Commit transaction
556 			commitTransaction(txStatus);
557 			logger.error("Committed transaction.");
558 
559 			
560 			logger.error("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
561 
562 			return success;
563 		} catch (SQLException e) {
564 			e.printStackTrace();
565 			logger.error(e.getMessage());
566 			return false;
567 		}
568 	}
569 
570 	/**
571 	 * Determines the current number of entries in the DataWarehouse database table <code>Taxon</code>.
572 	 * @param state The {@link PesiExportState PesiExportState}.
573 	 * @return The count.
574 	 */
575 	private Integer determineTaxonCount(PesiExportState state) {
576 		Integer result = null;
577 		PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
578 		
579 		String sql;
580 		Source destination =  pesiConfig.getDestination();
581 		sql = "SELECT COUNT(*) FROM Taxon";
582 		destination.setQuery(sql);
583 		ResultSet resultSet = destination.getResultSet();
584 		try {
585 			resultSet.next();
586 			result = resultSet.getInt(1);
587 		} catch (SQLException e) {
588 			logger.error("TaxonCount could not be determined: " + e.getMessage());
589 			e.printStackTrace();
590 		}
591 		return result;
592 	}
593 
594 	/**
595 	 * Returns the userId of the expert associated with the given TaxonName.
596 	 * @param taxonName A {@link TaxonNameBase TaxonName}.
597 	 * @return The userId.
598 	 */
599 	private String getExpertUserId(TaxonNameBase taxonName) {
600 		String result = null;
601 		try {
602 		Set<Extension> extensions = taxonName.getExtensions();
603 		for (Extension extension : extensions) {
604 			if (extension.getType().equals(expertUserIdExtensionType)) {
605 				result = extension.getValue();
606 			}
607 		}
608 		} catch (Exception e) {
609 			e.printStackTrace();
610 		}
611 		return result;
612 	}
613 
614 	/**
615 	 * Returns the userId of the speciesExpert associated with the given TaxonName.
616 	 * @param taxonName A {@link TaxonNameBase TaxonName}.
617 	 * @return The userId.
618 	 */
619 	private String getSpeciesExpertUserId(TaxonNameBase taxonName) {
620 		String result = null;
621 		try {
622 		Set<Extension> extensions = taxonName.getExtensions();
623 		for (Extension extension : extensions) {
624 			if (extension.getType().equals(speciesExpertUserIdExtensionType)) {
625 				result = extension.getValue();
626 			}
627 		}
628 		} catch (Exception e) {
629 			e.printStackTrace();
630 		}
631 		return result;
632 	}
633 	
634 	/**
635 	 * Checks whether a parent at specific level has a specific Rank.
636 	 * @param taxonName A {@link TaxonNameBase TaxonName}.
637 	 * @param level The ancestor level.
638 	 * @param ancestorRank The ancestor rank.
639 	 * @return Whether a parent at a specific level has a specific Rank.
640 	 */
641 	private boolean ancestorOfSpecificRank(TaxonNameBase taxonName, int level,
642 			Rank ancestorRank) {
643 		boolean result = false;
644 		Set<Taxon> taxa = taxonName.getTaxa();
645 		TaxonNode parentNode = null;
646 		if (taxa != null && taxa.size() > 0) {
647 			if (taxa.size() == 1) {
648 				Taxon taxon = taxa.iterator().next();
649 				if (taxon != null) {
650 					// Get ancestor Taxon via TaxonNode
651 					
652 					Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();
653 					if (taxonNodes.size() == 1) {
654 						TaxonNode taxonNode = taxonNodes.iterator().next();
655 						if (taxonNode != null) {
656 							for (int i = 0; i < level; i++) {
657 								if (taxonNode != null) {
658 									taxonNode  = taxonNode.getParent();
659 								}
660 							}
661 							parentNode = taxonNode;
662 						}
663 					} else if (taxonNodes.size() > 1) {
664 						logger.error("This taxon has " + taxonNodes.size() + " taxonNodes: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
665 					}
666 				}
667 			} else {
668 				logger.error("This taxonName has " + taxa.size() + " Taxa: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
669 			}
670 		}
671 		if (parentNode != null) {
672 			TaxonNode node = CdmBase.deproxy(parentNode, TaxonNode.class);
673 			Taxon parentTaxon = node.getTaxon();
674 			if (parentTaxon != null) {
675 				TaxonNameBase parentTaxonName = parentTaxon.getName();
676 				if (parentTaxonName != null && parentTaxonName.getRank().equals(ancestorRank)) {
677 					result = true;
678 				}
679 			} else {
680 				logger.error("This TaxonNode has no Taxon: " + node.getUuid());
681 			}
682 		}
683 		return result;
684 	}
685 
686 	/**
687 	 * Returns the AnnotationType for a given UUID.
688 	 * @param uuid The Annotation UUID.
689 	 * @param label The Annotation label.
690 	 * @param text The Annotation text.
691 	 * @param labelAbbrev The Annotation label abbreviation.
692 	 * @return The AnnotationType.
693 	 */
694 	protected AnnotationType getAnnotationType(UUID uuid, String label, String text, String labelAbbrev){
695 		AnnotationType annotationType = (AnnotationType)getTermService().find(uuid);
696 		if (annotationType == null) {
697 			annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
698 			annotationType.setUuid(uuid);
699 //			annotationType.setVocabulary(AnnotationType.EDITORIAL().getVocabulary());
700 			getTermService().save(annotationType);
701 		}
702 		return annotationType;
703 	}
704 
705 	/**
706 	 * Traverses the TaxonTree recursively and stores determined values for every Taxon.
707 	 * @param childNode The {@link TaxonNode TaxonNode} to process.
708 	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
709 	 * @param treeIndex The TreeIndex at the current level.
710 	 * @param fetchLevel Rank to stop fetching at.
711 	 * @param state The {@link PesiExportState PesiExportState}.
712 	 */
713 	private void traverseTree(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, Rank fetchLevel, PesiExportState state) {
714 		// Traverse all branches from this childNode until specified fetchLevel is reached.
715 		StringBuffer localTreeIndex = new StringBuffer(treeIndex);
716 		if (childNode.getTaxon() != null) {
717 			TaxonNameBase taxonName = childNode.getTaxon().getName();
718 			Integer taxonNameId = state.getDbId(taxonName);
719 			if (taxonNameId != null) {
720 				Rank childTaxonNameRank = taxonName.getRank();
721 				if (childTaxonNameRank != null) {
722 					if (! childTaxonNameRank.equals(fetchLevel)) {
723 
724 						localTreeIndex.append(taxonNameId);
725 						localTreeIndex.append("#");
726 
727 						saveData(childNode, parentNode, localTreeIndex, state, taxonNameId);
728 
729 						// Store treeIndex as annotation for further use
730 						Annotation annotation = Annotation.NewInstance(localTreeIndex.toString(), getTreeIndexAnnotationType(), Language.DEFAULT());
731 						childNode.addAnnotation(annotation);
732 
733 						for (TaxonNode newNode : childNode.getChildNodes()) {
734 							traverseTree(newNode, childNode, localTreeIndex, fetchLevel, state);
735 						}
736 						
737 					} else {
738 //						logger.error("Target Rank " + fetchLevel.getLabel() + " reached");
739 						return;
740 					}
741 				} else {
742 					logger.error("Rank is NULL. FetchLevel can not be checked: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
743 				}
744 			} else {
745 				logger.error("TaxonName can not be found in State: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
746 			}
747 
748 		} else {
749 			logger.error("Taxon is NULL for TaxonNode: " + childNode.getUuid());
750 		}
751 	}
752 
753 	/**
754 	 * Stores values in database for every recursive round.
755 	 * @param childNode The {@link TaxonNode TaxonNode} to process.
756 	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
757 	 * @param treeIndex The TreeIndex at the current level.
758 	 * @param state The {@link PesiExportState PesiExportState}.
759 	 * @param currentTaxonFk The TaxonFk to store the values for.
760 	 */
761 	private void saveData(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, PesiExportState state, Integer currentTaxonFk) {
762 		// We are differentiating kingdoms by the nomenclatural code for now.
763 		// This needs to be handled in a better way as soon as we know how to differentiate between more kingdoms.
764 		Taxon childNodeTaxon = childNode.getTaxon();
765 		TaxonNameBase childNodeTaxonName = childNode.getTaxon().getName();
766 		if (childNodeTaxon != null && childNodeTaxonName != null) {
767 			TaxonNameBase parentNodeTaxonName = null;
768 			if (parentNode != null) {
769 				Taxon parentNodeTaxon = parentNode.getTaxon();
770 				if (parentNodeTaxon != null) {
771 					parentNodeTaxonName  = parentNodeTaxon.getName();
772 				}
773 			}
774 
775 			invokeParentTaxonFkAndTreeIndex(
776 					state.getDbId(parentNodeTaxonName), 
777 					currentTaxonFk, 
778 					treeIndex);
779 		}
780 		
781 	}
782 
783 	/**
784 	 * Inserts values into the Taxon database table.
785 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
786 	 * @param state The {@link PesiExportState PesiExportState}.
787 	 * @param stmt The prepared statement.
788 	 * @return Whether save was successful or not.
789 	 */
790 	protected boolean invokeParentTaxonFkAndTreeIndex(Integer parentTaxonFk, Integer currentTaxonFk, StringBuffer treeIndex) {
791 		try {
792 			if (parentTaxonFk != null) {
793 				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(1, parentTaxonFk);
794 			} else {
795 				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(1, null);
796 			}
797 
798 			if (treeIndex != null) {
799 				parentTaxonFk_TreeIndex_KingdomFkStmt.setString(2, treeIndex.toString());
800 			} else {
801 				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(2, null);
802 			}
803 
804 			if (currentTaxonFk != null) {
805 				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(3, currentTaxonFk);
806 			} else {
807 				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(3, null);
808 			}
809 			
810 			parentTaxonFk_TreeIndex_KingdomFkStmt.executeUpdate();
811 			return true;
812 		} catch (SQLException e) {
813 			logger.error("ParentTaxonFk and TreeIndex could not be inserted into database: " + e.getMessage());
814 			e.printStackTrace();
815 			return false;
816 		}
817 	}
818 
819 	/**
820 	 * Inserts Rank data and KingdomFk into the Taxon database table.
821 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
822 	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
823 	 * @param taxonFk The TaxonFk to store the values for.
824 	 * @param kindomFk The KingdomFk.
825 	 * @return Whether save was successful or not.
826 	 */
827 	private boolean invokeRankDataAndKingdomFk(TaxonNameBase taxonName, NomenclaturalCode nomenclaturalCode, 
828 			Integer taxonFk, Integer kingdomFk) {
829 		try {
830 			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
831 			if (rankFk != null) {
832 				rankSqlStmt.setInt(1, rankFk);
833 			} else {
834 				rankSqlStmt.setObject(1, null);
835 			}
836 	
837 			String rankCache = getRankCache(taxonName, nomenclaturalCode);
838 			if (rankCache != null) {
839 				rankSqlStmt.setString(2, rankCache);
840 			} else {
841 				rankSqlStmt.setObject(2, null);
842 			}
843 			
844 			if (kingdomFk != null) {
845 				rankSqlStmt.setInt(3, kingdomFk);
846 			} else {
847 				rankSqlStmt.setObject(3, null);
848 			}
849 			
850 			if (taxonFk != null) {
851 				rankSqlStmt.setInt(4, taxonFk);
852 			} else {
853 				rankSqlStmt.setObject(4, null);
854 			}
855 			
856 			rankSqlStmt.executeUpdate();
857 			return true;
858 		} catch (SQLException e) {
859 			logger.error("Data (RankFk, RankCache, KingdomFk) could not be inserted into database: " + e.getMessage());
860 			e.printStackTrace();
861 			return false;
862 		}
863 	}
864 
865 	/**
866 	 * Inserts Rank data, TypeNameFk, KingdomFk, expertFk and speciesExpertFk into the Taxon database table.
867 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
868 	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
869 	 * @param taxonFk The TaxonFk to store the values for.
870 	 * @param typeNameFk The TypeNameFk.
871 	 * @param kindomFk The KingdomFk.
872 	 * @param expertFk The ExpertFk.
873 	 * @param speciesExpertFk The SpeciesExpertFk.
874 	 * @return Whether save was successful or not.
875 	 */
876 	private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonNameBase taxonName, NomenclaturalCode nomenclaturalCode, 
877 			Integer taxonFk, Integer typeNameFk, Integer kingdomFk,
878 			Integer expertFk, Integer speciesExpertFk) {
879 		try {
880 			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
881 			if (rankFk != null) {
882 				sqlStmt.setInt(1, rankFk);
883 			} else {
884 				sqlStmt.setObject(1, null);
885 			}
886 	
887 			String rankCache = getRankCache(taxonName, nomenclaturalCode);
888 			if (rankCache != null) {
889 				sqlStmt.setString(2, rankCache);
890 			} else {
891 				sqlStmt.setObject(2, null);
892 			}
893 			
894 			if (typeNameFk != null) {
895 				sqlStmt.setInt(3, typeNameFk);
896 			} else {
897 				sqlStmt.setObject(3, null);
898 			}
899 			
900 			if (kingdomFk != null) {
901 				sqlStmt.setInt(4, kingdomFk);
902 			} else {
903 				sqlStmt.setObject(4, null);
904 			}
905 			
906 			if (expertFk != null) {
907 				sqlStmt.setInt(5, expertFk);
908 			} else {
909 				sqlStmt.setObject(5, null);
910 			}
911 			
912 			if (speciesExpertFk != null) {
913 				sqlStmt.setInt(6, speciesExpertFk);
914 			} else {
915 				sqlStmt.setObject(6, null);
916 			}
917 			
918 			if (taxonFk != null) {
919 				sqlStmt.setInt(7, taxonFk);
920 			} else {
921 				sqlStmt.setObject(7, null);
922 			}
923 			
924 			sqlStmt.executeUpdate();
925 			return true;
926 		} catch (SQLException e) {
927 			logger.error("Data could not be inserted into database: " + e.getMessage());
928 			e.printStackTrace();
929 			return false;
930 		}
931 	}
932 
933 	/**
934 	 * Deletes all entries of database tables related to <code>Taxon</code>.
935 	 * @param state The {@link PesiExportState PesiExportState}.
936 	 * @return Whether the delete operation was successful or not.
937 	 */
938 	protected boolean doDelete(PesiExportState state) {
939 		PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
940 		
941 		String sql;
942 		Source destination =  pesiConfig.getDestination();
943 
944 		// Clear Taxon
945 		sql = "DELETE FROM " + dbTableName;
946 		destination.setQuery(sql);
947 		destination.update(sql);
948 		return true;
949 	}
950 
951 	/* (non-Javadoc)
952 	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
953 	 */
954 	@Override
955 	protected boolean isIgnore(PesiExportState state) {
956 		return ! state.getConfig().isDoTaxa();
957 	}
958 
959 	/**
960 	 * Returns the <code>RankFk</code> attribute.
961 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
962 	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
963 	 * @return The <code>RankFk</code> attribute.
964 	 * @see MethodMapper
965 	 */
966 	private static Integer getRankFk(TaxonNameBase taxonName, NomenclaturalCode nomenclaturalCode) {
967 		Integer result = null;
968 		try {
969 		if (nomenclaturalCode != null) {
970 			if (taxonName != null) {
971 				if (taxonName.getRank() == null) {
972 					logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
973 				} else {
974 					result = PesiTransformer.rank2RankId(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
975 				}
976 				if (result == null) {
977 					logger.warn("Rank could not be determined for PESI-Kingdom-Id " + PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode) + " and TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
978 				}
979 			}
980 		}
981 		} catch (Exception e) {
982 			e.printStackTrace();
983 		}
984 		return result;
985 	}
986 
987 	/**
988 	 * Returns the <code>RankCache</code> attribute.
989 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
990 	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
991 	 * @return The <code>RankCache</code> attribute.
992 	 * @see MethodMapper
993 	 */
994 	private static String getRankCache(TaxonNameBase taxonName, NomenclaturalCode nomenclaturalCode) {
995 		String result = null;
996 		try {
997 		if (nomenclaturalCode != null) {
998 			result = PesiTransformer.rank2RankCache(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
999 		}
1000 		} catch (Exception e) {
1001 			e.printStackTrace();
1002 		}
1003 		return result;
1004 	}
1005 
1006 	/**
1007 	 * Returns the <code>GenusOrUninomial</code> attribute.
1008 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1009 	 * @return The <code>GenusOrUninomial</code> attribute.
1010 	 * @see MethodMapper
1011 	 */
1012 	private static String getGenusOrUninomial(TaxonNameBase taxonName) {
1013 		String result = null;
1014 		try {
1015 		if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1016 			NonViralName nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1017 			result = nonViralName.getGenusOrUninomial();
1018 		}
1019 		} catch (Exception e) {
1020 			e.printStackTrace();
1021 		}
1022 		return result;
1023 	}
1024 
1025 	/**
1026 	 * Returns the <code>InfraGenericEpithet</code> attribute.
1027 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1028 	 * @return The <code>InfraGenericEpithet</code> attribute.
1029 	 * @see MethodMapper
1030 	 */
1031 	private static String getInfraGenericEpithet(TaxonNameBase taxonName) {
1032 		String result = null;
1033 		try {
1034 		if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1035 			NonViralName nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1036 			result = nonViralName.getInfraGenericEpithet();
1037 		}
1038 		} catch (Exception e) {
1039 			e.printStackTrace();
1040 		}
1041 		return result;
1042 	}
1043 
1044 	/**
1045 	 * Returns the <code>SpecificEpithet</code> attribute.
1046 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1047 	 * @return The <code>SpecificEpithet</code> attribute.
1048 	 * @see MethodMapper
1049 	 */
1050 	private static String getSpecificEpithet(TaxonNameBase taxonName) {
1051 		String result = null;
1052 		try {
1053 		if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1054 			NonViralName nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1055 			result = nonViralName.getSpecificEpithet();
1056 		}
1057 		} catch (Exception e) {
1058 			e.printStackTrace();
1059 		}
1060 		return result;
1061 	}
1062 
1063 	/**
1064 	 * Returns the <code>InfraSpecificEpithet</code> attribute.
1065 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1066 	 * @return The <code>InfraSpecificEpithet</code> attribute.
1067 	 * @see MethodMapper
1068 	 */
1069 	private static String getInfraSpecificEpithet(TaxonNameBase taxonName) {
1070 		String result = null;
1071 		try {
1072 		if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1073 			NonViralName nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1074 			result = nonViralName.getInfraSpecificEpithet();
1075 		}
1076 		} catch (Exception e) {
1077 			e.printStackTrace();
1078 		}
1079 		return result;
1080 	}
1081 
1082 	/**
1083 	 * Returns the <code>WebSearchName</code> attribute.
1084 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1085 	 * @return The <code>WebSearchName</code> attribute.
1086 	 * @see MethodMapper
1087 	 */
1088 	@SuppressWarnings("unused")
1089 	private static String getWebSearchName(TaxonNameBase taxonName) {
1090 		String result = null;
1091 		try {
1092 		if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1093 			NonViralName nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1094 			result = nonViralName.getNameCache();
1095 		}
1096 		} catch (Exception e) {
1097 			e.printStackTrace();
1098 		}
1099 		return result;
1100 	}
1101 	
1102 	/**
1103 	 * Returns the <code>WebShowName</code> attribute.
1104 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1105 	 * @return The <code>WebShowName</code> attribute.
1106 	 * @see MethodMapper
1107 	 */
1108 	private static String getWebShowName(TaxonNameBase taxonName) {
1109 		String result = "";
1110 		
1111 		try {
1112 		List taggedName = taxonName.getTaggedName();
1113 		boolean openTag = false;
1114 		boolean start = true;
1115 		for (Object object : taggedName) {
1116 			if (object instanceof String) {
1117 				// Name
1118 				if (! openTag) {
1119 					if (start) {
1120 						result = "<i>";
1121 						start = false;
1122 					} else {
1123 						result += " <i>";
1124 					}
1125 					openTag = true;
1126 				} else {
1127 					result += " ";
1128 				}
1129 				result += object;
1130 			} else if (object instanceof Rank) {
1131 				// Rank
1132 				Rank rank = CdmBase.deproxy(object, Rank.class);
1133 				
1134 				if ("".equals(rank.getAbbreviation().trim())) {
1135 					logger.error("Rank abbreviation is an empty string: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1136 				} else {
1137 					if (openTag) {
1138 						result += "</i> ";
1139 						openTag = false;
1140 					} else {
1141 						result += " ";
1142 					}
1143 					result += rank.getAbbreviation();
1144 				}
1145 			} else if (object instanceof Team) {
1146 				if (openTag) {
1147 					result += "</i> ";
1148 					openTag = false;
1149 				} else {
1150 					result += " ";
1151 				}
1152 				result += object;
1153 			} else if (object instanceof Date) {
1154 				if (openTag) {
1155 					result += "</i> ";
1156 					openTag = false;
1157 				} else {
1158 					result += " ";
1159 				}
1160 				result += object;
1161 			} else if (object instanceof ReferenceBase) {
1162 				if (openTag) {
1163 					result += "</i> ";
1164 					openTag = false;
1165 				} else {
1166 					result += " ";
1167 				}
1168 				result += object;
1169 			} else {
1170 				if (object == null) {
1171 					logger.error("One part of TaggedName is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1172 				} else {
1173 					logger.error("Instance of this part of TaggedName is unknown. Object: " + object.getClass().getSimpleName() + ", TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1174 				}
1175 			}
1176 		}
1177 		if (openTag) {
1178 			result += "</i>";
1179 		}
1180 		} catch (Exception e) {
1181 			e.printStackTrace();
1182 		}
1183 
1184 		return result;
1185 	}
1186 
1187 	/**
1188 	 * Returns the <code>AuthorString</code> attribute.
1189 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1190 	 * @return The <code>AuthorString</code> attribute.
1191 	 * @see MethodMapper
1192 	 */
1193 	@SuppressWarnings("unused")
1194 	private static String getAuthorString(TaxonNameBase taxonName) {
1195 		String result = null;
1196 		try {
1197 			if (taxonName != null) {
1198 				if (taxonName.isInstanceOf(NonViralName.class)) {
1199 					NonViralName nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1200 					String authorshipCache = nonViralName.getAuthorshipCache();
1201 	
1202 					// For a misapplied name without an authorshipCache the authorString should be set to "auct."
1203 					if (isMisappliedName(taxonName) && authorshipCache == null) {
1204 						// Set authorshipCache to "auct."
1205 						result = PesiTransformer.auctString;
1206 					} else {
1207 						// Return the content of the authorshipCache
1208 						result = authorshipCache;
1209 					}
1210 	
1211 				} else {
1212 					logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1213 				}
1214 			}
1215 		} catch (Exception e) {
1216 			e.printStackTrace();
1217 		}
1218 		
1219 		if ("".equals(result)) {
1220 			return null;
1221 		} else {
1222 			return result;
1223 		}
1224 	}
1225 
1226 	/**
1227 	 * Checks whether a given TaxonName is a misapplied name.
1228 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1229 	 * @return Whether the given TaxonName is a misapplied name or not.
1230 	 */
1231 	private static boolean isMisappliedName(TaxonNameBase taxonName) {
1232 		boolean result = false;
1233 		Set<NameRelationship> taxonNameRelations = taxonName.getRelationsFromThisName();
1234 		for (NameRelationship nameRelation : taxonNameRelations) {
1235 			NameRelationshipType relationshipType = nameRelation.getType();
1236 			if (relationshipType.equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())) {
1237 				result = true;
1238 			}
1239 		}
1240 		
1241 		Set<Taxon> taxa = taxonName.getTaxa();
1242 		if (taxa.size() == 1) {
1243 			Taxon taxon = CdmBase.deproxy(taxa.iterator().next(), Taxon.class);
1244 			Set<TaxonRelationship> taxonRelations = taxon.getRelationsFromThisTaxon();
1245 			for (TaxonRelationship taxonRelationship : taxonRelations) {
1246 				TaxonRelationshipType taxonRelationshipType = taxonRelationship.getType();
1247 				if (taxonRelationshipType.equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())) {
1248 					result = true;
1249 				}
1250 			}
1251 		} else if (taxa.size() > 1) {
1252 			logger.error("Could not check for misapplied name. This TaxonName has " + taxa.size() + " Taxa: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1253 		}
1254 		return result;
1255 	}
1256 	
1257 	/**
1258 	 * Returns the <code>FullName</code> attribute.
1259 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1260 	 * @return The <code>FullName</code> attribute.
1261 	 * @see MethodMapper
1262 	 */
1263 	@SuppressWarnings("unused")
1264 	private static String getFullName(TaxonNameBase taxonName) {
1265 		if (taxonName != null) {
1266 			return taxonName.getTitleCache();
1267 		} else {
1268 			return null;
1269 		}
1270 	}
1271 
1272 	/**
1273 	 * Returns the <code>NomRefString</code> attribute.
1274 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1275 	 * @return The <code>NomRefString</code> attribute.
1276 	 * @see MethodMapper
1277 	 */
1278 	@SuppressWarnings("unused")
1279 	private static String getNomRefString(TaxonNameBase taxonName) {
1280 		String result = null;
1281 		try {
1282 		if (taxonName != null) {
1283 			try {
1284 				result = taxonName.getNomenclaturalMicroReference();
1285 			} catch (Exception e) {
1286 				logger.error("While getting NomRefString");
1287 				e.printStackTrace();
1288 			}
1289 		}
1290 		} catch (Exception e) {
1291 			e.printStackTrace();
1292 		}
1293 		return result;
1294 	}
1295 	
1296 	/**
1297 	 * Returns the <code>DisplayName</code> attribute.
1298 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1299 	 * @return The <code>DisplayName</code> attribute.
1300 	 * @see MethodMapper
1301 	 */
1302 	@SuppressWarnings("unused")
1303 	private static String getDisplayName(TaxonNameBase taxonName) {
1304 		// TODO: extension?
1305 		if (taxonName != null) {
1306 			return taxonName.getFullTitleCache();
1307 		} else {
1308 			return null;
1309 		}
1310 	}
1311 	
1312 	/**
1313 	 * Returns the <code>NameStatusFk</code> attribute.
1314 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1315 	 * @return The <code>NameStatusFk</code> attribute.
1316 	 * @see MethodMapper
1317 	 */
1318 	@SuppressWarnings("unused")
1319 	private static Integer getNameStatusFk(TaxonNameBase taxonName) {
1320 		Integer result = null;
1321 		
1322 		try {
1323 		if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1324 			NonViralName nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1325 			Set<NomenclaturalStatus> states = nonViralName.getStatus();
1326 			if (states.size() == 1) {
1327 				NomenclaturalStatus state = states.iterator().next();
1328 				NomenclaturalStatusType statusType = null;
1329 				if (state != null) {
1330 					statusType = state.getType();
1331 				}
1332 				if (statusType != null) {
1333 					result = PesiTransformer.nomStatus2nomStatusFk(statusType);
1334 				}
1335 			} else if (states.size() > 1) {
1336 				logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1337 			}
1338 		}
1339 		
1340 		} catch (Exception e) {
1341 			e.printStackTrace();
1342 		}
1343 		return result;
1344 	}
1345 	
1346 	/**
1347 	 * Returns the <code>NameStatusCache</code> attribute.
1348 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1349 	 * @return The <code>NameStatusCache</code> attribute.
1350 	 * @see MethodMapper
1351 	 */
1352 	@SuppressWarnings("unused")
1353 	private static String getNameStatusCache(TaxonNameBase taxonName) {
1354 		String result = null;
1355 		
1356 		try {
1357 		if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1358 			NonViralName nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1359 			Set<NomenclaturalStatus> states = nonViralName.getStatus();
1360 			if (states.size() == 1) {
1361 				NomenclaturalStatus state = states.iterator().next();
1362 				if (state != null) {
1363 					result = PesiTransformer.nomStatus2NomStatusCache(state.getType());
1364 				}
1365 			} else if (states.size() > 1) {
1366 				logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1367 			}
1368 		}
1369 		
1370 		} catch (Exception e) {
1371 			e.printStackTrace();
1372 		}
1373 		return result;
1374 	}
1375 	
1376 	/**
1377 	 * Returns the <code>TaxonStatusFk</code> attribute.
1378 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1379 	 * @param state The {@link PesiExportState PesiExportState}.
1380 	 * @return The <code>TaxonStatusFk</code> attribute.
1381 	 * @see MethodMapper
1382 	 */
1383 	@SuppressWarnings("unused")
1384 	private static Integer getTaxonStatusFk(TaxonNameBase taxonName, PesiExportState state) {
1385 		Integer result = null;
1386 		
1387 		try {
1388 			if (isAuctReference(taxonName, state)) {
1389 				Synonym synonym = Synonym.NewInstance(null, null);
1390 				
1391 				// This works as long as only the instance is important to differentiate between TaxonStatus.
1392 				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1393 			} else {
1394 				Set taxa = taxonName.getTaxa();
1395 				if (taxa.size() == 1) {
1396 					result = PesiTransformer.taxonBase2statusFk((TaxonBase<?>) taxa.iterator().next());
1397 				} else if (taxa.size() > 1) {
1398 					logger.warn("This TaxonName has " + taxa.size() + " Taxa: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1399 				}
1400 				
1401 				Set synonyms = taxonName.getSynonyms();
1402 				if (synonyms.size() == 1) {
1403 					result = PesiTransformer.taxonBase2statusFk((TaxonBase<?>) synonyms.iterator().next());
1404 				} else if (synonyms.size() > 1) {
1405 					logger.warn("This TaxonName has " + synonyms.size() + " Synonyms: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1406 				}
1407 			}
1408 		
1409 		} catch (Exception e) {
1410 			e.printStackTrace();
1411 		}
1412 		return result;
1413 	}
1414 	
1415 	/**
1416 	 * Returns the <code>TaxonStatusCache</code> attribute.
1417 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1418 	 * @param state The {@link PesiExportState PesiExportState}.
1419 	 * @return The <code>TaxonStatusCache</code> attribute.
1420 	 * @see MethodMapper
1421 	 */
1422 	@SuppressWarnings("unused")
1423 	private static String getTaxonStatusCache(TaxonNameBase taxonName, PesiExportState state) {
1424 		String result = null;
1425 		
1426 		try {
1427 			if (isAuctReference(taxonName, state)) {
1428 				Synonym synonym = Synonym.NewInstance(null, null);
1429 				
1430 				// This works as long as only the instance is important to differentiate between TaxonStatus.
1431 				result = PesiTransformer.taxonBase2statusCache(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1432 			} else {
1433 				Set taxa = taxonName.getTaxa();
1434 				if (taxa.size() == 1) {
1435 					result = PesiTransformer.taxonBase2statusCache((TaxonBase<?>) taxa.iterator().next());
1436 				} else if (taxa.size() > 1) {
1437 					logger.warn("This TaxonName has " + taxa.size() + " Taxa: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1438 				}
1439 				
1440 				Set synonyms = taxonName.getSynonyms();
1441 				if (synonyms.size() == 1) {
1442 					result = PesiTransformer.taxonBase2statusCache((TaxonBase<?>) synonyms.iterator().next());
1443 				} else if (synonyms.size() > 1) {
1444 					logger.warn("This TaxonName has " + synonyms.size() + " Synonyms: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1445 				}
1446 			}
1447 		
1448 		} catch (Exception e) {
1449 			e.printStackTrace();
1450 		}
1451 		return result;
1452 	}
1453 	
1454 	/**
1455 	 * Returns the <code>TypeNameFk</code> attribute.
1456 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1457 	 * @param state The {@link PesiExportState PesiExportState}.
1458 	 * @return The <code>TypeNameFk</code> attribute.
1459 	 * @see MethodMapper
1460 	 */
1461 	private static Integer getTypeNameFk(TaxonNameBase taxonNameBase, PesiExportState state) {
1462 		Integer result = null;
1463 		if (taxonNameBase != null) {
1464 			Set<NameTypeDesignation> nameTypeDesignations = taxonNameBase.getNameTypeDesignations();
1465 			if (nameTypeDesignations.size() == 1) {
1466 				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1467 				if (nameTypeDesignation != null) {
1468 					TaxonNameBase typeName = nameTypeDesignation.getTypeName();
1469 					if (typeName != null) {
1470 						result = state.getDbId(typeName);
1471 					}
1472 				}
1473 			} else if (nameTypeDesignations.size() > 1) {
1474 				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonNameBase.getUuid() + " (" + taxonNameBase.getTitleCache() + ")");
1475 			}
1476 		}
1477 		return result;
1478 	}
1479 	
1480 	/**
1481 	 * Returns the <code>TypeFullnameCache</code> attribute.
1482 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1483 	 * @return The <code>TypeFullnameCache</code> attribute.
1484 	 * @see MethodMapper
1485 	 */
1486 	@SuppressWarnings("unused")
1487 	private static String getTypeFullnameCache(TaxonNameBase taxonName) {
1488 		String result = null;
1489 		
1490 		try {
1491 		if (taxonName != null) {
1492 			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1493 			if (nameTypeDesignations.size() == 1) {
1494 				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1495 				if (nameTypeDesignation != null) {
1496 					TaxonNameBase typeName = nameTypeDesignation.getTypeName();
1497 					if (typeName != null) {
1498 						result = typeName.getTitleCache();
1499 					}
1500 				}
1501 			} else if (nameTypeDesignations.size() > 1) {
1502 				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1503 			}
1504 		}
1505 		
1506 		} catch (Exception e) {
1507 			e.printStackTrace();
1508 		}
1509 		return result;
1510 	}
1511 	
1512 	/**
1513 	 * Returns the <code>QualityStatusFk</code> attribute.
1514 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1515 	 * @return The <code>QualityStatusFk</code> attribute.
1516 	 * @see MethodMapper
1517 	 */
1518 	@SuppressWarnings("unused")
1519 	private static Integer getQualityStatusFk(TaxonNameBase taxonName) {
1520 		// TODO: Not represented in CDM right now. Depends on import.
1521 		Integer result = null;
1522 		return result;
1523 	}
1524 	
1525 	/**
1526 	 * Returns the <code>QualityStatusCache</code> attribute.
1527 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1528 	 * @return The <code>QualityStatusCache</code> attribute.
1529 	 * @see MethodMapper
1530 	 */
1531 	@SuppressWarnings("unused")
1532 	private static String getQualityStatusCache(TaxonNameBase taxonName) {
1533 		// TODO: Not represented in CDM right now. Depends on import.
1534 		String result = null;
1535 		return result;
1536 	}
1537 	
1538 	/**
1539 	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1540 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1541 	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1542 	 * @see MethodMapper
1543 	 */
1544 	@SuppressWarnings("unused")
1545 	private static Integer getTypeDesignationStatusFk(TaxonNameBase taxonName) {
1546 		Integer result = null;
1547 		
1548 		try {
1549 		if (taxonName != null) {
1550 			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1551 			if (typeDesignations.size() == 1) {
1552 				Object obj = typeDesignations.iterator().next().getTypeStatus();
1553 				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1554 				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1555 			} else if (typeDesignations.size() > 1) {
1556 				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1557 			}
1558 		}
1559 		
1560 		} catch (Exception e) {
1561 			e.printStackTrace();
1562 		}
1563 		return result;
1564 	}
1565 
1566 	/**
1567 	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1568 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1569 	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1570 	 * @see MethodMapper
1571 	 */
1572 	@SuppressWarnings("unused")
1573 	private static String getTypeDesignationStatusCache(TaxonNameBase taxonName) {
1574 		String result = null;
1575 		
1576 		try {
1577 		if (taxonName != null) {
1578 			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1579 			if (typeDesignations.size() == 1) {
1580 				Object obj = typeDesignations.iterator().next().getTypeStatus();
1581 				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1582 				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
1583 			} else if (typeDesignations.size() > 1) {
1584 				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1585 			}
1586 		}
1587 		
1588 		} catch (Exception e) {
1589 			e.printStackTrace();
1590 		}
1591 		return result;
1592 	}
1593 	
1594 	/**
1595 	 * Returns the <code>FossilStatusFk</code> attribute.
1596 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1597 	 * @return The <code>FossilStatusFk</code> attribute.
1598 	 * @see MethodMapper
1599 	 */
1600 	@SuppressWarnings("unused")
1601 	private static Integer getFossilStatusFk(TaxonNameBase taxonNameBase) {
1602 		Integer result = null;
1603 //		Taxon taxon;
1604 //		if (taxonBase.isInstanceOf(Taxon.class)) {
1605 //			taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1606 //			Set<TaxonDescription> specimenDescription = taxon.;
1607 //			result = PesiTransformer.fossil2FossilStatusId(fossil);
1608 //		}
1609 		return result;
1610 	}
1611 	
1612 	/**
1613 	 * Returns the <code>FossilStatusCache</code> attribute.
1614 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1615 	 * @return The <code>FossilStatusCache</code> attribute.
1616 	 * @see MethodMapper
1617 	 */
1618 	@SuppressWarnings("unused")
1619 	private static String getFossilStatusCache(TaxonNameBase taxonName) {
1620 		// TODO
1621 		String result = null;
1622 		return result;
1623 	}
1624 	
1625 	/**
1626 	 * Returns the <code>IdInSource</code> attribute.
1627 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1628 	 * @return The <code>IdInSource</code> attribute.
1629 	 * @see MethodMapper
1630 	 */
1631 	@SuppressWarnings("unused")
1632 	private static String getIdInSource(TaxonNameBase taxonName) {
1633 		String result = null;
1634 		
1635 		try {
1636 			Set<IdentifiableSource> sources = getSources(taxonName);
1637 			for (IdentifiableSource source : sources) {
1638 				result = "TAX_ID: " + source.getIdInSource();
1639 				String sourceIdNameSpace = source.getIdNamespace();
1640 				if (sourceIdNameSpace != null) {
1641 					if (sourceIdNameSpace.equals("originalGenusId")) {
1642 						result = "Nominal Taxon from TAX_ID: " + source.getIdInSource();
1643 					} else if (sourceIdNameSpace.equals("InferredEpithetOf")) {
1644 						result = "Inferred epithet from TAX_ID: " + source.getIdInSource();
1645 					} else if (sourceIdNameSpace.equals("InferredGenusOf")) {
1646 						result = "Inferred genus from TAX_ID: " + source.getIdInSource();
1647 					} else if (sourceIdNameSpace.equals("PotentialCombinationOf")) {
1648 						result = "Potential combination from TAX_ID: " + source.getIdInSource();
1649 					} else {
1650 						result = "TAX_ID: " + source.getIdInSource();
1651 					}
1652 				}
1653 			}
1654 		} catch (Exception e) {
1655 			e.printStackTrace();
1656 		}
1657 
1658 		if (result == null) {
1659 			logger.error("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1660 		}
1661 		return result;
1662 	}
1663 	
1664 	/**
1665 	 * Returns the idInSource for a given TaxonName only.
1666 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1667 	 * @return The idInSource.
1668 	 */
1669 	private static String getIdInSourceOnly(TaxonNameBase taxonName) {
1670 		String result = null;
1671 		
1672 		// Get the sources first
1673 		Set<IdentifiableSource> sources = getSources(taxonName);
1674 
1675 		// Determine the idInSource
1676 		if (sources.size() == 1) {
1677 			IdentifiableSource source = sources.iterator().next();
1678 			if (source != null) {
1679 				result = source.getIdInSource();
1680 			}
1681 		} else if (sources.size() > 1) {
1682 			int count = 1;
1683 			result = "";
1684 			for (IdentifiableSource source : sources) {
1685 				result += source.getIdInSource();
1686 				if (count < sources.size()) {
1687 					result += "; ";
1688 				}
1689 				count++;
1690 			}
1691 
1692 		}
1693 		
1694 		return result;
1695 	}
1696 	
1697 	/**
1698 	 * Returns the Sources for a given TaxonName only.
1699 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1700 	 * @return The Sources.
1701 	 */
1702 	private static Set<IdentifiableSource> getSources(TaxonNameBase taxonName) {
1703 		Set<IdentifiableSource> sources = null;
1704 
1705 		// Sources from TaxonName
1706 		Set<IdentifiableSource> nameSources = taxonName.getSources();
1707 		sources = nameSources;
1708 		if (nameSources.size() > 1) {
1709 			logger.warn("This TaxonName has more than one Source: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1710 		}
1711 
1712 		// Sources from TaxonBase
1713 		if (sources == null || sources.isEmpty()) {
1714 			Set<Taxon> taxa = taxonName.getTaxa();
1715 			Set<Synonym> synonyms = taxonName.getSynonyms();
1716 			if (taxa.size() == 1) {
1717 				Taxon taxon = taxa.iterator().next();
1718 
1719 				if (taxon != null) {
1720 					sources = taxon.getSources();
1721 				}
1722 			} else if (taxa.size() > 1) {
1723 				logger.warn("This TaxonName has " + taxa.size() + " Taxa: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1724 			}
1725 			if (synonyms.size() == 1) {
1726 				Synonym synonym = synonyms.iterator().next();
1727 				
1728 				if (synonym != null) {
1729 					sources = synonym.getSources();
1730 				}
1731 			} else if (synonyms.size() > 1) {
1732 				logger.warn("This TaxonName has " + synonyms.size() + " Synonyms: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1733 			}
1734 		}
1735 		
1736 		if (sources == null || sources.isEmpty()) {
1737 			logger.error("This TaxonName has no Sources: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1738 		}
1739 		return sources;
1740 	}
1741 	
1742 	/**
1743 	 * Returns the <code>GUID</code> attribute.
1744 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1745 	 * @return The <code>GUID</code> attribute.
1746 	 * @see MethodMapper
1747 	 */
1748 	private static String getGUID(TaxonNameBase taxonName) {
1749 		String result = null;
1750 		try {
1751 			result = "urn:lsid:faunaeur.org:taxname:" + getIdInSourceOnly(taxonName);
1752 		} catch (Exception e) {
1753 			logger.error("Text could not be excluded from idInSource for taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1754 			e.printStackTrace();
1755 		}
1756 		return result;
1757 	}
1758 	
1759 	/**
1760 	 * Returns the <code>DerivedFromGuid</code> attribute.
1761 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1762 	 * @return The <code>DerivedFromGuid</code> attribute.
1763 	 * @see MethodMapper
1764 	 */
1765 	@SuppressWarnings("unused")
1766 	private static String getDerivedFromGuid(TaxonNameBase taxonName) {
1767 		String result = null;
1768 		try {
1769 		// The same as GUID for now
1770 		result = getGUID(taxonName);
1771 		} catch (Exception e) {
1772 			e.printStackTrace();
1773 		}
1774 		return result;
1775 	}
1776 	
1777 	/**
1778 	 * Returns the <code>CacheCitation</code> attribute.
1779 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1780 	 * @return The CacheCitation.
1781 	 * @see MethodMapper
1782 	 */
1783 	@SuppressWarnings("unused")
1784 	private static String getCacheCitation(TaxonNameBase taxonName) {
1785 		String result = "";
1786 		try {
1787 			String originalDb = getOriginalDB(taxonName);
1788 			if (originalDb == null) {
1789 //				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1790 			} else if (originalDb.equals("ERMS")) {
1791 				// TODO: 19.08.2010: An import of CacheCitation does not exist in the ERMS import yet or it will be imported in a different way...
1792 				// 		 So the following code is some kind of harmless assumption.
1793 				Set<Extension> extensions = taxonName.getExtensions();
1794 				for (Extension extension : extensions) {
1795 					if (extension.getType().equals(cacheCitationExtensionType)) {
1796 						result = extension.getValue();
1797 					}
1798 				}
1799 			} else {
1800 				String expertName = getExpertName(taxonName);
1801 				String webShowName = getWebShowName(taxonName);
1802 				
1803 				// idInSource only
1804 				String idInSource = getIdInSourceOnly(taxonName);
1805 				
1806 				// build the cacheCitation
1807 				if (expertName != null) {
1808 					result += expertName + ". ";
1809 				} else {
1810 	//				logger.error("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1811 				}
1812 				if (webShowName != null) {
1813 					result += webShowName + ". ";
1814 				} else {
1815 	//				logger.error("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1816 				}
1817 				
1818 				if (getOriginalDB(taxonName).equals("FaEu")) {
1819 					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
1820 				} else if (getOriginalDB(taxonName).equals("EM")) {
1821 					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
1822 				}
1823 				
1824 				if (idInSource != null) {
1825 					result += idInSource;
1826 				} else {
1827 	//				logger.error("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1828 				}
1829 			}
1830 		} catch (Exception e) {
1831 			e.printStackTrace();
1832 		}
1833 		
1834 		if ("".equals(result)) {
1835 			return null;
1836 		} else {
1837 			return result;
1838 		}
1839 	}
1840 	
1841 	/**
1842 	 * Returns the <code>OriginalDB</code> attribute.
1843 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1844 	 * @return The <code>OriginalDB</code> attribute.
1845 	 * @see MethodMapper
1846 	 */
1847 	private static String getOriginalDB(TaxonNameBase taxonName) {
1848 		String result = "";
1849 		try {
1850 
1851 		// Sources from TaxonName
1852 		Set<IdentifiableSource> sources = taxonName.getSources();
1853 
1854 		IdentifiableEntity taxonBase = null;
1855 		if (sources != null && sources.isEmpty()) {
1856 			// Sources from Taxa or Synonyms
1857 			Set taxa = taxonName.getTaxa();
1858 			if (taxa.size() == 1) {
1859 				taxonBase = (IdentifiableEntity) taxa.iterator().next();
1860 				sources  = taxonBase.getSources();
1861 			} else if (taxa.size() > 1) {
1862 				logger.warn("This TaxonName has " + taxa.size() + " Taxa: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1863 			}
1864 			Set synonyms = taxonName.getSynonyms();
1865 			if (synonyms.size() == 1) {
1866 				taxonBase = (IdentifiableEntity) synonyms.iterator().next();
1867 				sources = taxonBase.getSources();
1868 			} else if (synonyms.size() > 1) {
1869 				logger.warn("This TaxonName has " + synonyms.size() + " Synonyms: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1870 			}
1871 		}
1872 
1873 		if (sources != null && ! sources.isEmpty()) {
1874 			if (sources.size() == 1) {
1875 				IdentifiableSource source = sources.iterator().next();
1876 				if (source != null) {
1877 					ReferenceBase citation = source.getCitation();
1878 					if (citation != null) {
1879 						result = PesiTransformer.databaseString2Abbreviation(citation.getTitleCache());
1880 					}
1881 				}
1882 			} else if (sources.size() > 1) {
1883 				int count = 1;
1884 				for (IdentifiableSource source : sources) {
1885 					ReferenceBase citation = source.getCitation();
1886 					if (citation != null) {
1887 						if (count > 1) {
1888 							result += "; ";
1889 						}
1890 						result += PesiTransformer.databaseString2Abbreviation(citation.getTitleCache());
1891 						count++;
1892 					}
1893 				}
1894 			} else {
1895 				result = null;
1896 			}
1897 		}
1898 
1899 		} catch (Exception e) {
1900 			e.printStackTrace();
1901 		}
1902 		if ("".equals(result)) {
1903 			return null;
1904 		} else {
1905 			return result;
1906 		}
1907 	}
1908 	
1909 	/**
1910 	 * Returns the <code>LastAction</code> attribute.
1911 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1912 	 * @return The <code>LastAction</code> attribute.
1913 	 * @see MethodMapper
1914 	 */
1915 	@SuppressWarnings("unused")
1916 	private static String getLastAction(TaxonNameBase taxonName) {
1917 		String result = null;
1918 		try {
1919 		Set<Extension> extensions = taxonName.getExtensions();
1920 		for (Extension extension : extensions) {
1921 			if (extension.getType().equals(lastActionExtensionType)) {
1922 				result = extension.getValue();
1923 			}
1924 		}
1925 		} catch (Exception e) {
1926 			e.printStackTrace();
1927 		}
1928 		return result;
1929 	}
1930 	
1931 	/**
1932 	 * Returns the <code>LastActionDate</code> attribute.
1933 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1934 	 * @return The <code>LastActionDate</code> attribute.
1935 	 * @see MethodMapper
1936 	 */
1937 	@SuppressWarnings({ "unused" })
1938 	private static DateTime getLastActionDate(TaxonNameBase taxonName) {
1939 		DateTime result = null;
1940 		try {
1941 		Set<Extension> extensions = taxonName.getExtensions();
1942 		for (Extension extension : extensions) {
1943 			if (extension.getType().equals(lastActionDateExtensionType)) {
1944 				String dateTime = extension.getValue();
1945 				if (dateTime != null) {
1946 					DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
1947 					result = formatter.parseDateTime(dateTime);
1948 				}
1949 			}
1950 		}
1951 		} catch (Exception e) {
1952 			e.printStackTrace();
1953 		}
1954 		return result;
1955 	}
1956 	
1957 	/**
1958 	 * Returns the <code>ExpertName</code> attribute.
1959 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1960 	 * @return The <code>ExpertName</code> attribute.
1961 	 * @see MethodMapper
1962 	 */
1963 	private static String getExpertName(TaxonNameBase taxonName) {
1964 		String result = null;
1965 		try {
1966 		Set<Extension> extensions = taxonName.getExtensions();
1967 		for (Extension extension : extensions) {
1968 			if (extension.getType().equals(expertNameExtensionType)) {
1969 				result = extension.getValue();
1970 			}
1971 		}
1972 		} catch (Exception e) {
1973 			e.printStackTrace();
1974 		}
1975 		return result;
1976 	}
1977 	
1978 	/**
1979 	 * Returns the <code>ExpertFk</code> attribute.
1980 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1981 	 * @param state The {@link PesiExportState PesiExportState}.
1982 	 * @return The <code>ExpertFk</code> attribute.
1983 	 * @see MethodMapper
1984 	 */
1985 	private static Integer getExpertFk(ReferenceBase reference, PesiExportState state) {
1986 		Integer result = state.getDbId(reference);
1987 		return result;
1988 	}
1989 	
1990 	/**
1991 	 * Returns the <code>SpeciesExpertName</code> attribute.
1992 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1993 	 * @return The <code>SpeciesExpertName</code> attribute.
1994 	 * @see MethodMapper
1995 	 */
1996 	@SuppressWarnings("unused")
1997 	private static String getSpeciesExpertName(TaxonNameBase taxonName) {
1998 		String result = null;
1999 		try {
2000 		Set<Extension> extensions = taxonName.getExtensions();
2001 		for (Extension extension : extensions) {
2002 			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2003 				result = extension.getValue();
2004 			}
2005 		}
2006 		} catch (Exception e) {
2007 			e.printStackTrace();
2008 		}
2009 		return result;
2010 	}
2011 	
2012 	/**
2013 	 * Returns the <code>SpeciesExpertFk</code> attribute.
2014 	 * @param reference The {@link ReferenceBase Reference}.
2015 	 * @param state The {@link PesiExportState PesiExportState}.
2016 	 * @return The <code>SpeciesExpertFk</code> attribute.
2017 	 * @see MethodMapper
2018 	 */
2019 	private static Integer getSpeciesExpertFk(ReferenceBase reference, PesiExportState state) {
2020 		Integer result = state.getDbId(reference);
2021 		return result;
2022 	}
2023 
2024 	/**
2025 	 * Returns the <code>SourceFk</code> attribute.
2026 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2027 	 * @param state The {@link PesiExportState PesiExportState}.
2028 	 * @return The <code>SourceFk</code> attribute.
2029 	 */
2030 	@SuppressWarnings("unused")
2031 	private static Integer getSourceFk(TaxonNameBase taxonName, PesiExportState state) {
2032 		Integer result = null;
2033 		
2034 		try {
2035 		TaxonBase taxonBase = getSourceTaxonBase(taxonName);
2036 
2037 		if (taxonBase != null) {
2038 			result = state.getDbId(taxonBase.getSec());
2039 		}
2040 		} catch (Exception e) {
2041 			e.printStackTrace();
2042 		}
2043 		return result;
2044 	}
2045 
2046 	/**
2047 	 * Checks whether a Reference of a TaxonName's TaxonBase is an Auct Reference.
2048 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2049 	 * @param state The {@link PesiExportState PesiExportState}.
2050 	 * @return Whether a Reference of a TaxonName's TaxonBase is an Auct Reference.
2051 	 */
2052 	private static boolean isAuctReference(TaxonNameBase taxonName, PesiExportState state) {
2053 		boolean result = false;
2054 		
2055 		if (isMisappliedName(taxonName)) {
2056 			result = true;
2057 		}
2058 		return result;
2059 	}
2060 
2061 	/**
2062 	 * Determines the TaxonBase of a TaxonName.
2063 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2064 	 * @return The TaxonBase.
2065 	 */
2066 	private static TaxonBase getSourceTaxonBase(TaxonNameBase taxonName) {
2067 		TaxonBase taxonBase = null;
2068 		Set taxa = taxonName.getTaxa();
2069 		if (taxa.size() == 1) {
2070 			taxonBase = CdmBase.deproxy(taxa.iterator().next(), TaxonBase.class);
2071 		} else if (taxa.size() > 1) {
2072 			logger.warn("This TaxonName has " + taxa.size() + " Taxa: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2073 		}
2074 		
2075 		Set synonyms  = taxonName.getSynonyms();
2076 		if (synonyms.size() == 1) {
2077 			taxonBase = CdmBase.deproxy(synonyms.iterator().next(), TaxonBase.class);
2078 		} else if (synonyms.size() > 1) {
2079 			logger.warn("This TaxonName has " + synonyms.size() + " Synonyms: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2080 		}
2081 		return taxonBase;
2082 	}
2083 	
2084 	/**
2085 	 * Returns the CDM to PESI specific export mappings.
2086 	 * @return The {@link PesiExportMapping PesiExportMapping}.
2087 	 */
2088 	private PesiExportMapping getMapping() {
2089 		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2090 		ExtensionType extensionType = null;
2091 		
2092 		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2093 		mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2094 		mapping.addMapper(MethodMapper.NewInstance("GenusOrUninomial", this));
2095 		mapping.addMapper(MethodMapper.NewInstance("InfraGenericEpithet", this));
2096 		mapping.addMapper(MethodMapper.NewInstance("SpecificEpithet", this));
2097 		mapping.addMapper(MethodMapper.NewInstance("InfraSpecificEpithet", this));
2098 		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this));
2099 		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2100 		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));
2101 		mapping.addMapper(MethodMapper.NewInstance("FullName", this));
2102 		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this));
2103 		
2104 		// DisplayName
2105 		extensionType = (ExtensionType)getTermService().find(ErmsTransformer.uuidDisplayName);		
2106 		if (extensionType != null) {
2107 			mapping.addMapper(DbExtensionMapper.NewInstance(extensionType, "DisplayName"));
2108 		} else {
2109 			mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2110 		}
2111 
2112 		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this));
2113 		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this));
2114 		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2115 		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2116 		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this));
2117 
2118 		// QualityStatus (Fk, Cache)
2119 		extensionType = (ExtensionType)getTermService().find(ErmsTransformer.uuidQualityStatus);
2120 		if (extensionType != null) {
2121 			mapping.addMapper(DbExtensionMapper.NewInstance(extensionType, "QualityStatusCache"));
2122 		} else {
2123 			mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this));
2124 		}
2125 		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this)); // PesiTransformer.QualityStatusCache2QualityStatusFk?
2126 		
2127 		mapping.addMapper(MethodMapper.NewInstance("TypeDesignationStatusFk", this));
2128 		mapping.addMapper(MethodMapper.NewInstance("TypeDesignationStatusCache", this));
2129 
2130 		// FossilStatus (Fk, Cache)
2131 		extensionType = (ExtensionType)getTermService().find(ErmsTransformer.uuidFossilStatus);
2132 		if (extensionType != null) {
2133 			mapping.addMapper(DbExtensionMapper.NewInstance(extensionType, "FossilStatusCache"));
2134 		} else {
2135 			mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this));
2136 		}
2137 		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2138 
2139 		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this));
2140 		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2141 		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2142 		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2143 		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this));
2144 		mapping.addMapper(MethodMapper.NewInstance("LastAction", this));
2145 		mapping.addMapper(MethodMapper.NewInstance("LastActionDate", this));
2146 		mapping.addMapper(MethodMapper.NewInstance("ExpertName", this));
2147 		mapping.addMapper(MethodMapper.NewInstance("SpeciesExpertName", this));
2148 
2149 		return mapping;
2150 	}
2151 }