Project

General

Profile

Download (102 KB) Statistics
| Branch: | Revision:
1
/**
2
* Copyright (C) 2009 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.cdm.io.pesi.out;
10

    
11
import java.sql.Connection;
12
import java.sql.PreparedStatement;
13
import java.sql.ResultSet;
14
import java.sql.SQLException;
15
import java.sql.Types;
16
import java.util.ArrayList;
17
import java.util.BitSet;
18
import java.util.HashMap;
19
import java.util.HashSet;
20
import java.util.Iterator;
21
import java.util.List;
22
import java.util.Map;
23
import java.util.Set;
24
import java.util.UUID;
25
import java.util.regex.Matcher;
26
import java.util.regex.Pattern;
27

    
28
import org.apache.commons.lang.StringUtils;
29
import org.apache.log4j.Logger;
30
import org.joda.time.DateTime;
31
import org.joda.time.format.DateTimeFormat;
32
import org.joda.time.format.DateTimeFormatter;
33
import org.springframework.stereotype.Component;
34
import org.springframework.transaction.TransactionStatus;
35

    
36
import eu.etaxonomy.cdm.api.service.TaxonServiceImpl;
37
import eu.etaxonomy.cdm.common.CdmUtils;
38
import eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer;
39
import eu.etaxonomy.cdm.io.common.Source;
40
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
41
import eu.etaxonomy.cdm.io.common.mapping.out.DbConstantMapper;
42
import eu.etaxonomy.cdm.io.common.mapping.out.DbExtensionMapper;
43
import eu.etaxonomy.cdm.io.common.mapping.out.DbLastActionMapper;
44
import eu.etaxonomy.cdm.io.common.mapping.out.DbObjectMapper;
45
import eu.etaxonomy.cdm.io.common.mapping.out.DbStringMapper;
46
import eu.etaxonomy.cdm.io.common.mapping.out.IdMapper;
47
import eu.etaxonomy.cdm.io.common.mapping.out.MethodMapper;
48
import eu.etaxonomy.cdm.io.common.mapping.out.ObjectChangeMapper;
49
import eu.etaxonomy.cdm.io.pesi.erms.ErmsTransformer;
50
import eu.etaxonomy.cdm.model.common.Annotation;
51
import eu.etaxonomy.cdm.model.common.AnnotationType;
52
import eu.etaxonomy.cdm.model.common.CdmBase;
53
import eu.etaxonomy.cdm.model.common.Extension;
54
import eu.etaxonomy.cdm.model.common.ExtensionType;
55
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
56
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
57
import eu.etaxonomy.cdm.model.common.Language;
58
import eu.etaxonomy.cdm.model.common.Marker;
59
import eu.etaxonomy.cdm.model.common.MarkerType;
60
import eu.etaxonomy.cdm.model.common.RelationshipBase;
61
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
62
import eu.etaxonomy.cdm.model.name.NameTypeDesignationStatus;
63
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
64
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
65
import eu.etaxonomy.cdm.model.name.Rank;
66
import eu.etaxonomy.cdm.model.name.TaxonName;
67
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
68
import eu.etaxonomy.cdm.model.reference.Reference;
69
import eu.etaxonomy.cdm.model.taxon.Classification;
70
import eu.etaxonomy.cdm.model.taxon.Synonym;
71
import eu.etaxonomy.cdm.model.taxon.SynonymType;
72
import eu.etaxonomy.cdm.model.taxon.Taxon;
73
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
74
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
75
import eu.etaxonomy.cdm.strategy.cache.HTMLTagRules;
76
import eu.etaxonomy.cdm.strategy.cache.TagEnum;
77
import eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy;
78
import eu.etaxonomy.cdm.strategy.cache.name.TaxonNameDefaultCacheStrategy;
79
import eu.etaxonomy.cdm.strategy.cache.name.ZooNameNoMarkerCacheStrategy;
80

    
81
/**
82
 * The export class for {@link eu.etaxonomy.cdm.model.name.TaxonNameBase TaxonNames}.<p>
83
 * Inserts into DataWarehouse database table <code>Taxon</code>.
84
 * It is divided into four phases:<p><ul>
85
 * <li>Phase 1:	Export of all {@link eu.etaxonomy.cdm.model.name.TaxonName TaxonNames} except some data exported in the following phases.
86
 * <li>Phase 2:	Export of additional data: ParentTaxonFk and TreeIndex.
87
 * <li>Phase 3:	Export of additional data: Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk.
88
 * <li>Phase 4:	Export of Inferred Synonyms.</ul>
89
 *
90
 * @author e.-m.lee
91
 * @since 23.02.2010
92
 */
93
@Component
94
public class PesiTaxonExport extends PesiExportBase {
95

    
96
    private static final long serialVersionUID = -3412722058790200078L;
97
    private static final Logger logger = Logger.getLogger(PesiTaxonExport.class);
98

    
99
	private static final Class<? extends CdmBase> standardMethodParameter = TaxonBase.class;
100

    
101
	private static int modCount = 1000;
102
	private static final String dbTableName = "Taxon";
103
	private static final String dbTableNameSynRel = "RelTaxon";
104
	private static final String dbTableAdditionalSourceRel = "AdditionalTaxonSource";
105

    
106
	private static final String pluralString = "Taxa";
107
	private static final String parentPluralString = "Taxa";
108

    
109
	private PreparedStatement parentTaxonFk_TreeIndex_KingdomFkStmt;
110
	private PreparedStatement parentTaxonFkStmt;
111
	private PreparedStatement rankTypeExpertsUpdateStmt;
112
	private PreparedStatement rankUpdateStmt;
113
	private Integer kingdomFk;
114
	private AnnotationType treeIndexAnnotationType;
115
	private static ExtensionType lastActionExtensionType;
116
	private static ExtensionType lastActionDateExtensionType;
117
	private static ExtensionType expertNameExtensionType;
118
	private static ExtensionType speciesExpertNameExtensionType;
119
	private static ExtensionType cacheCitationExtensionType;
120
	public static TaxonNameDefaultCacheStrategy zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
121
	public static TaxonNameDefaultCacheStrategy nonViralNameStrategy = TaxonNameDefaultCacheStrategy.NewInstance();
122
	private static int currentTaxonId;
123

    
124
	protected AnnotationType getTreeIndexAnnotationType() {
125
		return treeIndexAnnotationType;
126
	}
127

    
128
	protected void setTreeIndexAnnotationType(AnnotationType treeIndexAnnotationType) {
129
		this.treeIndexAnnotationType = treeIndexAnnotationType;
130
	}
131

    
132
	enum NamePosition {
133
		beginning,
134
		end,
135
		between,
136
		alone,
137
		nowhere
138
	}
139

    
140
	public PesiTaxonExport() {
141
		super();
142
	}
143

    
144
	@Override
145
	public Class<? extends CdmBase> getStandardMethodParameter() {
146
		return standardMethodParameter;
147
	}
148

    
149
	@Override
150
	protected void doInvoke(PesiExportState state) {
151
		try {
152
			logger.info("*** Started Making " + pluralString + " ...");
153

    
154
			initPreparedStatements(state);
155

    
156
			// Stores whether this invoke was successful or not.
157
			boolean success = true;
158

    
159
			// PESI: Clear the database table Taxon.
160
//			doDelete(state);
161

    
162
			// Get specific mappings: (CDM) Taxon -> (PESI) Taxon
163
			PesiExportMapping mapping = getMapping();
164
			PesiExportMapping synonymRelMapping = getSynRelMapping();
165
			PesiExportMapping additionalSourceMapping = getAdditionalSourceMapping(state);
166

    
167
			// Initialize the db mapper
168
			mapping.initialize(state);
169
			synonymRelMapping.initialize(state);
170
			additionalSourceMapping.initialize(state);
171

    
172
			// Find extensionTypes
173
			lastActionExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionUuid);
174
			lastActionDateExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtLastActionDate);
175
			expertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
176
			speciesExpertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtSpeciesExpertName);
177
			cacheCitationExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtCacheCitation);
178

    
179
			//Export Taxa..
180
			success &= doPhase01(state, mapping, additionalSourceMapping);
181

    
182
			//"PHASE 1b: Handle names without taxa ...
183
			success &= doNames(state, additionalSourceMapping);
184

    
185

    
186
			// 2nd Round: Add ParentTaxonFk to each taxon
187
			success &= doPhase02(state);
188

    
189
			//PHASE 3: Add Rank data, KingdomFk, TypeNameFk ...
190
			success &= doPhase03(state);
191

    
192
			// 4nd Round: Add TreeIndex to each taxon
193
			success &= doPhase04(state);
194

    
195

    
196
			//"PHASE 5: Creating Inferred Synonyms...
197
			success &= doPhase05(state, mapping, synonymRelMapping);
198

    
199
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
200

    
201
			if (!success){
202
				state.getResult().addError("An error occurred in PesiTaxonExport.doInvoke. Success = false");
203
			}
204
			return;
205
		} catch (Exception e) {
206
			e.printStackTrace();
207
			logger.error(e.getMessage());
208
			state.getResult().addException(e);
209
		}
210
	}
211

    
212

    
213
	private void initPreparedStatements(PesiExportState state) throws SQLException {
214
		initTreeIndexStatement(state);
215
		initRankExpertsUpdateStmt(state);
216
		initRankUpdateStatement(state);
217

    
218
		initParentFkStatement(state);
219
	}
220

    
221
	// Prepare TreeIndex-And-KingdomFk-Statement
222
	private void initTreeIndexStatement(PesiExportState state) throws SQLException {
223
		Connection connection = state.getConfig().getDestination().getConnection();
224
		String parentTaxonFk_TreeIndex_KingdomFkSql = "UPDATE Taxon SET ParentTaxonFk = ?, TreeIndex = ? WHERE TaxonId = ?";
225
		parentTaxonFk_TreeIndex_KingdomFkStmt = connection.prepareStatement(parentTaxonFk_TreeIndex_KingdomFkSql);
226
	}
227

    
228
	// Prepare TreeIndex-And-KingdomFk-Statement
229
	private void initParentFkStatement(PesiExportState state) throws SQLException {
230
		Connection connection = state.getConfig().getDestination().getConnection();
231
		String parentTaxonFkSql = "UPDATE Taxon SET ParentTaxonFk = ? WHERE TaxonId = ?";
232
		parentTaxonFkStmt = connection.prepareStatement(parentTaxonFkSql);
233
	}
234

    
235
	private void initRankUpdateStatement(PesiExportState state) throws SQLException {
236
		Connection connection = state.getConfig().getDestination().getConnection();
237
		String rankSql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, KingdomFk = ? WHERE TaxonId = ?";
238
		rankUpdateStmt = connection.prepareStatement(rankSql);
239
	}
240

    
241
	private void initRankExpertsUpdateStmt(PesiExportState state) throws SQLException {
242
//		String sql_old = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ?, " +
243
//				"ExpertFk = ?, SpeciesExpertFk = ? WHERE TaxonId = ?";
244
		//TODO handle experts GUIDs
245
		Connection connection = state.getConfig().getDestination().getConnection();
246

    
247
		String sql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ? " +
248
				" WHERE TaxonId = ?";
249
		rankTypeExpertsUpdateStmt = connection.prepareStatement(sql);
250
	}
251

    
252
	private boolean doPhase01(PesiExportState state, PesiExportMapping mapping, PesiExportMapping additionalSourceMapping){
253
		int count = 0;
254
		int pastCount = 0;
255
		List<TaxonBase> list;
256
		boolean success = true;
257
		// Get the limit for objects to save within a single transaction.
258
		int limit = state.getConfig().getLimitSave();
259

    
260
		logger.info("PHASE 1: Export Taxa...limit is " + limit);
261
		// Start transaction
262
		TransactionStatus txStatus = startTransaction(true);
263
		if (logger.isDebugEnabled()) {
264
            logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
265
            logger.info("Taking snapshot at the beginning of phase 1 of taxonExport");
266
            //ProfilerController.memorySnapshot();
267
        }
268

    
269
		int partitionCount = 0;
270
		while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)) != null   ) {
271

    
272
			logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
273

    
274
			for (TaxonBase<?> taxon : list) {
275
				doCount(count++, modCount, pluralString);
276
				TaxonName taxonName = taxon.getName();
277

    
278
				TaxonName nvn = CdmBase.deproxy(taxonName);
279
//				System.err.println(nvn.getTitleCache());
280
				if (! nvn.isProtectedTitleCache()){
281
					nvn.setTitleCache(null, false);
282
				}
283
				if (! nvn.isProtectedNameCache()){
284
					nvn.setNameCache(null, false);
285
				}
286
				if (! nvn.isProtectedFullTitleCache()){
287
					nvn.setFullTitleCache(null, false);
288
				}
289
				if (! nvn.isProtectedAuthorshipCache()){
290
					nvn.setAuthorshipCache(null, false);
291
				}
292
				try{
293
    				if (nvn.getRank().equals(Rank.KINGDOM())){
294
    				    if(taxon.isInstanceOf(Taxon.class)){
295
    				        String treeIndex = ((Taxon)taxon).getTaxonNodes().iterator().next().treeIndex();
296
    				        Integer kingdomId = PesiTransformer.pesiKingdomMap.get(nvn.getGenusOrUninomial());
297
    				        state.getTreeIndexKingdomMap().put(treeIndex, kingdomId);
298
    				    }else{
299
    				        logger.warn("Kingdom taxon is not of class Taxon but " + taxon.getClass().getSimpleName() + ": " + nvn.getGenusOrUninomial());
300
    				    }
301
    				}
302
				}catch(NullPointerException e){
303
				    logger.error(nvn.getTitleCache() + " has no Rank!");
304
				    System.err.println(nvn.getTitleCache() + " has no Rank!");
305
				}
306
				//core mapping
307
				success &= mapping.invoke(taxon);
308
				//additional source
309
				if (nvn.getNomenclaturalReference() != null || StringUtils.isNotBlank(nvn.getNomenclaturalMicroReference() )){
310
					additionalSourceMapping.invoke(taxon);
311
				}
312

    
313
				//TODO switch on again, leads to some warnings in ERMS for taxa of not correctly handled kingdoms
314
//				validatePhaseOne(taxon, nvn);
315
			}
316

    
317
			// Commit transaction
318
			commitTransaction(txStatus);
319
			logger.debug("Committed transaction.");
320
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
321
			pastCount = count;
322
			/*logger.warn("Taking snapshot at the end of the loop of phase 1 of taxonExport");
323
			//ProfilerController.memorySnapshot();
324
			*/
325
			// Start transaction
326
			txStatus = startTransaction(true);
327
			if (logger.isDebugEnabled()) {
328
                logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
329
            }
330

    
331
		}
332
		logger.info("No " + pluralString + " left to fetch.");
333

    
334
		// Commit transaction
335
		commitTransaction(txStatus);
336
		txStatus = null;
337
		logger.debug("Committed transaction.");
338
		if (logger.isDebugEnabled()){
339
			logger.debug("Taking snapshot at the end of phase 1 of taxonExport");
340
//			ProfilerController.memorySnapshot();
341
		}
342
		return success;
343
	}
344

    
345

    
346
	private void validatePhaseOne(TaxonBase<?> taxon, TaxonName taxonName) {
347

    
348
	    // Check whether some rules are violated
349
		NomenclaturalCode nomenclaturalCode = taxonName.getNameType();
350
		String genusOrUninomial = taxonName.getGenusOrUninomial();
351
		String specificEpithet = taxonName.getSpecificEpithet();
352
		String infraSpecificEpithet = taxonName.getInfraSpecificEpithet();
353
		String infraGenericEpithet = taxonName.getInfraGenericEpithet();
354
		Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
355

    
356
		if (rankFk == null) {
357
			logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
358
		} else {
359

    
360
			// Check whether infraGenericEpithet is set correctly
361
			// 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
362
			// 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
363

    
364
			int ancestorLevel = 0;
365
			if (taxonName.getRank().equals(Rank.SUBSPECIES())) {
366
				// The accepted taxon two rank levels above should be of rank subgenus
367
				ancestorLevel  = 2;
368
			}
369
			if (taxonName.getRank().equals(Rank.SPECIES())) {
370
				// The accepted taxon one rank level above should be of rank subgenus
371
				ancestorLevel = 1;
372
			}
373
			if (ancestorLevel > 0) {
374
				if (validateAncestorOfSpecificRank(taxon, ancestorLevel, Rank.SUBGENUS())) {
375
					// The child (species or subspecies) of this parent (subgenus) has to have an infraGenericEpithet
376
					if (infraGenericEpithet == null) {
377
						logger.warn("InfraGenericEpithet for (sub)species of infrageneric taxon does not exist even though it should (also valid for Botanical Names?) for: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
378
						// maybe the taxon could be named here
379
					}
380
				}
381
			}
382

    
383
			if (infraGenericEpithet == null && rankFk.intValue() == 190) {
384
				logger.warn("InfraGenericEpithet was not determined although it should exist for rank 190: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
385
			}
386
			if (specificEpithet != null && rankFk.intValue() < 216) {
387
				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() + ")");
388
			}
389
			if (infraSpecificEpithet != null && rankFk.intValue() < 225) {
390
				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() + ")";
391
				if (StringUtils.isNotBlank(infraSpecificEpithet)){
392
					logger.warn(message);
393
				}else{
394
					logger.warn(message);
395
				}
396
			}
397
		}
398
		if (infraSpecificEpithet != null && specificEpithet == null) {
399
			logger.warn("An infraSpecificEpithet was determined, but a specificEpithet was not determined: "  + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
400
		}
401
		if (genusOrUninomial == null) {
402
			logger.warn("GenusOrUninomial was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
403
		}
404
	}
405

    
406
	/**
407
	 * 2nd Round: Add ParentTaxonFk to each taxon and add Biota if not exists
408
	 * @param state
409
	 * @return
410
	 */
411
	private boolean doPhase02(PesiExportState state) {
412
		int count = 0;
413
		int pastCount = 0;
414
		boolean success = true;
415
		if (! state.getConfig().isDoParentAndBiota()){
416
			logger.info ("Ignore PHASE 2: Make ParentFk and Biota...");
417
			return success;
418
		}
419

    
420
		List<Taxon> list;
421

    
422
		// Get the limit for objects to save within a single transaction.
423
		int limit = state.getConfig().getLimitSave();
424

    
425
		insertBiota(state);
426

    
427
		logger.info("PHASE 2: Make ParentFk and Biota ... limit is " + limit);
428
		// Start transaction
429
		TransactionStatus txStatus = startTransaction(true);
430
		int partitionCount = 0;
431

    
432
//		ProfilerController.memorySnapshot();
433
		while ((list = getNextTaxonPartition(Taxon.class, limit, partitionCount++, null)) != null   ) {
434

    
435
			if(logger.isDebugEnabled()) {
436
                logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
437
            }
438
			for (Taxon taxon : list) {
439
				for (TaxonNode node : taxon.getTaxonNodes()){
440
					doCount(count++, modCount, pluralString);
441
					TaxonNode parentNode = node.getParent();
442
					if (parentNode != null && parentNode.getTaxon() != null){  //new root node handling requires has root taxon with taxon == null
443
						int childId = state.getDbId( taxon);
444
						int parentId = state.getDbId(parentNode.getTaxon());
445
						success &= invokeParentTaxonFk(parentId, childId);
446
					}
447
				}
448
			}
449

    
450
			// Commit transaction
451
			commitTransaction(txStatus);
452
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 2)");
453
			pastCount = count;
454
			// Start transaction
455
			txStatus = startTransaction(true);
456
			if (logger.isDebugEnabled()){
457
			    logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
458
			}
459
		}
460
		logger.info("No " + pluralString + " left to fetch.");
461

    
462
		// Commit transaction
463
		commitTransaction(txStatus);
464

    
465
		return success;
466
	}
467

    
468
	/**
469
	 * Inserts the Biota Taxon if not yet exists.
470
	 * @param state
471
	 * @throws SQLException
472
	 */
473
	private void insertBiota(PesiExportState state) {
474
		try {
475
			ResultSet rs = state.getConfig().getDestination().getResultSet("SELECT * FROM Taxon WHERE GenusOrUninomial = 'Biota' ");
476
			if (rs.next() == false){
477
				int biotaId = state.getConfig().getNameIdStart() -1 ;
478
				String sqlInsertBiota = "INSERT INTO Taxon (TaxonId, KingdomFk, RankFk, RankCache, GenusOrUninomial, WebSearchName, WebShowName, FullName, DisplayName, TaxonStatusFk, TaxonStatusCache) " +
479
									       " VALUES (" + biotaId + ",    0,    0,   'Superdomain',   'Biota',          'Biota',  '<i>Biota</i>',   'Biota', '<i>Biota</i>',  1 ,      'accepted')";
480
				state.getConfig().getDestination().update(sqlInsertBiota);
481
			}
482
			rs = null;
483
		} catch (SQLException e) {
484
			logger.warn ("Biota could not be requested or inserted");
485
		}
486
	}
487

    
488
	// 4th round: Add TreeIndex to each taxon
489
	private boolean doPhase04(PesiExportState state) {
490
		boolean success = true;
491

    
492
		logger.info("PHASE 4: Make TreeIndex ... ");
493

    
494
		//TODO test if possible to move to phase 02
495
		String sql = " UPDATE Taxon SET ParentTaxonFk = (SELECT TaxonId FROM Taxon WHERE RankFk = 0) " +
496
				" WHERE (RankFk = 10) and TaxonStatusFk = 1 ";
497
		state.getConfig().getDestination().update(sql);
498

    
499
		state.getConfig().getDestination().update("EXEC dbo.recalculateallstoredpaths");
500

    
501
		logger.info("PHASE 4: Make TreeIndex DONE");
502

    
503
		return success;
504

    
505
	}
506

    
507

    
508
//	// 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
509
//	private boolean doPhase02_OLD(PesiExportState state) {
510
//		boolean success = true;
511
//		boolean includeUnpublished = false;
512
//		if (! state.getConfig().isDoTreeIndex()){
513
//			logger.info ("Ignore PHASE 2: ParentTaxonFk and TreeIndex");
514
//			return success;
515
//		}
516
//
517
//		List<Classification> classificationList = null;
518
//		logger.info("PHASE 2: Add ParenTaxonFk and TreeIndex...");
519
//
520
//		// Specify starting ranks for tree traversing
521
//		rankList.add(Rank.KINGDOM());
522
//		rankList.add(Rank.GENUS());
523
//
524
//		// Specify where to stop traversing (value) when starting at a specific Rank (key)
525
//		rank2endRankMap.put(Rank.GENUS(), null); // Since NULL does not match an existing Rank, traverse all the way down to the leaves
526
//		rank2endRankMap.put(Rank.KINGDOM(), Rank.GENUS()); // excludes rank genus
527
//
528
//		StringBuffer treeIndex = new StringBuffer();
529
//
530
//		// Retrieve list of classifications
531
//		TransactionStatus txStatus = startTransaction(true);
532
//		logger.info("Started transaction for parentFk and treeIndex. Fetching all classifications...");
533
//		classificationList = getClassificationService().listClassifications(null, 0, null, null);
534
//		commitTransaction(txStatus);
535
//		logger.debug("Committed transaction.");
536
//
537
//		logger.info("Fetched " + classificationList.size() + " classification(s).");
538
//
539
//		setTreeIndexAnnotationType(getAnnotationType(uuidTreeIndex, "TreeIndex", "TreeIndex", "TI"));
540
//		List<TaxonNode> rankSpecificRootNodes;
541
//		for (Classification classification : classificationList) {
542
//			for (Rank rank : rankList) {
543
//
544
//				txStatus = startTransaction(true);
545
//				logger.info("Started transaction to fetch all rootNodes specific to Rank " + rank.getLabel() + " ...");
546
//
547
//				rankSpecificRootNodes = getClassificationService().listRankSpecificRootNodes(classification,
548
//				        null, rank, includeUnpublished, null, null, null);
549
//				logger.info("Fetched " + rankSpecificRootNodes.size() + " RootNodes for Rank " + rank.getLabel());
550
//
551
//				commitTransaction(txStatus);
552
//				logger.debug("Committed transaction.");
553
//
554
//				for (TaxonNode rootNode : rankSpecificRootNodes) {
555
//					txStatus = startTransaction(false);
556
//					Rank endRank = rank2endRankMap.get(rank);
557
//					if (endRank != null) {
558
//						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till Rank " + endRank.getLabel() + " ...");
559
//					} else {
560
//						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till leaves are reached ...");
561
//					}
562
//
563
//					TaxonNode newNode = getTaxonNodeService().load(rootNode.getUuid());
564
//
565
//					if (isPesiTaxon(newNode.getTaxon())){
566
//						TaxonNode parentNode = newNode.getParent();
567
//						if (rank.equals(Rank.KINGDOM())) {
568
//							treeIndex = new StringBuffer();
569
//							treeIndex.append("#");
570
//						} else {
571
//							// Get treeIndex from parentNode
572
//							if (parentNode != null) {
573
//								boolean annotationFound = false;
574
//								Set<Annotation> annotations = parentNode.getAnnotations();
575
//								for (Annotation annotation : annotations) {
576
//									AnnotationType annotationType = annotation.getAnnotationType();
577
//									if (annotationType != null && annotationType.equals(getTreeIndexAnnotationType())) {
578
//										treeIndex = new StringBuffer(CdmUtils.Nz(annotation.getText()));
579
//										annotationFound = true;
580
//	//									logger.error("treeIndex: " + treeIndex);
581
//										break;
582
//									}
583
//								}
584
//								if (!annotationFound) {
585
//									// This should not happen because it means that the treeIndex was not set correctly as an annotation to parentNode
586
//									logger.error("TreeIndex could not be read from annotation of TaxonNode: " + parentNode.getUuid() + ", Taxon: " + parentNode.getTaxon().getUuid());
587
//									treeIndex = new StringBuffer();
588
//									treeIndex.append("#");
589
//								}
590
//							} else {
591
//								// TreeIndex could not be determined, but it's unclear how to proceed to generate a correct treeIndex if the parentNode is NULL
592
//								logger.error("ParentNode for RootNode is NULL. TreeIndex could not be determined: " + newNode.getUuid());
593
//								treeIndex = new StringBuffer(); // This just prevents growing of the treeIndex in a wrong manner
594
//								treeIndex.append("#");
595
//							}
596
//						}
597
//						nomenclaturalCode = newNode.getTaxon().getName().getNameType();
598
//						kingdomFk = PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
599
//						traverseTree(newNode, parentNode, treeIndex, endRank, state);
600
//						parentNode =null;
601
//					}else{
602
//						logger.debug("Taxon is not a PESI taxon: " + newNode.getTaxon().getUuid());
603
//					}
604
//
605
//					newNode = null;
606
//
607
//					try {
608
//						commitTransaction(txStatus);
609
//						logger.debug("Committed transaction.");
610
//					} catch (Exception e) {
611
//						logger.error(e.getMessage());
612
//						e.printStackTrace();
613
//					}
614
//
615
//				}
616
//				rankSpecificRootNodes = null;
617
//			}
618
//
619
//		}
620
//
621
//		logger.warn("Taking snapshot at the end of phase 2 of taxonExport");
622
//		//ProfilerController.memorySnapshot();
623
//		return success;
624
//	}
625

    
626
	//PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...
627
	private boolean doPhase03(PesiExportState state) {
628
		int count = 0;
629
		int pastCount = 0;
630
		boolean success = true;
631
		if (! state.getConfig().isDoTreeIndex()){
632
			logger.info ("Ignore PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
633
			return success;
634
		}
635
		// Get the limit for objects to save within a single transaction.
636
		int limit = state.getConfig().getLimitSave();
637

    
638
		List<TaxonBase> list;
639
		logger.info("PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
640
		// Be sure to add rank information, KingdomFk, TypeNameFk, expertFk and speciesExpertFk to every taxonName
641

    
642
		// Start transaction
643
		TransactionStatus txStatus = startTransaction(true);
644
		if (logger.isDebugEnabled()) {
645
            logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
646
        }
647
		int partitionCount = 0;
648
		while ((list = getNextTaxonPartition(TaxonBase.class, limit, partitionCount++, null)) != null) {
649

    
650
			if (logger.isDebugEnabled()) {
651
                logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
652
            }
653
			for (TaxonBase<?> taxon : list) {
654
				TaxonName taxonName = CdmBase.deproxy(taxon.getName());
655
				// Determine expertFk
656
//				Integer expertFk = makeExpertFk(state, taxonName);
657
//
658
//				// Determine speciesExpertFk
659
//				Integer speciesExpertFk = makeSpeciesExpertFk(state, taxonName);
660

    
661
				doCount(count++, modCount, pluralString);
662
				Integer typeNameFk = getTypeNameFk(taxonName, state);
663
				Integer kingdomFk = findKingdomIdFromTreeIndex(taxon, state);
664
				 //       PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
665

    
666
				//TODO why are expertFks needed? (Andreas M.)
667
//				if (expertFk != null || speciesExpertFk != null) {
668
				    NomenclaturalCode nomCode = taxonName.getNameType();
669
				    //is there a reason why we do pass nomCode separately? Before nomCode was class variable, but not clear why and when it was set
670
					invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, nomCode, state.getDbId(taxon),
671
							typeNameFk, kingdomFk, state);
672
//				}
673
			}
674

    
675
			// Commit transaction
676
			commitTransaction(txStatus);
677
			if (logger.isDebugEnabled()){logger.debug("Committed transaction.");}
678
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 3)");
679
			pastCount = count;
680

    
681
			// Start transaction
682
			txStatus = startTransaction(true);
683
			if (logger.isDebugEnabled()) {
684
                logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
685
            }
686
		}
687
		logger.info("No " + pluralString + " left to fetch.");
688

    
689
		// Commit transaction
690
		commitTransaction(txStatus);
691

    
692
		if (logger.isDebugEnabled()){
693
		    logger.debug("Committed transaction.");
694
		    logger.debug("Try to take snapshot at the end of phase 3 of taxonExport, number of partitions: " + partitionCount);
695
		    //ProfilerController.memorySnapshot();
696
		}
697
		return success;
698
	}
699

    
700
    private static Integer findKingdomIdFromTreeIndex(TaxonBase<?> taxonBase,PesiExportState state) {
701
        Taxon taxon;
702
        if (taxonBase instanceof Synonym){
703
            taxon = ((Synonym) taxonBase).getAcceptedTaxon();
704
        }else{
705
            taxon = (Taxon)taxonBase;
706
        }
707
        if (taxon != null){
708
            Set<TaxonNode> nodes = taxon.getTaxonNodes();
709
            if (nodes.size()>1){
710
                logger.warn("The taxon has more then 1 taxon node: " + taxon.getTitleCache() + ". Take arbitrary one.");
711
            }
712
            if (!nodes.isEmpty()){
713
                String treeIndex = nodes.iterator().next().treeIndex();
714

    
715
                Pattern pattern = Pattern.compile("#t[0-9]+#([0-9]+#){3}");
716
                Matcher matcher = pattern.matcher(treeIndex);
717
                Integer kingdomID = null;
718
                if(matcher.find()) {
719
                    String treeIndexKingdom = matcher.group(0);
720
                    kingdomID = state.getTreeIndexKingdomMap().get(treeIndexKingdom);
721
                }else{
722
                    pattern = Pattern.compile("#t[0-9]+#([0-9]+#){2}");
723
                    matcher = pattern.matcher(treeIndex);
724
                    if(matcher.find()) {
725
                        String treeIndexKingdom = matcher.group(0);
726
                        kingdomID = state.getTreeIndexKingdomMap().get(treeIndexKingdom);
727
                    }
728
                }
729
                if(Rank.DOMAIN().equals(taxon.getName().getRank())){
730
                    return null;
731
                }
732
                if(kingdomID == null){
733
                    logger.warn("Kingdom could not be defined for treeindex " + treeIndex);
734
                }
735
                return kingdomID;
736
            } else {
737
                NomenclaturalCode nomenclaturalCode = taxon.getName().getNameType();
738
                logger.warn("The taxon has no nodes: " + taxon.getTitleCache() + ". The kingdom is taken from the nomenclatural code: " + PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
739
                return PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
740
            }
741
        } else{
742
            NomenclaturalCode nomenclaturalCode = taxonBase.getName().getNameType();
743
            logger.warn("Taxon is synonym with no accepted taxon attached: " + taxonBase.getTitleCache() + ". The kingdom is taken from the nomenclatural code: " + PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode) );
744
            return PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
745
        }
746
    }
747

    
748
    //	"PHASE 5: Creating Inferred Synonyms..."
749
	private boolean doPhase05(PesiExportState state, PesiExportMapping mapping, PesiExportMapping synRelMapping) {
750
		int count;
751
		int pastCount;
752
		boolean success = true;
753
		// Get the limit for objects to save within a single transaction.
754
		if (! state.getConfig().isDoInferredSynonyms()){
755
			logger.info ("Ignore PHASE 5: Creating Inferred Synonyms...");
756
			return success;
757
		}
758

    
759
		int limit = state.getConfig().getLimitSave();
760
		// Create inferred synonyms for accepted taxa
761
		logger.info("PHASE 5: Creating Inferred Synonyms...");
762

    
763
		// Determine the count of elements in data warehouse database table Taxon
764
		currentTaxonId = determineTaxonCount(state);
765
		currentTaxonId++;
766

    
767
		count = 0;
768
		pastCount = 0;
769
		int pageSize = limit/10;
770
		int pageNumber = 1;
771
		String inferredSynonymPluralString = "Inferred Synonyms";
772

    
773
		// Start transaction
774
		TransactionStatus txStatus = startTransaction(true);
775
		if (logger.isDebugEnabled()) {
776
            logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
777
        }
778
		List<TaxonBase> taxonList = null;
779

    
780
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SPECIES(), pageSize, pageNumber)).size() > 0) {
781

    
782
		    Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
783

    
784
			if (logger.isDebugEnabled()) {
785
                logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
786
            }
787
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
788
					synRelMapping, taxonList));
789

    
790
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
791
			// Commit transaction
792
			commitTransaction(txStatus);
793
			if (logger.isDebugEnabled()){logger.debug("Committed transaction.");}
794
			logger.info("Exported " + (taxonList.size()) + " " + inferredSynonymPluralString + ". Total: " + count);
795
			//pastCount = count;
796

    
797
			// Save Rank Data and KingdomFk for inferred synonyms
798
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
799
			    TaxonName taxonName = inferredSynonymsDataToBeSaved.get(taxonFk);
800
                NomenclaturalCode nomCode = taxonName.getNameType(); //nomCode was class variable before, not sure if this was important
801
                invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomCode, taxonFk, kingdomFk, state);
802
			}
803

    
804
			// Start transaction
805
			txStatus = startTransaction(true);
806
			if (logger.isDebugEnabled()) {
807
                logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
808
            }
809

    
810
			// Increment pageNumber
811
			pageNumber++;
812
		}
813
		taxonList = null;
814
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SUBSPECIES(), pageSize, pageNumber)).size() > 0) {
815
			Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
816

    
817
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
818
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
819
					synRelMapping, taxonList));
820

    
821
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
822
			// Commit transaction
823
			commitTransaction(txStatus);
824
			logger.debug("Committed transaction.");
825
			logger.info("Exported " + taxonList.size()+ " " + inferredSynonymPluralString + ". Total: " + count);
826
			//pastCount = count;
827

    
828
			// Save Rank Data and KingdomFk for inferred synonyms
829
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
830
			    TaxonName taxonName = inferredSynonymsDataToBeSaved.get(taxonFk);
831
			    NomenclaturalCode nomCode = taxonName.getNameType(); //nomCode was class variable before, not sure if this was important
832
				invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomCode, taxonFk, kingdomFk, state);
833
			}
834

    
835
			// Start transaction
836
			txStatus = startTransaction(true);
837
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
838

    
839
			// Increment pageNumber
840
			pageNumber++;
841
			inferredSynonymsDataToBeSaved = null;
842
		}
843
		if (taxonList.size() == 0) {
844
			logger.info("No " + parentPluralString + " left to fetch.");
845
		}
846

    
847
		taxonList = null;
848
//		logger.warn("Taking snapshot at the end of phase 5 of taxonExport");
849
//		ProfilerController.memorySnapshot();
850

    
851
		// Commit transaction
852
		commitTransaction(txStatus);
853
		System.gc();
854
		logger.debug("Taking snapshot at the end of phase 5 after gc() of taxonExport");
855
		//ProfilerController.memorySnapshot();
856
		logger.debug("Committed transaction.");
857
		return success;
858
	}
859

    
860
	/**
861
	 * @param state
862
	 * @param mapping
863
	 * @param synRelMapping
864
	 * @param currentTaxonId
865
	 * @param taxonList
866
	 * @param inferredSynonymsDataToBeSaved
867
	 * @return
868
	 */
869
	private Map<Integer, TaxonName> createInferredSynonymsForTaxonList(PesiExportState state,
870
			PesiExportMapping mapping, PesiExportMapping synRelMapping,	 List<TaxonBase> taxonList) {
871

    
872
		Taxon acceptedTaxon;
873
		Classification classification = null;
874
		List<Synonym> inferredSynonyms = null;
875
		boolean localSuccess = true;
876

    
877
		Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
878

    
879
		for (TaxonBase<?> taxonBase : taxonList) {
880

    
881
			if (taxonBase.isInstanceOf(Taxon.class)) { // this should always be the case since we should have fetched accepted taxon only, but you never know...
882
				acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
883
				TaxonName taxonName = acceptedTaxon.getName();
884

    
885
				if (taxonName.isZoological()) {
886
					kingdomFk = findKingdomIdFromTreeIndex(taxonBase, state);
887

    
888
					Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
889
					TaxonNode singleNode = null;
890

    
891
					if (taxonNodes.size() > 0) {
892
						// Determine the classification of the current TaxonNode
893

    
894
						singleNode = taxonNodes.iterator().next();
895
						if (singleNode != null) {
896
							classification = singleNode.getClassification();
897
						} else {
898
							logger.error("A TaxonNode belonging to this accepted Taxon is NULL: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() +")");
899
						}
900
					} else {
901
						// Classification could not be determined directly from this TaxonNode
902
						// The stored classification from another TaxonNode is used. It's a simple, but not a failsafe fallback solution.
903
						if (taxonNodes.size() == 0) {
904
							//logger.error("Classification could not be determined directly from this Taxon: " + acceptedTaxon.getUuid() + " is misapplication? "+acceptedTaxon.isMisapplication()+ "). The classification of the last taxon is used");
905
						}
906
					}
907

    
908
					if (classification != null) {
909
						try{
910
						    TaxonName name = acceptedTaxon.getName();
911
							//if (name.isSpecies() || name.isInfraSpecific()){
912
								inferredSynonyms  = getTaxonService().createAllInferredSynonyms(acceptedTaxon, classification, true);
913
							//}
914
//								inferredSynonyms = getTaxonService().createInferredSynonyms(classification, acceptedTaxon, SynonymType.INFERRED_GENUS_OF());
915
							if (inferredSynonyms != null) {
916
								for (Synonym synonym : inferredSynonyms) {
917
//									TaxonName synonymName = synonym.getName();
918
									MarkerType markerType =getUuidMarkerType(PesiTransformer.uuidMarkerGuidIsMissing, state);
919
									synonym.addMarker(Marker.NewInstance(markerType, true));
920
									// Both Synonym and its TaxonName have no valid Id yet
921
									synonym.setId(currentTaxonId++);
922

    
923

    
924
									localSuccess &= mapping.invoke(synonym);
925
									//get SynonymRelationship and export
926
									if (synonym.getAcceptedTaxon() == null ){
927
										IdentifiableSource source = synonym.getSources().iterator().next();
928
										if (source.getIdNamespace().contains("Potential combination")){
929
											acceptedTaxon.addSynonym(synonym, SynonymType.POTENTIAL_COMBINATION_OF());
930
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to potential combination");
931
										} else if (source.getIdNamespace().contains("Inferred Genus")){
932
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_GENUS_OF());
933
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred genus");
934
										} else if (source.getIdNamespace().contains("Inferred Epithet")){
935
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_EPITHET_OF());
936
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred epithet");
937
										} else{
938
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_SYNONYM_OF());
939
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred synonym");
940
										}
941

    
942
										localSuccess &= synRelMapping.invoke(synonym);
943
										if (!localSuccess) {
944
											logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
945
										}
946
									} else {
947
										localSuccess &= synRelMapping.invoke(synonym);
948
										if (!localSuccess) {
949
											logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
950
										} else {
951
											logger.info("Synonym relationship successfully exported: " + synonym.getTitleCache() + "  " +acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
952
										}
953
									}
954

    
955
									inferredSynonymsDataToBeSaved.put(synonym.getId(), synonym.getName());
956
								}
957
							}
958
						}catch(Exception e){
959
							logger.error(e.getMessage());
960
							e.printStackTrace();
961
						}
962
					} else {
963
						logger.error("Classification is NULL. Inferred Synonyms could not be created for this Taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() + ")");
964
					}
965
				} else {
966
//							logger.error("TaxonName is not a ZoologicalName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
967
				}
968
			} else {
969
				logger.error("This TaxonBase is not a Taxon even though it should be: " + taxonBase.getUuid() + " (" + taxonBase.getTitleCache() + ")");
970
			}
971
		}
972
		return inferredSynonymsDataToBeSaved;
973
	}
974

    
975

    
976
	/**
977
	 * Handles names that do not appear in taxa
978
	 * @param state
979
	 * @param mapping
980
	 */
981
	private boolean doNames(PesiExportState state, PesiExportMapping additionalSourceMapping)  throws SQLException {
982

    
983
		boolean success = true;
984
		if (! state.getConfig().isDoPureNames()){
985
			logger.info ("Ignore PHASE 1b: PureNames");
986
			return success;
987
		}
988

    
989
		try {
990
			PesiExportMapping mapping = getPureNameMapping(state);
991
			mapping.initialize(state);
992
			int count = 0;
993
			int pastCount = 0;
994
			List<TaxonName> list;
995
			success = true;
996
			// Get the limit for objects to save within a single transaction.
997
			int limit = state.getConfig().getLimitSave();
998

    
999
			logger.info("PHASE 1b: Export Pure Names ...");
1000
			// Start transaction
1001
			TransactionStatus txStatus = startTransaction(true);
1002
			logger.info("Started new transaction for Pure Names. Fetching some " + pluralString + " (max: " + limit + ") ...");
1003

    
1004
			int partitionCount = 0;
1005
			while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null   ) {
1006

    
1007
				logger.info("Fetched " + list.size() + " names without taxa. Exporting...");
1008
				for (TaxonName taxonName : list) {
1009
					doCount(count++, modCount, pluralString);
1010
					success &= mapping.invoke(taxonName);
1011
					//additional source
1012
					if (taxonName.getNomenclaturalReference() != null || StringUtils.isNotBlank(taxonName.getNomenclaturalMicroReference() )){
1013
						additionalSourceMapping.invoke(taxonName);
1014
					}
1015
				}
1016

    
1017
				// Commit transaction
1018
				commitTransaction(txStatus);
1019
				logger.debug("Committed transaction.");
1020
				logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
1021
				pastCount = count;
1022

    
1023
				// Start transaction
1024
				txStatus = startTransaction(true);
1025
				logger.info("Started new transaction for PureNames. Fetching some " + pluralString + " (max: " + limit + ") ...");
1026
			}
1027
			logger.info("No " + pluralString + " left to fetch.");
1028

    
1029
			// Commit transaction
1030
			commitTransaction(txStatus);
1031
			logger.debug("Committed transaction.");
1032
		} catch (Exception e) {
1033
			logger.error("Error occurred in pure name export");
1034
			e.printStackTrace();
1035
			success = false;
1036
		}
1037
		return success;
1038
	}
1039

    
1040
	/**
1041
	 * Determines the current number of entries in the DataWarehouse database table <code>Taxon</code>.
1042
	 * @param state The {@link PesiExportState PesiExportState}.
1043
	 * @return The count.
1044
	 */
1045
	private Integer determineTaxonCount(PesiExportState state) {
1046
		Integer result = null;
1047
		PesiExportConfigurator pesiConfig = state.getConfig();
1048

    
1049
		String sql;
1050
		Source destination =  pesiConfig.getDestination();
1051
		sql = "SELECT max(taxonId) FROM Taxon";
1052
		destination.setQuery(sql);
1053
		ResultSet resultSet = destination.getResultSet();
1054
		try {
1055
			resultSet.next();
1056
			result = resultSet.getInt(1);
1057
		} catch (SQLException e) {
1058
			logger.error("TaxonCount could not be determined: " + e.getMessage());
1059
			e.printStackTrace();
1060
		}
1061
		resultSet = null;
1062
		return result;
1063
	}
1064

    
1065
	/**
1066
	 * Checks whether a parent at specific level has a specific Rank.
1067
	 * @param taxonName A {@link TaxonNameBase TaxonName}.
1068
	 * @param level The ancestor level.
1069
	 * @param ancestorRank The ancestor rank.
1070
	 * @return Whether a parent at a specific level has a specific Rank.
1071
	 */
1072
	private boolean validateAncestorOfSpecificRank(TaxonBase<?> taxonBase, int level, Rank ancestorRank) {
1073
		boolean result = false;
1074
		TaxonNode parentNode = null;
1075
		if (taxonBase.isInstanceOf(Taxon.class)){
1076
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1077
			// Get ancestor Taxon via TaxonNode
1078
			Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();
1079
			if (taxonNodes.size() == 1) {
1080
				TaxonNode taxonNode = taxonNodes.iterator().next();
1081
				if (taxonNode != null) {
1082
					for (int i = 0; i < level; i++) {
1083
						if (taxonNode != null) {
1084
							taxonNode  = taxonNode.getParent();
1085
						}
1086
					}
1087
					parentNode = taxonNode;
1088
				}
1089
			} else if (taxonNodes.size() > 1) {
1090
				logger.error("This taxon has " + taxonNodes.size() + " taxonNodes: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1091
			}
1092
		}
1093
		//compare
1094
		if (parentNode != null) {
1095
			TaxonNode node = CdmBase.deproxy(parentNode, TaxonNode.class);
1096
			Taxon parentTaxon = node.getTaxon();
1097
			if (parentTaxon != null) {
1098
				TaxonName parentTaxonName = parentTaxon.getName();
1099
				if (parentTaxonName != null && parentTaxonName.getRank().equals(ancestorRank)) {
1100
					result = true;
1101
				}
1102
			} else if (parentNode.treeIndex().matches("#t\\d+#\\d+#")) {
1103
				//do nothing (is root node)
1104
			} else {
1105
				logger.error("This TaxonNode has no Taxon: " + node.getUuid());
1106
			}
1107
		}
1108
		return result;
1109
	}
1110

    
1111
	/**
1112
	 * Returns the AnnotationType for a given UUID.
1113
	 * @param uuid The Annotation UUID.
1114
	 * @param label The Annotation label.
1115
	 * @param text The Annotation text.
1116
	 * @param labelAbbrev The Annotation label abbreviation.
1117
	 * @return The AnnotationType.
1118
	 */
1119
	protected AnnotationType getAnnotationType(UUID uuid, String label, String text, String labelAbbrev){
1120
		AnnotationType annotationType = (AnnotationType)getTermService().find(uuid);
1121
		if (annotationType == null) {
1122
			annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
1123
			annotationType.setUuid(uuid);
1124
//			annotationType.setVocabulary(AnnotationType.EDITORIAL().getVocabulary());
1125
			getTermService().save(annotationType);
1126
		}
1127
		return annotationType;
1128
	}
1129

    
1130
	/**
1131
	 * Traverses the classification recursively and stores determined values for every Taxon.
1132
	 * @param childNode The {@link TaxonNode TaxonNode} to process.
1133
	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1134
	 * @param treeIndex The TreeIndex at the current level.
1135
	 * @param fetchLevel Rank to stop fetching at.
1136
	 * @param state The {@link PesiExportState PesiExportState}.
1137
	 */
1138
	private void traverseTree(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, Rank fetchLevel, PesiExportState state) {
1139
		// Traverse all branches from this childNode until specified fetchLevel is reached.
1140
		StringBuffer localTreeIndex = new StringBuffer(treeIndex);
1141
		Taxon childTaxon = childNode.getTaxon();
1142
		if (childTaxon != null) {
1143
			if (isPesiTaxon(childTaxon)){
1144
				Integer taxonId = state.getDbId(childTaxon);
1145
				TaxonName childName = childTaxon.getName();
1146
				if (taxonId != null) {
1147
					Rank childRank = childName.getRank();
1148
					if (childRank != null) {
1149
						if (! childRank.equals(fetchLevel)) {
1150

    
1151
							localTreeIndex.append(taxonId + "#");
1152

    
1153
							saveData(childNode, parentNode, localTreeIndex, state, taxonId);
1154

    
1155
							// Store treeIndex as annotation for further use
1156
							Annotation annotation = Annotation.NewInstance(localTreeIndex.toString(), getTreeIndexAnnotationType(), Language.DEFAULT());
1157
							childNode.addAnnotation(annotation);
1158

    
1159
							for (TaxonNode newNode : childNode.getChildNodes()) {
1160
								if (newNode.getTaxon() != null && isPesiTaxon(newNode.getTaxon())){
1161
									traverseTree(newNode, childNode, localTreeIndex, fetchLevel, state);
1162
								}
1163
							}
1164

    
1165
						} else {
1166
	//						logger.debug("Target Rank " + fetchLevel.getLabel() + " reached");
1167
							return;
1168
						}
1169
					} else {
1170
						logger.error("Rank is NULL. FetchLevel can not be checked: " + childName.getUuid() + " (" + childName.getTitleCache() + ")");
1171
					}
1172
				} else {
1173
					logger.error("Taxon can not be found in state: " + childTaxon.getUuid() + " (" + childTaxon.getTitleCache() + ")");
1174
				}
1175
			}else{
1176
				if (logger.isDebugEnabled()){
1177
					logger.debug("Taxon is not a PESI taxon: " + childTaxon.getUuid());
1178
				}
1179
			}
1180
		} else {
1181
			logger.error("Taxon is NULL for TaxonNode: " + childNode.getUuid());
1182
		}
1183
	}
1184

    
1185
	/**
1186
	 * Stores values in database for every recursive round.
1187
	 * @param childNode The {@link TaxonNode TaxonNode} to process.
1188
	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1189
	 * @param treeIndex The TreeIndex at the current level.
1190
	 * @param state The {@link PesiExportState PesiExportState}.
1191
	 * @param currentTaxonFk The TaxonFk to store the values for.
1192
	 */
1193
	private void saveData(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, PesiExportState state, Integer currentTaxonFk) {
1194
		// We are differentiating kingdoms by the nomenclatural code for now.
1195
		// This needs to be handled in a better way as soon as we know how to differentiate between more kingdoms.
1196
		Taxon childTaxon = childNode.getTaxon();
1197
		if (isPesiTaxon(childTaxon)) {
1198
			TaxonBase<?> parentTaxon = null;
1199
			if (parentNode != null) {
1200
				parentTaxon = parentNode.getTaxon();
1201
			}
1202

    
1203
			invokeParentTaxonFkAndTreeIndex(state.getDbId(parentTaxon), currentTaxonFk,	treeIndex);
1204
		}
1205
	}
1206

    
1207
	/**
1208
	 * Inserts values into the Taxon database table.
1209
	 *
1210
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1211
	 * @param state The {@link PesiExportState PesiExportState}.
1212
	 * @param stmt The prepared statement.
1213
	 * @return Whether save was successful or not.
1214
	 */
1215
	protected boolean invokeParentTaxonFkAndTreeIndex(Integer parentTaxonFk, Integer currentTaxonFk, StringBuffer treeIndex) {
1216
		try {
1217
			if (parentTaxonFk != null) {
1218
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(1, parentTaxonFk);
1219
			} else {
1220
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(1, null);
1221
			}
1222

    
1223
			if (treeIndex != null) {
1224
				parentTaxonFk_TreeIndex_KingdomFkStmt.setString(2, treeIndex.toString());
1225
			} else {
1226
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(2, null);
1227
			}
1228

    
1229
			if (currentTaxonFk != null) {
1230
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(3, currentTaxonFk);
1231
			} else {
1232
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(3, null);
1233
			}
1234

    
1235
			parentTaxonFk_TreeIndex_KingdomFkStmt.executeUpdate();
1236
			return true;
1237
		} catch (SQLException e) {
1238
			logger.error("ParentTaxonFk (" + (parentTaxonFk ==null? "-":parentTaxonFk) + ") and TreeIndex could not be inserted into database for taxon "+ (currentTaxonFk == null? "-" :currentTaxonFk) + ": " + e.getMessage());
1239
			e.printStackTrace();
1240
			return false;
1241
		}
1242
	}
1243

    
1244
	protected boolean invokeParentTaxonFk(Integer parentId, Integer childId) {
1245
		try {
1246
			parentTaxonFkStmt.setInt(1, parentId);
1247
			parentTaxonFkStmt.setInt(2, childId);
1248
			parentTaxonFkStmt.executeUpdate();
1249
			return true;
1250
		} catch (SQLException e) {
1251
			logger.warn("ParentTaxonFk (" + (parentId ==null? "-":parentId) + ") could not be inserted into database "
1252
			        + "for taxon "+ (childId == null? "-" :childId) + ": " + e.getMessage());
1253
			e.printStackTrace();
1254
			return false;
1255
		}
1256
	}
1257

    
1258

    
1259
	/**
1260
	 * Inserts Rank data and KingdomFk into the Taxon database table.
1261
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1262
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1263
	 * @param taxonFk The TaxonFk to store the values for.
1264
	 * @param state
1265
	 * @param kindomFk The KingdomFk.
1266
	 * @return Whether save was successful or not.
1267
	 */
1268
	private boolean invokeRankDataAndKingdomFk(TaxonName taxonName, NomenclaturalCode nomenclaturalCode, Integer taxonFk, Integer kingdomFk, PesiExportState state) {
1269
		try {
1270
			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1271
			if (rankFk != null) {
1272
				rankUpdateStmt.setInt(1, rankFk);
1273
			} else {
1274
				rankUpdateStmt.setObject(1, null);
1275
			}
1276

    
1277
			String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
1278
			if (rankCache != null) {
1279
				rankUpdateStmt.setString(2, rankCache);
1280
			} else {
1281
				rankUpdateStmt.setObject(2, null);
1282
			}
1283

    
1284
			if (kingdomFk != null) {
1285

    
1286
				rankUpdateStmt.setInt(3, kingdomFk);
1287
			} else {
1288
				rankUpdateStmt.setObject(3, null);
1289
			}
1290

    
1291
			if (taxonFk != null) {
1292
				rankUpdateStmt.setInt(4, taxonFk);
1293
			} else {
1294
				rankUpdateStmt.setObject(4, null);
1295
			}
1296

    
1297
			rankUpdateStmt.executeUpdate();
1298
			return true;
1299
		} catch (SQLException e) {
1300
			logger.error("Data (RankFk, RankCache, KingdomFk) could not be inserted into database: " + e.getMessage());
1301
			e.printStackTrace();
1302
			return false;
1303
		}
1304
	}
1305

    
1306
	/**
1307
	 * Inserts Rank data, TypeNameFk, KingdomFk, expertFk and speciesExpertFk into the Taxon database table.
1308
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1309
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1310
	 * @param taxonFk The TaxonFk to store the values for.
1311
	 * @param typeNameFk The TypeNameFk.
1312
	 * @param state
1313
	 * @param kindomFk The KingdomFk.
1314
	 * @param expertFk The ExpertFk.
1315
	 * @param speciesExpertFk The SpeciesExpertFk.
1316
	 * @return Whether save was successful or not.
1317
	 */
1318
	private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonName taxonName, NomenclaturalCode nomenclaturalCode,
1319
			Integer taxonFk, Integer typeNameFk, Integer kingdomFk, PesiExportState state) {
1320

    
1321
	    Integer rankFk = null;
1322
	    try {
1323
			int index = 1;
1324
			rankFk = getRankFk(taxonName, nomenclaturalCode);
1325
			if (rankFk != null) {
1326
				rankTypeExpertsUpdateStmt.setInt(index++, rankFk);
1327
			} else {
1328
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1329
			}
1330

    
1331
			String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
1332
			if (rankCache != null) {
1333
				rankTypeExpertsUpdateStmt.setString(index++, rankCache);
1334
			} else {
1335
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1336
			}
1337

    
1338
			if (typeNameFk != null) {
1339
				rankTypeExpertsUpdateStmt.setInt(index++, typeNameFk);
1340
			} else {
1341
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1342
			}
1343

    
1344
			if (kingdomFk != null) {
1345
				rankTypeExpertsUpdateStmt.setInt(index++, kingdomFk);
1346
			} else {
1347
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1348
			}
1349

    
1350
//			if (expertFk != null) {
1351
//				rankTypeExpertsUpdateStmt.setInt(5, expertFk);
1352
//			} else {
1353
//				rankTypeExpertsUpdateStmt.setObject(5, null);
1354
//			}
1355
//
1356
//			//TODO handle experts GUIDS
1357
//			if (speciesExpertFk != null) {
1358
//				rankTypeExpertsUpdateStmt.setInt(6, speciesExpertFk);
1359
//			} else {
1360
//				rankTypeExpertsUpdateStmt.setObject(6, null);
1361
//			}
1362
//
1363
			if (taxonFk != null) {
1364
				rankTypeExpertsUpdateStmt.setInt(index++, taxonFk);
1365
			} else {
1366
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1367
			}
1368

    
1369
			rankTypeExpertsUpdateStmt.executeUpdate();
1370
			return true;
1371
		} catch (SQLException e) {
1372
			logger.error("Data could not be inserted into database: " + e.getMessage() + "; rankFk = " + rankFk + "; kingdomFk = " + kingdomFk );
1373
			e.printStackTrace();
1374
			return false;
1375
		} catch (Exception e) {
1376
			logger.error("Some exception occurred: " + e.getMessage() + "; rankFk = " + rankFk + "; kingdomFk = " + kingdomFk);
1377
			e.printStackTrace();
1378
			return false;
1379
		}
1380
	}
1381

    
1382
	/**
1383
	 * Deletes all entries of database tables related to <code>Taxon</code>.
1384
	 * @param state The {@link PesiExportState PesiExportState}.
1385
	 * @return Whether the delete operation was successful or not.
1386
	 */
1387
	protected boolean doDelete(PesiExportState state) {
1388
		PesiExportConfigurator pesiConfig = state.getConfig();
1389

    
1390
		String sql;
1391
		Source destination =  pesiConfig.getDestination();
1392

    
1393
		// Clear Taxon
1394
		sql = "DELETE FROM " + dbTableName;
1395
		destination.setQuery(sql);
1396
		destination.update(sql);
1397
		return true;
1398
	}
1399

    
1400
	/**
1401
	 * Creates the kingdom fk.
1402
	 * @param taxonName
1403
	 * @return
1404
	 */
1405
	@SuppressWarnings("unused")  //used by mapper
1406
	private static Integer getKingdomFk(TaxonName taxonName){
1407
		return PesiTransformer.nomenclaturalCode2Kingdom(taxonName.getNameType());
1408
	}
1409

    
1410
	/**
1411
	 * Creates the parent fk.
1412
	 * @param taxonName
1413
	 * @return
1414
	 */
1415
	@SuppressWarnings("unused")  //used by mapper
1416
	private static Integer getParentTaxonFk(TaxonBase<?> taxonBase, PesiExportState state){
1417
		if (taxonBase.isInstanceOf(Taxon.class)){
1418
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1419
			if (! isMisappliedName(taxon)){
1420
				Set<TaxonNode> nodes = taxon.getTaxonNodes();
1421
				if (nodes.size() == 0){
1422
					if (taxon.getName().getRank().isLower(Rank.KINGDOM())){
1423
						logger.warn("Accepted taxon has no parent. " + taxon.getTitleCache() + ", " +  taxon.getUuid());
1424
					}
1425
				}else if (nodes.size() > 1){
1426
					logger.warn("Taxon has more than 1 node attached. This is not supported by PESI export." +  taxon.getTitleCache() + ", " +  taxon.getUuid());
1427
				}else{
1428
					Taxon parent =nodes.iterator().next().getParent().getTaxon();
1429
					return state.getDbId(parent);
1430
				}
1431
			}
1432
		}
1433
		return null;
1434
	}
1435

    
1436
	/**
1437
	 * Returns the rankFk for the taxon name based on the names nomenclatural code.
1438
	 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1439
	 * @param taxonName
1440
	 * @return
1441
	 */
1442
	@SuppressWarnings("unused")  //used by mapper
1443
	private static Integer getRankFk(TaxonName taxonName) {
1444
		return getRankFk(taxonName, taxonName.getNameType());
1445
	}
1446

    
1447

    
1448
	/**
1449
	 * Returns the <code>RankFk</code> attribute.
1450
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1451
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1452
	 * @return The <code>RankFk</code> attribute.
1453
	 * @see MethodMapper
1454
	 */
1455
	private static Integer getRankFk(TaxonName taxonName, NomenclaturalCode nomenclaturalCode) {
1456
		Integer result = null;
1457
		try {
1458
			if (nomenclaturalCode != null) {
1459
				if (taxonName != null) {
1460
					if (taxonName.getRank() == null) {
1461
						logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1462
					} else {
1463
						result = PesiTransformer.rank2RankId(taxonName.getRank(), PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
1464
					}
1465
					if (result == null) {
1466
						logger.warn("Rank could not be determined for PESI-Kingdom-Id " + PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode) + " and TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1467
					}
1468
				}
1469
			}
1470
		} catch (Exception e) {
1471
			e.printStackTrace();
1472
		}
1473
		return result;
1474
	}
1475

    
1476
	/**
1477
	 * Returns the rank cache for the taxon name based on the names nomenclatural code.
1478
	 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1479
	 */
1480
	@SuppressWarnings("unused")  //used by mapper
1481
	private static String getRankCache(TaxonName taxonName, PesiExportState state) {
1482
		return getRankCache(taxonName, taxonName.getNameType(), state);
1483
	}
1484

    
1485
	/**
1486
	 * Returns the <code>RankCache</code> attribute.
1487
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1488
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1489
	 * @param state
1490
	 * @return The <code>RankCache</code> attribute.
1491
	 * @see MethodMapper
1492
	 */
1493
	private static String getRankCache(TaxonName taxonName, NomenclaturalCode nomenclaturalCode, PesiExportState state) {
1494
	    List<TaxonNode> nodes = getTaxonNodes(taxonName);
1495
	    if (Rank.DOMAIN().equals(taxonName.getRank())){
1496
            return state.getTransformer().getCacheByRankAndKingdom(Rank.DOMAIN(), null);
1497
        }else if (!nodes.isEmpty()) {
1498
            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), findKingdomIdFromTreeIndex(nodes.iterator().next().getTaxon(), state)); //PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
1499
        }else if (nomenclaturalCode != null){
1500
            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
1501
        }else{
1502
			logger.warn("No nomenclatural code defined for name " + taxonName.getUuid());
1503
			return null;
1504
		}
1505
	}
1506

    
1507
	/**
1508
     * @param taxonName
1509
     * @return
1510
     */
1511
    private static List<TaxonNode> getTaxonNodes(TaxonName taxonName) {
1512
        List<TaxonNode> result = new ArrayList<>();
1513
        for (TaxonBase<?> tb:taxonName.getTaxonBases()){
1514
            Taxon taxon;
1515
            //TODO handle ERMS taxon relationships
1516
            if (tb.isInstanceOf(Taxon.class)){
1517
                taxon = CdmBase.deproxy(tb, Taxon.class);
1518
            }else{
1519
                taxon = CdmBase.deproxy(tb, Synonym.class).getAcceptedTaxon();
1520
            }
1521
            if (taxon != null){
1522
                for (TaxonNode node : taxon.getTaxonNodes()){
1523
                    result.add(node);
1524
                }
1525
            }
1526
        }
1527
        return result;
1528
    }
1529

    
1530
    /**
1531
	 * Returns the <code>DisplayName</code> attribute.
1532
	 * @param taxon The {@link TaxonBase Taxon}.
1533
	 * @return The <code>DisplayName</code> attribute.
1534
	 * @see MethodMapper
1535
	 */
1536
	@SuppressWarnings("unused")  //used by Mapper
1537
	private static String getDisplayName(TaxonBase<?> taxon) {
1538
		TaxonName taxonName = taxon.getName();
1539
		String result = getDisplayName(taxonName);
1540
		if (isMisappliedName(taxon)){
1541
			result = result + " " + getAuthorString(taxon);
1542
		}
1543
		return result;
1544
	}
1545

    
1546
	/**
1547
	 * Returns the <code>AuthorString</code> attribute.
1548
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1549
	 * @return The <code>AuthorString</code> attribute.
1550
	 * @see MethodMapper
1551
	 */
1552
	//used by mapper
1553
	protected static String getAuthorString(TaxonBase<?> taxon) {
1554
		try {
1555
			String result = null;
1556
			boolean isNonViralName = false;
1557
			String authorshipCache = null;
1558
			TaxonName taxonName = taxon.getName();
1559
			if (taxonName != null && taxonName.isNonViral()){
1560
				authorshipCache = taxonName.getAuthorshipCache();
1561
				isNonViralName = true;
1562
			}
1563
			result = authorshipCache;
1564

    
1565
			// For a misapplied names there are special rules
1566
			if (isMisappliedName(taxon)){
1567
				if (taxon.getSec() != null){
1568
					String secTitle = taxon.getSec().getTitleCache();
1569
					if (! secTitle.startsWith("auct")){
1570
						secTitle = "sensu " + secTitle;
1571
					}else if (secTitle.equals("auct")){  //may be removed once the title cache is generated correctly for references with title auct. #
1572
						secTitle = "auct.";
1573
					}
1574
					return secTitle;
1575
				}else if (StringUtils.isBlank(authorshipCache)) {
1576
					// Set authorshipCache to "auct."
1577
					result = PesiTransformer.AUCT_STRING;
1578
				}else{
1579
					result = PesiTransformer.AUCT_STRING;
1580
//					result = authorshipCache;
1581
				}
1582
			}
1583

    
1584
			if (taxonName == null){
1585
				logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1586
			}else if (! isNonViralName){
1587
				logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1588
			}
1589

    
1590
			if (StringUtils.isBlank(result)) {
1591
				return null;
1592
			} else {
1593
				return result;
1594
			}
1595
		} catch (Exception e) {
1596
			e.printStackTrace();
1597
			return null;
1598
		}
1599
	}
1600

    
1601
	/**
1602
	 * Returns the <code>DisplayName</code> attribute.
1603
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1604
	 * @return The <code>DisplayName</code> attribute.
1605
	 * @see MethodMapper
1606
	 */
1607
	 //used by Mapper
1608
	private static String getDisplayName(TaxonName taxonName) {
1609
		// TODO: extension?
1610
		if (taxonName == null) {
1611
			return null;
1612
		}else{
1613
		    taxonName = CdmBase.deproxy(taxonName);
1614
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1615
			HTMLTagRules tagRules = new HTMLTagRules().
1616
					addRule(TagEnum.name, "i").
1617
					addRule(TagEnum.nomStatus, "@status@");
1618

    
1619
			String result;
1620
			if (getSources(taxonName).get(PesiTransformer.SOURCE_ERMS)){
1621
			    result = cacheStrategy.getTitleCache(taxonName, tagRules);  //according to SQL script (also in ERMS sources are not abbreviated)
1622
			}else if (getSources(taxonName).get(PesiTransformer.SOURCE_EM)){
1623
			    result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
1624
			}else{
1625
			    //TODO define for FE + IF and for multiple sources
1626
			    result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
1627
			}
1628
			return result.replaceAll(",?\\<@status@\\>.*\\</@status@\\>", "");
1629
		}
1630
	}
1631

    
1632
	@SuppressWarnings("unused")
1633
	private static String getGUID(TaxonName taxonName) {
1634
		UUID uuid = taxonName.getUuid();
1635
		String result = "NameUUID:" + uuid.toString();
1636
		return result;
1637
	}
1638

    
1639

    
1640
	/**
1641
	 * Returns the <code>WebShowName</code> attribute for a taxon.
1642
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1643
	 * @return The <code>WebShowName</code> attribute.
1644
	 * @see MethodMapper
1645
	*/
1646
	@SuppressWarnings("unused")
1647
	private static String getWebShowName(TaxonBase<?> taxon) {
1648
		TaxonName taxonName = taxon.getName();
1649
		String result = getWebShowName(taxonName);
1650
		if (isMisappliedName(taxon)){
1651
			result = result + " " + getAuthorString(taxon);
1652
		}
1653
		return result;
1654
	}
1655

    
1656
	/**
1657
	 * Returns the <code>WebShowName</code> attribute.
1658
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1659
	 * @return The <code>WebShowName</code> attribute.
1660
	 * @see MethodMapper
1661
	 */
1662
	private static String getWebShowName(TaxonName taxonName) {
1663
		//TODO extensions?
1664
		if (taxonName == null) {
1665
			return null;
1666
		}else{
1667
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1668

    
1669
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1670
			String result = cacheStrategy.getTitleCache(taxonName, tagRules);
1671
			return result;
1672
		}
1673
	}
1674

    
1675

    
1676
	/**
1677
	 * Returns the <code>WebSearchName</code> attribute.
1678
	 * @param taxonName The {@link NonViralName NonViralName}.
1679
	 * @return The <code>WebSearchName</code> attribute.
1680
	 * @see MethodMapper
1681
	 */
1682
	@SuppressWarnings("unused")
1683
	private static String getWebSearchName(TaxonName taxonName) {
1684
		//TODO extensions?
1685
	    TaxonNameDefaultCacheStrategy strategy = getCacheStrategy(taxonName);
1686
		String result = strategy.getNameCache(taxonName);
1687
		return result;
1688
	}
1689

    
1690

    
1691
	/**
1692
	 * Returns the <code>FullName</code> attribute.
1693
	 * @param taxonName The {@link NonViralName NonViralName}.
1694
	 * @return The <code>FullName</code> attribute.
1695
	 * @see MethodMapper
1696
	 */
1697
	@SuppressWarnings("unused")
1698
	private static String getFullName(TaxonName taxonName) {
1699
		//TODO extensions?
1700
		String result = getCacheStrategy(taxonName).getTitleCache(taxonName);
1701
		Iterator<Taxon> taxa = taxonName.getTaxa().iterator();
1702
		if (taxonName.getTaxa().size() >0){
1703
			if (taxonName.getTaxa().size() == 1){
1704
				Taxon taxon = taxa.next();
1705
				if (isMisappliedName(taxon)){
1706
					result = result + " " + getAuthorString(taxon);
1707
				}
1708
				taxon = null;
1709
			}
1710
		}
1711
		return result;
1712
	}
1713

    
1714
	/**
1715
	 * Returns the SourceNameCache for the AdditionalSource table
1716
	 * @param taxonName
1717
	 * @return
1718
	 */
1719
	static boolean isFirstAbbrevTitle = true;
1720
	@SuppressWarnings("unused")
1721
	private static String getSourceNameCache(TaxonName taxonName) {
1722
		if (taxonName != null){
1723
			Reference nomRef = taxonName.getNomenclaturalReference();
1724
			if (nomRef != null ){
1725
			    if (isFirstAbbrevTitle){
1726
			        //#5388 is definetely not the correct ticket number
1727
			        logger.warn("Semantics of getAbbrevTitleCache has changed. Please check if output is still correct. See #5388");
1728
			        isFirstAbbrevTitle = false;
1729
			    }
1730
			    return nomRef.getAbbrevTitleCache();
1731
			}
1732
		}
1733
		return null;
1734
	}
1735

    
1736

    
1737

    
1738
	/**
1739
	 * Returns the <code>FullName</code> attribute.
1740
	 * @param taxon The {@link TaxonBase taxon}.
1741
	 * @return The <code>FullName</code> attribute.
1742
	 * @see MethodMapper
1743
	 */
1744
	/*@SuppressWarnings("unused")
1745
	private static String getFullName(TaxonBase taxon) {
1746
		//TODO extensions?
1747
		TaxonNameBase name = taxon.getName();
1748
		String result = getFullName(name);
1749
		if (isMisappliedName(taxon)){
1750
			result = result + " " + getAuthorString(taxon);
1751
		}
1752

    
1753
		return result;
1754
	}
1755
*/
1756

    
1757
	/**
1758
	 * Returns the nomenclatural reference which is the reference
1759
	 * including the detail (microreference).
1760
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1761
	 * @return The <code>AuthorString</code> attribute.
1762
	 * @see MethodMapper
1763
	 */
1764
	@SuppressWarnings("unused")
1765
	private static String getNomRefString(TaxonName taxonName) {
1766
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1767
		if (ref == null){
1768
			return null;
1769
		}
1770
		String result = null;
1771
		BitSet sources = getSources(taxonName);
1772
		int len = sources.length();
1773
		if(sources.get(PesiTransformer.SOURCE_EM)){
1774
		    if (! ref.isProtectedAbbrevTitleCache()){
1775
		        ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1776
		    }
1777
		    result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1778
		}else if(sources.get(PesiTransformer.SOURCE_FE)||sources.get(PesiTransformer.SOURCE_IF) ){
1779
            //TODO still need to check if correct for FE + IF
1780
		    if (! ref.isProtectedAbbrevTitleCache()){
1781
                ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1782
            }
1783
            result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1784
            return result;   // according to SQL script
1785
		}else if(sources.get(PesiTransformer.SOURCE_ERMS)) {
1786
            //result = null; //according to SQL script
1787
		}else{
1788
		    logger.warn("Source not yet supported");
1789
		}
1790
		return result;
1791
	}
1792

    
1793

    
1794
	/**
1795
	 * Returns the <code>NameStatusFk</code> attribute.
1796
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1797
	 * @return The <code>NameStatusFk</code> attribute.
1798
	 * @see MethodMapper
1799
	 */
1800
	@SuppressWarnings("unused")
1801
	private static Integer getNameStatusFk(TaxonName taxonName) {
1802
		Integer result = null;
1803

    
1804
		NomenclaturalStatus state = getNameStatus(taxonName);
1805
		if (state != null) {
1806
			result = PesiTransformer.nomStatus2nomStatusFk(state.getType());
1807
		}
1808
		return result;
1809
	}
1810

    
1811
	/**
1812
	 * Returns the <code>NameStatusCache</code> attribute.
1813
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1814
	 * @return The <code>NameStatusCache</code> attribute.
1815
	 * @throws UndefinedTransformerMethodException
1816
	 * @see MethodMapper
1817
	 */
1818
	@SuppressWarnings("unused")
1819
	private static String getNameStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1820
		String result = null;
1821
		NomenclaturalStatus status = getNameStatus(taxonName);
1822
		if (status != null) {
1823
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1824
		}
1825
		return result;
1826
	}
1827

    
1828

    
1829
	private static NomenclaturalStatus getNameStatus(TaxonName taxonName) {
1830
		try {
1831
			if (taxonName != null) {
1832
			    Set<NomenclaturalStatus> states = taxonName.getStatus();
1833
				if (states.size() == 1) {
1834
					NomenclaturalStatus status = states.iterator().next();
1835
					return status;
1836
				} else if (states.size() > 1) {
1837
					logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1838
				}
1839
			}
1840
		} catch (Exception e) {
1841
			e.printStackTrace();
1842
		}
1843
		return null;
1844
	}
1845
	/**
1846
	 * Returns the <code>TaxonStatusFk</code> attribute.
1847
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1848
	 * @param state The {@link PesiExportState PesiExportState}.
1849
	 * @return The <code>TaxonStatusFk</code> attribute.
1850
	 * @see MethodMapper
1851
	 */
1852
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1853
		Integer result = null;
1854

    
1855
		try {
1856
			if (isMisappliedName(taxon)) {
1857
				Synonym synonym = Synonym.NewInstance(null, null);
1858

    
1859
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1860
				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1861
			} else {
1862
				result = PesiTransformer.taxonBase2statusFk(taxon);
1863
			}
1864
		} catch (Exception e) {
1865
			e.printStackTrace();
1866
		}
1867
		return result;
1868
	}
1869

    
1870
	/**
1871
	 * Returns the <code>TaxonStatusCache</code> attribute.
1872
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1873
	 * @param state The {@link PesiExportState PesiExportState}.
1874
	 * @return The <code>TaxonStatusCache</code> attribute.
1875
	 * @throws UndefinedTransformerMethodException
1876
	 * @see MethodMapper
1877
	 */
1878
	@SuppressWarnings("unused")
1879
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1880
		return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1881
	}
1882

    
1883
	/**
1884
	 * Returns the <code>TypeNameFk</code> attribute.
1885
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1886
	 * @param state The {@link PesiExportState PesiExportState}.
1887
	 * @return The <code>TypeNameFk</code> attribute.
1888
	 * @see MethodMapper
1889
	 */
1890
	private static Integer getTypeNameFk(TaxonName taxonName, PesiExportState state) {
1891
		Integer result = null;
1892
		if (taxonName != null) {
1893
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1894
			if (nameTypeDesignations.size() == 1) {
1895
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1896
				if (nameTypeDesignation != null) {
1897
					TaxonName typeName = nameTypeDesignation.getTypeName();
1898
					if (typeName != null) {
1899
					    result = state.getDbId(typeName);
1900
					}
1901
				}
1902
			} else if (nameTypeDesignations.size() > 1) {
1903
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1904
			}
1905
		}
1906
		return result;
1907
	}
1908

    
1909
	/**
1910
	 * Returns the <code>TypeFullnameCache</code> attribute.
1911
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1912
	 * @return The <code>TypeFullnameCache</code> attribute.
1913
	 * @see MethodMapper
1914
	 */
1915
	@SuppressWarnings("unused")
1916
	private static String getTypeFullnameCache(TaxonName taxonName) {
1917
		String result = null;
1918

    
1919
		try {
1920
    		if (taxonName != null) {
1921
    			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1922
    			if (nameTypeDesignations.size() == 1) {
1923
    				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1924
    				if (nameTypeDesignation != null) {
1925
    					TaxonName typeName = nameTypeDesignation.getTypeName();
1926
    					if (typeName != null) {
1927
    						result = typeName.getTitleCache();
1928
    					}
1929
    				}
1930
    			} else if (nameTypeDesignations.size() > 1) {
1931
    				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1932
    			}
1933
    		}
1934
		} catch (Exception e) {
1935
			e.printStackTrace();
1936
		}
1937
		return result;
1938
	}
1939

    
1940

    
1941
	/**
1942
	 * Returns the <code>QualityStatusFk</code> attribute.
1943
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1944
	 * @return The <code>QualityStatusFk</code> attribute.
1945
	 * @see MethodMapper
1946
	 */
1947
	private static Integer getQualityStatusFk(TaxonName taxonName) {
1948
		BitSet sources = getSources(taxonName);
1949
		return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1950
	}
1951

    
1952

    
1953
	/**
1954
	 * Returns the <code>QualityStatusCache</code> attribute.
1955
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1956
	 * @return The <code>QualityStatusCache</code> attribute.
1957
	 * @throws UndefinedTransformerMethodException
1958
	 * @see MethodMapper
1959
	 */
1960
	@SuppressWarnings("unused")
1961
	private static String getQualityStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1962
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1963
	}
1964

    
1965

    
1966
	/**
1967
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1968
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1969
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1970
	 * @see MethodMapper
1971
	 */
1972
	@SuppressWarnings("unused")
1973
	private static Integer getTypeDesignationStatusFk(TaxonName taxonName) {
1974
		Integer result = null;
1975

    
1976
		try {
1977
		if (taxonName != null) {
1978
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1979
			if (typeDesignations.size() == 1) {
1980
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1981
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1982
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1983
			} else if (typeDesignations.size() > 1) {
1984
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1985
			}
1986
		}
1987

    
1988
		} catch (Exception e) {
1989
			e.printStackTrace();
1990
		}
1991
		return result;
1992
	}
1993

    
1994
	/**
1995
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1996
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1997
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1998
	 * @see MethodMapper
1999
	 */
2000
	@SuppressWarnings("unused")
2001
	private static String getTypeDesignationStatusCache(TaxonName taxonName) {
2002
		String result = null;
2003

    
2004
		try {
2005
		if (taxonName != null) {
2006
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
2007
			if (typeDesignations.size() == 1) {
2008
				Object obj = typeDesignations.iterator().next().getTypeStatus();
2009
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
2010
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
2011
			} else if (typeDesignations.size() > 1) {
2012
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2013
			}
2014
		}
2015

    
2016
		} catch (Exception e) {
2017
			e.printStackTrace();
2018
		}
2019
		return result;
2020
	}
2021

    
2022
	/**
2023
	 * Returns the <code>FossilStatusFk</code> attribute.
2024
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2025
	 * @return The <code>FossilStatusFk</code> attribute.
2026
	 * @see MethodMapper
2027
	 */
2028
	@SuppressWarnings("unused")
2029
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
2030
		Integer result = null;
2031

    
2032
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
2033
		if (fossilStatuus.size() == 0){
2034
			return null;
2035
		}else if (fossilStatuus.size() > 1){
2036
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
2037
		}
2038
		String fossilStatus = fossilStatuus.iterator().next();
2039

    
2040
		int statusFk = state.getTransformer().fossilStatusCache2FossilStatusFk(fossilStatus);
2041
		return statusFk;
2042
	}
2043

    
2044
	/**
2045
	 * Returns the <code>FossilStatusCache</code> attribute.
2046
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2047
	 * @return The <code>FossilStatusCache</code> attribute.
2048
	 * @see MethodMapper
2049
	 */
2050
	@SuppressWarnings("unused")
2051
	private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
2052
		String result = null;
2053
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
2054
		if (fossilStatuus.size() == 0){
2055
			return null;
2056
		}
2057
		for (String strFossilStatus : fossilStatuus){
2058
			result = CdmUtils.concat(";", result, strFossilStatus);
2059
		}
2060
		return result;
2061
	}
2062

    
2063
	/**
2064
	 * Returns the <code>IdInSource</code> attribute.
2065
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2066
	 * @return The <code>IdInSource</code> attribute.
2067
	 * @see MethodMapper
2068
	 */
2069
	@SuppressWarnings("unused")
2070
	private static String getIdInSource(IdentifiableEntity taxonName) {
2071
		String result = null;
2072

    
2073
		try {
2074
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
2075
			if (sources.size() > 1){
2076
				logger.warn("There is > 1 Pesi source. This is not yet handled.");
2077
			}
2078
			if (sources.size() == 0){
2079
				logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2080
			}
2081
			for (IdentifiableSource source : sources) {
2082
				Reference ref = source.getCitation();
2083
				UUID refUuid = ref.getUuid();
2084
				String idInSource = source.getIdInSource();
2085
				if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
2086
					result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
2087
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2088
					result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
2089
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2090
					result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
2091
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
2092
					result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
2093
				}else{
2094
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2095
				}
2096

    
2097
				String sourceIdNameSpace = source.getIdNamespace();
2098
				if (sourceIdNameSpace != null) {
2099
					if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
2100
						result =  idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
2101
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
2102
						result =  idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
2103
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
2104
						result =  idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
2105
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
2106
						result =  idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
2107
					}
2108
				}
2109
				if (result == null) {
2110
					logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
2111
				}
2112
			}
2113
		} catch (Exception e) {
2114
			e.printStackTrace();
2115
			logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
2116
		}
2117

    
2118
		if (result == null) {
2119
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2120
		}
2121
		return result;
2122
	}
2123

    
2124
	/**
2125
	 * Returns the idInSource for a given TaxonName only.
2126
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2127
	 * @return The idInSource.
2128
	 */
2129
	private static String getIdInSourceOnly(IdentifiableEntity identEntity) {
2130
		String result = null;
2131

    
2132
		// Get the sources first
2133
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2134

    
2135
		// Determine the idInSource
2136
		if (sources.size() == 1) {
2137
			IdentifiableSource source = sources.iterator().next();
2138
			if (source != null) {
2139
				result = source.getIdInSource();
2140
			}
2141
		} else if (sources.size() > 1) {
2142
			int count = 1;
2143
			result = "";
2144
			for (IdentifiableSource source : sources) {
2145
				result += source.getIdInSource();
2146
				if (count < sources.size()) {
2147
					result += "; ";
2148
				}
2149
				count++;
2150
			}
2151

    
2152
		}
2153

    
2154
		return result;
2155
	}
2156

    
2157
	/**
2158
	 * Returns the Sources for a given TaxonName only.
2159
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2160
	 * @return The Sources.
2161
	 */
2162
	private static Set<IdentifiableSource> getPesiSources(IdentifiableEntity<?> identifiableEntity) {
2163
		Set<IdentifiableSource> sources = new HashSet<>();
2164

    
2165
		//Taxon Names
2166
		if (identifiableEntity.isInstanceOf(TaxonName.class)){
2167
			// Sources from TaxonName
2168
		    TaxonName taxonName = CdmBase.deproxy(identifiableEntity, TaxonName.class);
2169
			Set<IdentifiableSource> testSources = identifiableEntity.getSources();
2170
			sources = filterPesiSources(identifiableEntity.getSources());
2171

    
2172
			if (sources.size() == 0 && testSources.size()>0){
2173
				IdentifiableSource source = testSources.iterator().next();
2174
				logger.warn("There are sources, but they are no pesi sources!!!" + source.getIdInSource() + " - " + source.getIdNamespace() + " - " + source.getCitation().getTitleCache());
2175
			}
2176
			if (sources.size() > 1) {
2177
				logger.warn("This TaxonName has more than one Source: " + identifiableEntity.getUuid() + " (" + identifiableEntity.getTitleCache() + ")");
2178
			}
2179

    
2180
			// name has no PESI source, take sources from TaxonBase
2181
			if (sources == null || sources.isEmpty()) {
2182
				Set<TaxonBase> taxa = taxonName.getTaxonBases();
2183
				for (TaxonBase taxonBase: taxa){
2184
					sources.addAll(filterPesiSources(taxonBase.getSources()));
2185
				}
2186
			}
2187

    
2188
		//for TaxonBases
2189
		}else if (identifiableEntity.isInstanceOf(TaxonBase.class)){
2190
			sources = filterPesiSources(identifiableEntity.getSources());
2191
		}
2192

    
2193
		/*TODO: deleted only for testing the inferred synonyms
2194
		if (sources == null || sources.isEmpty()) {
2195
			logger.warn("This TaxonName has no PESI Sources: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2196
		}else if (sources.size() > 1){
2197
			logger.warn("This Taxon(Name) has more than 1 PESI source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2198
		}
2199
		*/
2200
		return sources;
2201
	}
2202

    
2203
	// return all sources with a PESI reference
2204
	private static Set<IdentifiableSource> filterPesiSources(Set<? extends IdentifiableSource> sources) {
2205
		Set<IdentifiableSource> result = new HashSet<>();
2206
		for (IdentifiableSource source : sources){
2207
			Reference ref = source.getCitation();
2208
			UUID refUuid = ref.getUuid();
2209
			if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed) ||
2210
				refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)||
2211
				refUuid.equals(PesiTransformer.uuidSourceRefErms)||
2212
				refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum) ||
2213
				refUuid.equals(PesiTransformer.uuidSourceRefAuct)){
2214
				result.add(source);
2215
			}
2216
		}
2217
		return result;
2218
	}
2219

    
2220
	/**
2221
	 * Returns the <code>GUID</code> attribute.
2222
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2223
	 * @return The <code>GUID</code> attribute.
2224
	 * @see MethodMapper
2225
	 */
2226
	private static String getGUID(TaxonBase<?> taxon) {
2227
		if (taxon.getLsid() != null ){
2228
			return taxon.getLsid().getLsid();
2229
		}else if (taxon.hasMarker(PesiTransformer.uuidMarkerGuidIsMissing, true)){
2230
			return null;
2231
		}else{
2232
			return taxon.getUuid().toString();
2233
		}
2234
	}
2235

    
2236

    
2237
	/**
2238
	 * Returns the <code>DerivedFromGuid</code> attribute.
2239
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2240
	 * @return The <code>DerivedFromGuid</code> attribute.
2241
	 * @see MethodMapper
2242
	 */
2243
	@SuppressWarnings("unused")
2244
	private static String getDerivedFromGuid(TaxonBase<?> taxon) {
2245
		String result = null;
2246
		try {
2247
		// The same as GUID for now
2248
		result = getGUID(taxon);
2249
		} catch (Exception e) {
2250
			e.printStackTrace();
2251
		}
2252
		return result;
2253
	}
2254

    
2255
	/**
2256
	 * Returns the <code>CacheCitation</code> attribute.
2257
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2258
	 * @return The CacheCitation.
2259
	 * @see MethodMapper
2260
	 */
2261
	@SuppressWarnings("unused")
2262
	private static String getCacheCitation(TaxonBase taxon) {
2263
		// !!! See also doPhaseUpdates
2264

    
2265
		TaxonName taxonName = taxon.getName();
2266
		String result = "";
2267
		//TODO implement anew for taxa
2268
		try {
2269
			BitSet sources = getSources(taxonName);
2270
			if (sources.isEmpty()) {
2271
//				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2272
			} else if (sources.get(PesiTransformer.SOURCE_ERMS)) {
2273
				// 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...
2274
				// 		 So the following code is some kind of harmless assumption.
2275
				Set<Extension> extensions = taxonName.getExtensions();
2276
				for (Extension extension : extensions) {
2277
					if (extension.getType().equals(cacheCitationExtensionType)) {
2278
						result = extension.getValue();
2279
					}
2280
				}
2281
			} else {
2282
				String expertName = getExpertName(taxon);
2283
				String webShowName = getWebShowName(taxonName);
2284

    
2285
				// idInSource only
2286
				String idInSource = getIdInSourceOnly(taxonName);
2287

    
2288
				// build the cacheCitation
2289
				if (expertName != null) {
2290
					result += expertName + ". ";
2291
				} else {
2292
					if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
2293
				}
2294
				if (webShowName != null) {
2295
					result += webShowName + ". ";
2296
				} else {
2297
					logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2298
				}
2299

    
2300
				if (getOriginalDB(taxonName).equals("FaEu")) {
2301
					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
2302
				} else if (getOriginalDB(taxonName).equals("EM")) {
2303
					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
2304
				}
2305

    
2306
				if (idInSource != null) {
2307
					result += idInSource;
2308
				} else {
2309
					logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2310
				}
2311
			}
2312
		} catch (Exception e) {
2313
			e.printStackTrace();
2314
		}
2315

    
2316
		if (StringUtils.isBlank(result)) {
2317
			return null;
2318
		} else {
2319
			return result;
2320
		}
2321
	}
2322

    
2323
	/**
2324
	 * Returns the <code>OriginalDB</code> attribute.
2325
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2326
	 * @return The <code>OriginalDB</code> attribute.
2327
	 * @see MethodMapper
2328
	 */
2329
	private static String getOriginalDB(IdentifiableEntity identEntity) {
2330
		// Sources from TaxonName
2331
		BitSet sources  = getSources(identEntity);
2332
		return PesiTransformer.getOriginalDbBySources(sources);
2333
	}
2334

    
2335
	/**
2336
	 * Returns the <code>LastAction</code> attribute.
2337
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2338
	 * @return The <code>LastAction</code> attribute.
2339
	 * @see MethodMapper
2340
	 */
2341
	@SuppressWarnings("unused")
2342
	private static String getLastAction(IdentifiableEntity<?> identEntity) {
2343
		String result = null;
2344
		try {
2345
		Set<Extension> extensions = identEntity.getExtensions();
2346
		for (Extension extension : extensions) {
2347
			if (extension.getType().equals(lastActionExtensionType)) {
2348
				result = extension.getValue();
2349
			}
2350
		}
2351
		} catch (Exception e) {
2352
			e.printStackTrace();
2353
		}
2354
		return result;
2355
	}
2356

    
2357
	/**
2358
	 * Returns the <code>LastActionDate</code> attribute.
2359
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2360
	 * @return The <code>LastActionDate</code> attribute.
2361
	 * @see MethodMapper
2362
	 */
2363
	@SuppressWarnings({ "unused" })
2364
	private static DateTime getLastActionDate(IdentifiableEntity identEntity) {
2365
		DateTime result = null;
2366
		try {
2367
			Set<Extension> extensions = identEntity.getExtensions();
2368
			for (Extension extension : extensions) {
2369
				if (extension.getType().equals(lastActionDateExtensionType)) {
2370
					String dateTime = extension.getValue();
2371
					if (dateTime != null) {
2372
						DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
2373
						result = formatter.parseDateTime(dateTime);
2374
					}
2375
				}
2376
			}
2377
		} catch (Exception e) {
2378
			e.printStackTrace();
2379
		}
2380
		return result;
2381
	}
2382

    
2383
	/**
2384
	 * Returns the <code>ExpertName</code> attribute.
2385
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2386
	 * @return The <code>ExpertName</code> attribute.
2387
	 * @see MethodMapper
2388
	 */
2389
	@SuppressWarnings("unused")
2390
	private static String getExpertName(TaxonBase<?> taxonName) {
2391
		String result = null;
2392
		try {
2393
		Set<Extension> extensions = taxonName.getExtensions();
2394
		for (Extension extension : extensions) {
2395
			if (extension.getType().equals(expertNameExtensionType)) {
2396
				result = extension.getValue();
2397
			}
2398
		}
2399
		} catch (Exception e) {
2400
			e.printStackTrace();
2401
		}
2402
		return result;
2403
	}
2404

    
2405
	/**
2406
	 * Returns the <code>ExpertFk</code> attribute.
2407
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2408
	 * @param state The {@link PesiExportState PesiExportState}.
2409
	 * @return The <code>ExpertFk</code> attribute.
2410
	 * @see MethodMapper
2411
	 */
2412
	private static Integer getExpertFk(Reference reference, PesiExportState state) {
2413
		Integer result = state.getDbId(reference);
2414
		return result;
2415
	}
2416

    
2417
	/**
2418
	 * Returns the <code>SpeciesExpertName</code> attribute.
2419
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2420
	 * @return The <code>SpeciesExpertName</code> attribute.
2421
	 * @see MethodMapper
2422
	 */
2423
	@SuppressWarnings("unused")
2424
	private static String getSpeciesExpertName(TaxonBase<?> taxonName) {
2425
		String result = null;
2426
		try {
2427
		Set<Extension> extensions = taxonName.getExtensions();
2428
		for (Extension extension : extensions) {
2429
			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2430
				result = extension.getValue();
2431
			}
2432
		}
2433
		} catch (Exception e) {
2434
			e.printStackTrace();
2435
		}
2436
		return result;
2437
	}
2438

    
2439
	/**
2440
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2441
	 * @param reference The {@link Reference Reference}.
2442
	 * @param state The {@link PesiExportState PesiExportState}.
2443
	 * @return The <code>SpeciesExpertFk</code> attribute.
2444
	 * @see MethodMapper
2445
	 */
2446
	private static Integer getSpeciesExpertFk(Reference reference, PesiExportState state) {
2447
		Integer result = state.getDbId(reference);
2448
		return result;
2449
	}
2450

    
2451

    
2452
	/**
2453
	 * Returns the source (E+M, Fauna Europaea, Index Fungorum, ERMS) of a given
2454
	 * Identifiable Entity as a BitSet
2455
	 * @param identEntity
2456
	 * @return
2457
	 */
2458
	private static BitSet getSources(IdentifiableEntity<?> identEntity){
2459
		BitSet bitSet = new BitSet();
2460
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2461
		for (IdentifiableSource source : sources) {
2462
			Reference ref = source.getCitation();
2463
			UUID refUuid = ref.getUuid();
2464
			if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
2465
				bitSet.set(PesiTransformer.SOURCE_EM);
2466
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2467
				bitSet.set(PesiTransformer.SOURCE_FE);
2468
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2469
				bitSet.set(PesiTransformer.SOURCE_ERMS);
2470
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){
2471
				bitSet.set(PesiTransformer.SOURCE_IF);
2472
			}else{
2473
				if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2474
			}
2475
		}
2476
		return bitSet;
2477
	}
2478

    
2479
	protected static TaxonNameDefaultCacheStrategy getCacheStrategy(TaxonName taxonName) {
2480
		taxonName = CdmBase.deproxy(taxonName);
2481
		TaxonNameDefaultCacheStrategy cacheStrategy;
2482
		if (taxonName.isZoological()){
2483
			cacheStrategy = zooNameStrategy;
2484
		}else if (taxonName.isBotanical()) {
2485
			cacheStrategy = nonViralNameStrategy;
2486
		}else if (taxonName.isNonViral()) {
2487
			cacheStrategy = nonViralNameStrategy;
2488
		}else if (taxonName.isBacterial()) {
2489
			cacheStrategy = nonViralNameStrategy;
2490
		}else{
2491
			logger.error("Unhandled taxon name type. Can't define strategy class");
2492
			cacheStrategy = nonViralNameStrategy;
2493
		}
2494
		return cacheStrategy;
2495
	}
2496

    
2497
	/**
2498
	 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2499
	 * @param relationship The {@link RelationshipBase Relationship}.
2500
	 * @return The <code>RelTaxonQualifierFk</code> attribute.
2501
	 * @see MethodMapper
2502
	 */
2503
	@SuppressWarnings("unused")
2504
	private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2505
		return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2506
	}
2507
	/**
2508
	 * Returns the <code>Notes</code> attribute.
2509
	 * @param relationship The {@link RelationshipBase Relationship}.
2510
	 * @return The <code>Notes</code> attribute.
2511
	 * @see MethodMapper
2512
	 */
2513
	@SuppressWarnings("unused")
2514
	private static String getNotes(RelationshipBase<?, ?, ?> relationship) {
2515
		// TODO
2516
		return null;
2517
	}
2518

    
2519

    
2520
	/**
2521
	 * Returns the CDM to PESI specific export mappings.
2522
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2523
	 */
2524
	private PesiExportMapping getMapping() {
2525
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2526

    
2527
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2528
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2529
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2530
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2531

    
2532
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2533

    
2534
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2535
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2536
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misapllied Names are handled differently
2537
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2538

    
2539
		// DisplayName
2540
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2541

    
2542
		// FossilStatus (Fk, Cache)
2543
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2544
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2545

    
2546
		//handled by name mapping
2547
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2548
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2549

    
2550
		//experts
2551
		ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtSpeciesExpertName);
2552
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2553
		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
2554
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2555

    
2556
//		mapping.addMapper(MethodMapper.NewInstance("ParentTaxonFk", this, TaxonBase.class, PesiExportState.class));  //by AM, doesn't work, FK exception
2557
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2558

    
2559
		addNameMappers(mapping);
2560

    
2561
		return mapping;
2562
	}
2563

    
2564
	/**
2565
	 * Returns the CDM to PESI specific export mappings.
2566
	 * @param state
2567
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2568
	 * @throws UndefinedTransformerMethodException
2569
	 */
2570
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2571
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2572

    
2573
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2574

    
2575
		//		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2576

    
2577
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonName.class));
2578
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonName.class));
2579
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonName.class, PesiExportState.class));
2580
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
2581
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2582
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
2583
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonName.class));
2584
		mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonName.class));
2585

    
2586
		// DisplayName
2587
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonName.class));
2588

    
2589
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2590
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2591

    
2592
		addNameMappers(mapping);
2593
		//TODO add author mapper, TypeNameFk
2594

    
2595
		return mapping;
2596
	}
2597

    
2598

    
2599
	private void addNameMappers(PesiExportMapping mapping) {
2600
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2601
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2602
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2603
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2604

    
2605
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2606
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonName.class));
2607

    
2608
//		mapping.addMapper(DbStringMapper.NewInstance("TitleCache", "FullName"));    //does not work as we need other cache strategy
2609
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonName.class));
2610

    
2611

    
2612
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonName.class));
2613

    
2614
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonName.class));
2615
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonName.class, PesiExportState.class));
2616
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
2617
		//TODO TypeNameFk
2618

    
2619
		//quality status
2620
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonName.class));
2621
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonName.class, PesiExportState.class));
2622

    
2623
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2624
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2625

    
2626
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2627

    
2628
	}
2629

    
2630
    /**
2631
     * Returns the <code>TaxonFk1</code> attribute. It corresponds to a CDM <code>TaxonRelationship</code>.
2632
     * @param relationship The {@link RelationshipBase Relationship}.
2633
     * @param state The {@link PesiExportState PesiExportState}.
2634
     * @return The <code>TaxonFk1</code> attribute.
2635
     * @see MethodMapper
2636
     */
2637
    @SuppressWarnings("unused")
2638
    private static Integer getSynonym(Synonym synonym, PesiExportState state) {
2639
         return state.getDbId(synonym);
2640
    }
2641

    
2642

    
2643
	private PesiExportMapping getSynRelMapping() {
2644
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2645
		logger.warn("SynRelMapping currently not implemented. Needs to be checked");
2646

    
2647
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getSynonym", Synonym.class, PesiExportState.class));
2648
		mapping.addMapper(DbObjectMapper.NewInstance("acceptedTaxon", "TaxonFk2"));
2649
		mapping.addMapper(DbObjectMapper.NewInstance("type", "RelTaxonQualifierFk"));
2650
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this.getClass(), "getSynonymTypeCache", Synonym.class, PesiExportState.class));
2651
		// TODO
2652
//		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2653

    
2654
		return mapping;
2655
	}
2656

    
2657
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state) {
2658
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2659

    
2660
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2661
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2662

    
2663
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2664
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2665
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonName.class));
2666

    
2667
		//we have only nomenclatural references here
2668
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2669
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2670

    
2671
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2672

    
2673
		return mapping;
2674
	}
2675

    
2676

    
2677
    @Override
2678
    protected boolean doCheck(PesiExportState state) {
2679
        return true;
2680
    }
2681

    
2682
    @Override
2683
    protected boolean isIgnore(PesiExportState state) {
2684
        return ! state.getConfig().isDoTaxa();
2685
    }
2686
}
(12-12/13)