Project

General

Profile

Download (87.2 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.EnumSet;
18
import java.util.HashMap;
19
import java.util.Iterator;
20
import java.util.List;
21
import java.util.Map;
22
import java.util.Set;
23
import java.util.UUID;
24
import java.util.regex.Matcher;
25
import java.util.regex.Pattern;
26

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
153
			initPreparedStatements(state);
154

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

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

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

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

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

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

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

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

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

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

    
193
			//"PHASE 5: Creating Inferred Synonyms...
194
			success &= doPhase05(state, mapping, synonymRelMapping);
195

    
196
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
197

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

    
209

    
210
	private void initPreparedStatements(PesiExportState state) throws SQLException {
211
		initTreeIndexStatement(state);
212
		initRankExpertsUpdateStmt(state);
213
		initRankUpdateStatement(state);
214

    
215
		initParentFkStatement(state);
216
	}
217

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

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

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

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

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

    
249
	private boolean doPhase01(PesiExportState state, PesiExportMapping mapping, PesiExportMapping additionalSourceMapping){
250
		int count = 0;
251
		int pastCount = 0;
252
		boolean success = true;
253
		// Get the limit for objects to save within a single transaction.
254
		int limit = state.getConfig().getLimitSave();
255

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

    
265
		int partitionCount = 0;
266
		List<TaxonBase<?>> list;
267
		while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)) != null   ) {
268

    
269
			logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
270

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

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

    
309
				//TODO switch on again, leads to some warnings in ERMS for taxa of not correctly handled kingdoms
310
				validatePhaseOne(taxon, nvn);
311
			}
312

    
313
			// Commit transaction
314
			commitTransaction(txStatus);
315
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 01)");
316
			pastCount = count;
317

    
318
			// Start new transaction
319
			txStatus = startTransaction(true);
320
			if (logger.isDebugEnabled()) {
321
                logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
322
            }
323

    
324
		}
325
		logger.debug("No " + pluralString + " left to fetch.");
326

    
327
		// Commit transaction
328
		commitTransaction(txStatus);
329
		txStatus = null;
330

    
331
		return success;
332
	}
333

    
334
	private void validatePhaseOne(TaxonBase<?> taxon, TaxonName taxonName) {
335

    
336
	    // Check whether some rules are violated
337
		String genusOrUninomial = taxonName.getGenusOrUninomial();
338
		String specificEpithet = taxonName.getSpecificEpithet();
339
		String infraSpecificEpithet = taxonName.getInfraSpecificEpithet();
340
		String infraGenericEpithet = taxonName.getInfraGenericEpithet();
341
		Rank rank =  taxonName.getRank();
342

    
343
		//as kingdomFk can not be defined in Phase 01 the below code was switched to use the CDM rank. This may be changed if we move validation to Phase03 or later
344
//		Integer rankFk = getRankFk(taxonName, taxonName.getNameType());
345
//		if (rankFk == null) {
346
//			logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
347
//		} else {
348

    
349
			// Check whether infraGenericEpithet is set correctly
350
			// 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
351
			// 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
352

    
353
			int ancestorLevel = 0;
354
			if (taxonName.getRank().equals(Rank.SUBSPECIES())) {
355
				// The accepted taxon two rank levels above should be of rank subgenus
356
				ancestorLevel  = 2;
357
			}
358
			if (taxonName.getRank().equals(Rank.SPECIES())) {
359
				// The accepted taxon one rank level above should be of rank subgenus
360
				ancestorLevel = 1;
361
			}
362
			if (ancestorLevel > 0) {
363
				if (validateAncestorOfSpecificRank(taxon, ancestorLevel, Rank.SUBGENUS())) {
364
					// The child (species or subspecies) of this parent (subgenus) has to have an infraGenericEpithet
365
					if (infraGenericEpithet == null) {
366
						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() + ")");
367
						// maybe the taxon could be named here
368
					}
369
				}
370
			}
371

    
372
			if (infraGenericEpithet == null && rank.isInfraGenericButNotSpeciesGroup()) {
373
				logger.warn("InfraGenericEpithet was not determined although it should exist for infra generic names: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
374
			}
375
			if (specificEpithet != null && (rank.isInfraGenericButNotSpeciesGroup()||rank.isGenus()||rank.isSupraGeneric())) {
376
				logger.warn("SpecificEpithet was determined for rank " + rank.getTitleCache() + " although it should only exist for species aggregates, species or infraspecific taxa: TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
377
			}
378
			if (infraSpecificEpithet != null && !rank.isInfraSpecific()) {
379
				String message = "InfraSpecificEpithet '" +infraSpecificEpithet + "' was determined for rank " + rank.getTitleCache() + " although it should only exist for rank species and higher: "  + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")";
380
				if (StringUtils.isNotBlank(infraSpecificEpithet)){
381
					logger.warn(message);
382
				}else{
383
					logger.warn(message);
384
				}
385
			}
386
//		}
387
		if (infraSpecificEpithet != null && specificEpithet == null) {
388
			logger.warn("An infraSpecificEpithet was determined, but a specificEpithet was not determined: "  + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
389
		}
390
		if (genusOrUninomial == null) {
391
			logger.warn("GenusOrUninomial was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
392
		}
393
	}
394

    
395
	/**
396
	 * 2nd Round: Add ParentTaxonFk to each taxon and add Biota if not exists
397
	 */
398
	private boolean doPhase02(PesiExportState state) {
399
		int count = 0;
400
		int pastCount = 0;
401
		boolean success = true;
402
		if (! state.getConfig().isDoParentAndBiota()){
403
			logger.info ("Ignore PHASE 2: Make ParentFk and Biota...");
404
			return success;
405
		}
406

    
407
		// Get the limit for objects to save within a single transaction.
408
		int limit = state.getConfig().getLimitSave();
409

    
410
		insertBiota(state);
411

    
412
		logger.info("PHASE 2: Make ParentFk and Biota ... limit is " + limit);
413
		// Start transaction
414
		TransactionStatus txStatus = startTransaction(true);
415
		int partitionCount = 0;
416

    
417
//		ProfilerController.memorySnapshot();
418
		List<Taxon> list;
419
		while ((list = getNextTaxonPartition(Taxon.class, limit, partitionCount++, null)) != null   ) {
420

    
421
			if(logger.isDebugEnabled()) {
422
                logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
423
            }
424
			for (Taxon taxon : list) {
425
				for (TaxonNode node : taxon.getTaxonNodes()){
426
					doCount(count++, modCount, pluralString);
427
					TaxonNode parentNode = node.getParent();
428
					if (parentNode != null && parentNode.getTaxon() != null){  //new root node handling requires has root taxon with taxon == null
429
						int childId = state.getDbId( taxon);
430
						int parentId = state.getDbId(parentNode.getTaxon());
431
						success &= invokeParentTaxonFk(parentId, childId);
432
					}
433
				}
434
			}
435

    
436
			// Commit transaction
437
			commitTransaction(txStatus);
438
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 2)");
439
			pastCount = count;
440
			// Start transaction
441
			txStatus = startTransaction(true);
442
			if (logger.isDebugEnabled()){
443
			    logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
444
			}
445
		}
446
		logger.debug("No " + pluralString + " left to fetch.");
447

    
448
		// Commit transaction
449
		commitTransaction(txStatus);
450

    
451
		return success;
452
	}
453

    
454
	/**
455
	 * Inserts the Biota Taxon if not yet exists.
456
	 */
457
	private void insertBiota(PesiExportState state) {
458
		try {
459
			ResultSet rs = state.getConfig().getDestination().getResultSet("SELECT * FROM Taxon WHERE GenusOrUninomial = 'Biota' ");
460
			if (rs.next() == false){
461
				int biotaId = state.getConfig().getNameIdStart() -1 ;
462
				String sqlInsertBiota = "INSERT INTO Taxon (TaxonId, KingdomFk, RankFk, RankCache, GenusOrUninomial, WebSearchName, WebShowName, FullName, DisplayName, TaxonStatusFk, TaxonStatusCache) " +
463
									       " VALUES (" + biotaId + ",    0,    0,   'Superdomain',   'Biota',          'Biota',  '<i>Biota</i>',   'Biota', '<i>Biota</i>',  1 ,      'accepted')";
464
				state.getConfig().getDestination().update(sqlInsertBiota);
465
			}
466
			rs = null;
467
		} catch (SQLException e) {
468
			logger.warn ("Biota could not be requested or inserted");
469
		}
470
	}
471

    
472
	//PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...
473
	private boolean doPhase03(PesiExportState state) {
474
		int count = 0;
475
		int pastCount = 0;
476
		boolean success = true;
477
		if (! state.getConfig().isDoTreeIndex()){
478
			logger.info ("Ignore PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
479
			return success;
480
		}
481
		// Get the limit for objects to save within a single transaction.
482
		int limit = state.getConfig().getLimitSave();
483

    
484
		logger.info("PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
485
		// Be sure to add rank information, KingdomFk, TypeNameFk, expertFk and speciesExpertFk to every taxonName
486

    
487
		// Start transaction
488
		TransactionStatus txStatus = startTransaction(true);
489
		if (logger.isDebugEnabled()) {
490
            logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
491
        }
492
		int partitionCount = 0;
493
		List<TaxonBase> list;
494
		while ((list = getNextTaxonPartition(TaxonBase.class, limit, partitionCount++, null)) != null) {
495

    
496
			if (logger.isDebugEnabled()) {
497
                logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
498
            }
499
			for (TaxonBase<?> taxon : list) {
500
				TaxonName taxonName = CdmBase.deproxy(taxon.getName());
501
				// Determine expertFk
502
//				Integer expertFk = makeExpertFk(state, taxonName);
503
//
504
//				// Determine speciesExpertFk
505
//				Integer speciesExpertFk = makeSpeciesExpertFk(state, taxonName);
506

    
507
				doCount(count++, modCount, pluralString);
508
				Integer typeNameFk = getTypeNameFk(taxonName, state);
509
				Integer kingdomFk = findKingdomIdFromTreeIndex(taxon, state);
510

    
511
			    invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, state.getDbId(taxon),
512
						typeNameFk, kingdomFk, state);
513
			}
514

    
515
			// Commit transaction
516
			commitTransaction(txStatus);
517
			if (logger.isDebugEnabled()){logger.debug("Committed transaction.");}
518
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 3)");
519
			pastCount = count;
520

    
521
			// Start transaction
522
			txStatus = startTransaction(true);
523
			if (logger.isDebugEnabled()) {
524
                logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
525
            }
526
		}
527
		logger.debug("No " + pluralString + " left to fetch.");
528

    
529
		// Commit transaction
530
		commitTransaction(txStatus);
531

    
532
		if (logger.isDebugEnabled()){
533
		    logger.debug("Committed transaction.");
534
		    logger.debug("Try to take snapshot at the end of phase 3 of taxonExport, number of partitions: " + partitionCount);
535
		    //ProfilerController.memorySnapshot();
536
		}
537
		return success;
538
	}
539

    
540
    // 4th round: Add TreeIndex to each taxon
541
    private boolean doPhase04(PesiExportState state) {
542
        boolean success = true;
543

    
544
        logger.info("PHASE 4: Make TreeIndex ... ");
545

    
546
        //TODO test if possible to move to phase 02
547
        String sql = " UPDATE Taxon SET ParentTaxonFk = (SELECT TaxonId FROM Taxon WHERE RankFk = 0) " +
548
                " WHERE (RankFk = 10) and TaxonStatusFk = 1 ";
549
        state.getConfig().getDestination().update(sql);
550

    
551
        state.getConfig().getDestination().update("EXEC dbo.recalculateallstoredpaths");
552

    
553
        logger.info("PHASE 4: Make TreeIndex DONE");
554

    
555
        return success;
556
    }
557

    
558
    private static Integer findKingdomIdFromTreeIndex(TaxonBase<?> taxonBase,PesiExportState state) {
559
        Taxon taxon;
560
        if (taxonBase instanceof Synonym){
561
            taxon = ((Synonym) taxonBase).getAcceptedTaxon();
562
        }else{
563
            taxon = checkPseudoOrRelatedTaxon((Taxon)taxonBase);
564
        }
565
        if (taxon == null){
566
            NomenclaturalCode nomenclaturalCode = taxonBase.getName().getNameType();
567
            logger.warn("Taxon is synonym with no accepted taxon attached: " + taxonBase.getTitleCache() + ". The kingdom is taken from the nomenclatural code: " + PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode) );
568
            return PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
569
        } else{
570
            Set<TaxonNode> nodes = taxon.getTaxonNodes();
571
            if (nodes.isEmpty()){
572
                NomenclaturalCode nomenclaturalCode = taxon.getName().getNameType();
573
                logger.warn("The taxon has no nodes: " + taxon.getTitleCache() + ". The kingdom is taken from the nomenclatural code: " + PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
574
                return PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
575
            } else {
576
                if (nodes.size()>1){
577
                    logger.warn("The taxon has more then 1 taxon node: " + taxon.getTitleCache() + ". Take arbitrary one.");
578
                }
579
                String treeIndex = nodes.iterator().next().treeIndex();
580

    
581
                Pattern pattern = Pattern.compile("#t[0-9]+#([0-9]+#){3}");
582
                Matcher matcher = pattern.matcher(treeIndex);
583
                Integer kingdomID = null;
584
                if(matcher.find()) {
585
                    String treeIndexKingdom = matcher.group(0);
586
                    kingdomID = state.getTreeIndexKingdomMap().get(treeIndexKingdom);
587
                }else{
588
                    pattern = Pattern.compile("#t[0-9]+#([0-9]+#){2}");
589
                    matcher = pattern.matcher(treeIndex);
590
                    if(matcher.find()) {
591
                        String treeIndexKingdom = matcher.group(0);
592
                        kingdomID = state.getTreeIndexKingdomMap().get(treeIndexKingdom);
593
                    }
594
                }
595
                if(Rank.DOMAIN().equals(taxon.getName().getRank())){
596
                    return 0;
597
                }
598
                if(kingdomID == null){
599
                    logger.warn("Kingdom could not be defined for treeindex " + treeIndex);
600
                }
601
                return kingdomID;
602
            }
603
        }
604
    }
605

    
606
    private static Taxon checkPseudoOrRelatedTaxon(Taxon taxon) {
607
        if (!taxon.getTaxonNodes().isEmpty()){
608
            return taxon;
609
        }else if(hasPseudoTaxonRelationship(taxon)){
610
            return acceptedPseudoTaxon(taxon);
611
        }else if(isMisappliedNameOrProParteSynonym(taxon)){
612
            return acceptedTaxonConcept(taxon);
613
        }else{
614
            return taxon;
615
        }
616
    }
617

    
618
    private static Taxon acceptedPseudoTaxon(Taxon taxon) {
619
        for (TaxonRelationship rel : taxon.getRelationsFromThisTaxon()){
620
            if (TaxonRelationshipType.pseudoTaxonUuids().contains(rel.getType().getUuid())){
621
                return rel.getToTaxon();
622
            }
623
        }
624
        return taxon;
625
    }
626

    
627
    private static Taxon acceptedTaxonConcept(Taxon taxon) {
628
       for (TaxonRelationship rel : taxon.getRelationsFromThisTaxon()){
629
            if (TaxonRelationshipType.misappliedNameUuids().contains(rel.getType().getUuid())||
630
                    TaxonRelationshipType.proParteOrPartialSynonymUuids().contains(rel.getType().getUuid())){
631
                return rel.getToTaxon();
632
            }
633
        }
634
        return taxon;
635
    }
636

    
637
    private static boolean hasPseudoTaxonRelationship(Taxon taxon) {
638
        for (TaxonRelationship rel : taxon.getRelationsFromThisTaxon()){
639
            if (TaxonRelationshipType.pseudoTaxonUuids().contains(rel.getType().getUuid())){
640
                return true;
641
            }
642
        }
643
        return false;
644
    }
645

    
646
    private static boolean isMisappliedNameOrProParteSynonym(Taxon taxon) {
647
        for (TaxonRelationship rel : taxon.getRelationsFromThisTaxon()){
648
            if (TaxonRelationshipType.misappliedNameUuids().contains(rel.getType().getUuid())||
649
                    TaxonRelationshipType.proParteOrPartialSynonymUuids().contains(rel.getType().getUuid())){
650
                return true;
651
            }
652
        }
653
        return false;
654
    }
655

    
656
    //	"PHASE 5: Creating Inferred Synonyms..."
657
	private boolean doPhase05(PesiExportState state, PesiExportMapping mapping, PesiExportMapping synRelMapping) {
658
		int count;
659
		int pastCount;
660
		boolean success = true;
661
		// Get the limit for objects to save within a single transaction.
662
		if (! state.getConfig().isDoInferredSynonyms()){
663
			logger.info ("Ignore PHASE 5: Creating Inferred Synonyms...");
664
			return success;
665
		}
666

    
667
		int limit = state.getConfig().getLimitSave();
668
		// Create inferred synonyms for accepted taxa
669
		logger.info("PHASE 5: Creating Inferred Synonyms...");
670

    
671
		// Determine the count of elements in data warehouse database table Taxon
672
		currentTaxonId = determineTaxonCount(state);
673
		currentTaxonId++;
674

    
675
		count = 0;
676
		pastCount = 0;
677
		int pageSize = limit/10;
678
		int pageNumber = 1;
679
		String inferredSynonymPluralString = "Inferred Synonyms";
680

    
681
		// Start transaction
682
		TransactionStatus txStatus = startTransaction(true);
683
		if (logger.isDebugEnabled()) {
684
            logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
685
        }
686

    
687
		List<TaxonBase> taxonList = null;
688
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SPECIES(), pageSize, pageNumber)).size() > 0) {
689

    
690
		    Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
691

    
692
			if (logger.isDebugEnabled()) {
693
                logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
694
            }
695
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
696
					synRelMapping, taxonList));
697

    
698
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
699
			// Commit transaction
700
			commitTransaction(txStatus);
701
			if (logger.isDebugEnabled()){logger.debug("Committed transaction.");}
702
			logger.info("Exported " + (taxonList.size()) + " " + inferredSynonymPluralString + ". Total: " + count);
703
			//pastCount = count;
704

    
705
			// Save Rank Data and KingdomFk for inferred synonyms
706
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
707
			    TaxonName taxonName = inferredSynonymsDataToBeSaved.get(taxonFk);
708
                invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), taxonFk, kingdomFk, state);
709
			}
710

    
711
			// Start transaction
712
			txStatus = startTransaction(true);
713
			if (logger.isDebugEnabled()) {
714
                logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
715
            }
716

    
717
			// Increment pageNumber
718
			pageNumber++;
719
		}
720
		taxonList = null;
721
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SUBSPECIES(), pageSize, pageNumber)).size() > 0) {
722
			Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
723

    
724
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
725
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
726
					synRelMapping, taxonList));
727

    
728
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
729
			// Commit transaction
730
			commitTransaction(txStatus);
731
			logger.debug("Committed transaction.");
732
			logger.info("Exported " + taxonList.size()+ " " + inferredSynonymPluralString + ". Total: " + count);
733
			//pastCount = count;
734

    
735
			// Save Rank Data and KingdomFk for inferred synonyms
736
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
737
			    TaxonName taxonName = inferredSynonymsDataToBeSaved.get(taxonFk);
738
			    invokeRankDataAndKingdomFk(taxonName, taxonFk, kingdomFk, state);
739
			}
740

    
741
			// Start transaction
742
			txStatus = startTransaction(true);
743
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
744

    
745
			// Increment pageNumber
746
			pageNumber++;
747
			inferredSynonymsDataToBeSaved = null;
748
		}
749
		if (taxonList.size() == 0) {
750
			logger.info("No " + parentPluralString + " left to fetch.");
751
		}
752

    
753
		taxonList = null;
754
//		logger.warn("Taking snapshot at the end of phase 5 of taxonExport");
755
//		ProfilerController.memorySnapshot();
756

    
757
		// Commit transaction
758
		commitTransaction(txStatus);
759
		System.gc();
760
		logger.debug("Taking snapshot at the end of phase 5 after gc() of taxonExport");
761
		//ProfilerController.memorySnapshot();
762
		logger.debug("Committed transaction.");
763
		return success;
764
	}
765

    
766
	private Map<Integer, TaxonName> createInferredSynonymsForTaxonList(PesiExportState state,
767
			PesiExportMapping mapping, PesiExportMapping synRelMapping,	 List<TaxonBase> taxonList) {
768

    
769
		Taxon acceptedTaxon;
770
		Classification classification = null;
771
		List<Synonym> inferredSynonyms = null;
772
		boolean localSuccess = true;
773

    
774
		Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
775

    
776
		for (TaxonBase<?> taxonBase : taxonList) {
777

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

    
782
				if (taxonName.isZoological()) {
783
					kingdomFk = findKingdomIdFromTreeIndex(taxonBase, state);
784

    
785
					Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
786
					TaxonNode singleNode = null;
787

    
788
					if (taxonNodes.size() > 0) {
789
						// Determine the classification of the current TaxonNode
790

    
791
						singleNode = taxonNodes.iterator().next();
792
						if (singleNode != null) {
793
							classification = singleNode.getClassification();
794
						} else {
795
							logger.error("A TaxonNode belonging to this accepted Taxon is NULL: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() +")");
796
						}
797
					} else {
798
						// Classification could not be determined directly from this TaxonNode
799
						// The stored classification from another TaxonNode is used. It's a simple, but not a failsafe fallback solution.
800
						if (taxonNodes.size() == 0) {
801
							//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");
802
						}
803
					}
804

    
805
					if (classification != null) {
806
						try{
807
						    TaxonName name = acceptedTaxon.getName();
808
							//if (name.isSpecies() || name.isInfraSpecific()){
809
								inferredSynonyms  = getTaxonService().createAllInferredSynonyms(acceptedTaxon, classification, true);
810
							//}
811
//								inferredSynonyms = getTaxonService().createInferredSynonyms(classification, acceptedTaxon, SynonymType.INFERRED_GENUS_OF());
812
							if (inferredSynonyms != null) {
813
								for (Synonym synonym : inferredSynonyms) {
814
//									TaxonName synonymName = synonym.getName();
815
									MarkerType markerType =getUuidMarkerType(PesiTransformer.uuidMarkerGuidIsMissing, state);
816
									synonym.addMarker(Marker.NewInstance(markerType, true));
817
									// Both Synonym and its TaxonName have no valid Id yet
818
									synonym.setId(currentTaxonId++);
819

    
820

    
821
									localSuccess &= mapping.invoke(synonym);
822
									//get SynonymRelationship and export
823
									if (synonym.getAcceptedTaxon() == null ){
824
										IdentifiableSource source = synonym.getSources().iterator().next();
825
										if (source.getIdNamespace().contains("Potential combination")){
826
											acceptedTaxon.addSynonym(synonym, SynonymType.POTENTIAL_COMBINATION_OF());
827
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to potential combination");
828
										} else if (source.getIdNamespace().contains("Inferred Genus")){
829
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_GENUS_OF());
830
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred genus");
831
										} else if (source.getIdNamespace().contains("Inferred Epithet")){
832
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_EPITHET_OF());
833
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred epithet");
834
										} else{
835
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_SYNONYM_OF());
836
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred synonym");
837
										}
838

    
839
										localSuccess &= synRelMapping.invoke(synonym);
840
										if (!localSuccess) {
841
											logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
842
										}
843
									} else {
844
										localSuccess &= synRelMapping.invoke(synonym);
845
										if (!localSuccess) {
846
											logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
847
										} else {
848
											logger.info("Synonym relationship successfully exported: " + synonym.getTitleCache() + "  " +acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
849
										}
850
									}
851

    
852
									inferredSynonymsDataToBeSaved.put(synonym.getId(), synonym.getName());
853
								}
854
							}
855
						}catch(Exception e){
856
							logger.error(e.getMessage());
857
							e.printStackTrace();
858
						}
859
					} else {
860
						logger.error("Classification is NULL. Inferred Synonyms could not be created for this Taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() + ")");
861
					}
862
				} else {
863
//							logger.error("TaxonName is not a ZoologicalName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
864
				}
865
			} else {
866
				logger.error("This TaxonBase is not a Taxon even though it should be: " + taxonBase.getUuid() + " (" + taxonBase.getTitleCache() + ")");
867
			}
868
		}
869
		return inferredSynonymsDataToBeSaved;
870
	}
871

    
872

    
873
	/**
874
	 * Handles names that do not appear in taxa
875
	 * @param state
876
	 * @param mapping
877
	 */
878
	private boolean doNames(PesiExportState state, PesiExportMapping additionalSourceMapping)  throws SQLException {
879

    
880
		boolean success = true;
881
		if (! state.getConfig().isDoPureNames()){
882
			logger.info ("Ignore PHASE 1b: PureNames");
883
			return success;
884
		}
885

    
886
		try {
887
			PesiExportMapping mapping = getPureNameMapping(state);
888
			mapping.initialize(state);
889
			int count = 0;
890
			int pastCount = 0;
891
			success = true;
892
			// Get the limit for objects to save within a single transaction.
893
			int limit = state.getConfig().getLimitSave();
894

    
895
			logger.info("PHASE 1b: Export Pure Names ...");
896
			// Start transaction
897
			TransactionStatus txStatus = startTransaction(true);
898
			logger.info("Started new transaction for Pure Names. Fetching some " + pluralString + " (max: " + limit + ") ...");
899

    
900
			int partitionCount = 0;
901
			List<TaxonName> list;
902
			while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null   ) {
903

    
904
				logger.info("Fetched " + list.size() + " names without taxa. Exporting...");
905
				for (TaxonName taxonName : list) {
906
					doCount(count++, modCount, pluralString);
907
					success &= mapping.invoke(taxonName);
908
					//additional source
909
					if (taxonName.getNomenclaturalReference() != null || StringUtils.isNotBlank(taxonName.getNomenclaturalMicroReference() )){
910
						additionalSourceMapping.invoke(taxonName);
911
					}
912
				}
913

    
914
				// Commit transaction
915
				commitTransaction(txStatus);
916
				logger.debug("Committed transaction.");
917
				logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
918
				pastCount = count;
919

    
920
				// Start transaction
921
				txStatus = startTransaction(true);
922
				logger.info("Started new transaction for PureNames. Fetching some " + pluralString + " (max: " + limit + ") ...");
923
			}
924
			logger.info("No " + pluralString + " left to fetch.");
925

    
926
			// Commit transaction
927
			commitTransaction(txStatus);
928
			logger.debug("Committed transaction.");
929
		} catch (Exception e) {
930
			logger.error("Error occurred in pure name export");
931
			e.printStackTrace();
932
			success = false;
933
		}
934
		return success;
935
	}
936

    
937
	/**
938
	 * Determines the current number of entries in the DataWarehouse database table <code>Taxon</code>.
939
	 * @param state The {@link PesiExportState PesiExportState}.
940
	 * @return The count.
941
	 */
942
	private Integer determineTaxonCount(PesiExportState state) {
943
		Integer result = null;
944
		PesiExportConfigurator pesiConfig = state.getConfig();
945

    
946
		String sql;
947
		Source destination =  pesiConfig.getDestination();
948
		sql = "SELECT max(taxonId) FROM Taxon";
949
		destination.setQuery(sql);
950
		ResultSet resultSet = destination.getResultSet();
951
		try {
952
			resultSet.next();
953
			result = resultSet.getInt(1);
954
		} catch (SQLException e) {
955
			logger.error("TaxonCount could not be determined: " + e.getMessage());
956
			e.printStackTrace();
957
		}
958
		resultSet = null;
959
		return result;
960
	}
961

    
962
	/**
963
	 * Checks whether a parent at specific level has a specific Rank.
964
	 * @param taxonName A {@link TaxonNameBase TaxonName}.
965
	 * @param level The ancestor level.
966
	 * @param ancestorRank The ancestor rank.
967
	 * @return Whether a parent at a specific level has a specific Rank.
968
	 */
969
	private boolean validateAncestorOfSpecificRank(TaxonBase<?> taxonBase, int level, Rank ancestorRank) {
970
		boolean result = false;
971
		TaxonNode parentNode = null;
972
		if (taxonBase.isInstanceOf(Taxon.class)){
973
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
974
			// Get ancestor Taxon via TaxonNode
975
			Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();
976
			if (taxonNodes.size() == 1) {
977
				TaxonNode taxonNode = taxonNodes.iterator().next();
978
				if (taxonNode != null) {
979
					for (int i = 0; i < level; i++) {
980
						if (taxonNode != null) {
981
							taxonNode  = taxonNode.getParent();
982
						}
983
					}
984
					parentNode = taxonNode;
985
				}
986
			} else if (taxonNodes.size() > 1) {
987
				logger.error("This taxon has " + taxonNodes.size() + " taxonNodes: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
988
			}
989
		}
990
		//compare
991
		if (parentNode != null) {
992
			TaxonNode node = CdmBase.deproxy(parentNode, TaxonNode.class);
993
			Taxon parentTaxon = node.getTaxon();
994
			if (parentTaxon != null) {
995
				TaxonName parentTaxonName = parentTaxon.getName();
996
				if (parentTaxonName != null && parentTaxonName.getRank().equals(ancestorRank)) {
997
					result = true;
998
				}
999
			} else if (parentNode.treeIndex().matches("#t\\d+#\\d+#")) {
1000
				//do nothing (is root node)
1001
			} else {
1002
				logger.error("This TaxonNode has no Taxon: " + node.getUuid());
1003
			}
1004
		}
1005
		return result;
1006
	}
1007

    
1008
	/**
1009
	 * Returns the AnnotationType for a given UUID.
1010
	 * @param uuid The Annotation UUID.
1011
	 * @param label The Annotation label.
1012
	 * @param text The Annotation text.
1013
	 * @param labelAbbrev The Annotation label abbreviation.
1014
	 * @return The AnnotationType.
1015
	 */
1016
	protected AnnotationType getAnnotationType(UUID uuid, String label, String text, String labelAbbrev){
1017
		AnnotationType annotationType = (AnnotationType)getTermService().find(uuid);
1018
		if (annotationType == null) {
1019
			annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
1020
			annotationType.setUuid(uuid);
1021
//			annotationType.setVocabulary(AnnotationType.EDITORIAL().getVocabulary());
1022
			getTermService().save(annotationType);
1023
		}
1024
		return annotationType;
1025
	}
1026

    
1027
	private boolean invokeParentTaxonFk(Integer parentId, Integer childId) {
1028
		try {
1029
			parentTaxonFkStmt.setInt(1, parentId);
1030
			parentTaxonFkStmt.setInt(2, childId);
1031
			parentTaxonFkStmt.executeUpdate();
1032
			return true;
1033
		} catch (SQLException e) {
1034
			logger.warn("ParentTaxonFk (" + (parentId ==null? "-":parentId) + ") could not be inserted into database "
1035
			        + "for taxon "+ (childId == null? "-" :childId) + ": " + e.getMessage());
1036
			e.printStackTrace();
1037
			return false;
1038
		}
1039
	}
1040

    
1041

    
1042
	/**
1043
	 * Inserts Rank data and KingdomFk into the Taxon database table.
1044
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1045
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1046
	 * @param taxonFk The TaxonFk to store the values for.
1047
	 * @param state
1048
	 * @param kindomFk The KingdomFk.
1049
	 * @return Whether save was successful or not.
1050
	 */
1051
	private boolean invokeRankDataAndKingdomFk(TaxonName taxonName,
1052
	        Integer taxonFk, Integer kingdomFk, PesiExportState state) {
1053

    
1054
	    try {
1055
			Integer rankFk = getRankFk(taxonName, kingdomFk);
1056
			if (rankFk != null) {
1057
				rankUpdateStmt.setInt(1, rankFk);
1058
			} else {
1059
				rankUpdateStmt.setObject(1, null);
1060
			}
1061

    
1062
			String rankCache = getRankCache(taxonName, kingdomFk, state);
1063
			if (rankCache != null) {
1064
				rankUpdateStmt.setString(2, rankCache);
1065
			} else {
1066
				rankUpdateStmt.setObject(2, null);
1067
			}
1068

    
1069
			if (kingdomFk != null) {
1070

    
1071
				rankUpdateStmt.setInt(3, kingdomFk);
1072
			} else {
1073
				rankUpdateStmt.setObject(3, null);
1074
			}
1075

    
1076
			if (taxonFk != null) {
1077
				rankUpdateStmt.setInt(4, taxonFk);
1078
			} else {
1079
				rankUpdateStmt.setObject(4, null);
1080
			}
1081

    
1082
			rankUpdateStmt.executeUpdate();
1083
			return true;
1084
		} catch (SQLException e) {
1085
			logger.error("Data (RankFk, RankCache, KingdomFk) could not be inserted into database: " + e.getMessage());
1086
			e.printStackTrace();
1087
			return false;
1088
		}
1089
	}
1090

    
1091
	/**
1092
	 * Inserts Rank data, TypeNameFk, KingdomFk, expertFk and speciesExpertFk into the Taxon database table.
1093
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1094
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1095
	 * @param taxonFk The TaxonFk to store the values for.
1096
	 * @param typeNameFk The TypeNameFk.
1097
	 * @param state
1098
	 * @param kindomFk The KingdomFk.
1099
	 * @param expertFk The ExpertFk.
1100
	 * @param speciesExpertFk The SpeciesExpertFk.
1101
	 * @return Whether save was successful or not.
1102
	 */
1103
	private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonName taxonName,
1104
			Integer taxonFk, Integer typeNameFk, Integer kingdomFk, PesiExportState state) {
1105

    
1106
	    NomenclaturalCode nomenclaturalCodes = taxonName.getNameType();
1107

    
1108
	    Integer rankFk = null;
1109
	    try {
1110
			int index = 1;
1111
			rankFk = getRankFk(taxonName, kingdomFk);
1112
			if (rankFk != null) {
1113
				rankTypeExpertsUpdateStmt.setInt(index++, rankFk);
1114
			} else {
1115
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1116
			}
1117

    
1118
			String rankCache = getRankCache(taxonName, kingdomFk, state);
1119
			if (rankCache != null) {
1120
				rankTypeExpertsUpdateStmt.setString(index++, rankCache);
1121
			} else {
1122
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1123
			}
1124

    
1125
			if (typeNameFk != null) {
1126
				rankTypeExpertsUpdateStmt.setInt(index++, typeNameFk);
1127
			} else {
1128
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1129
			}
1130

    
1131
			if (kingdomFk != null) {
1132
				rankTypeExpertsUpdateStmt.setInt(index++, kingdomFk);
1133
			} else {
1134
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1135
			}
1136

    
1137
//			if (expertFk != null) {
1138
//				rankTypeExpertsUpdateStmt.setInt(5, expertFk);
1139
//			} else {
1140
//				rankTypeExpertsUpdateStmt.setObject(5, null);
1141
//			}
1142
//
1143
//			//TODO handle experts GUIDS
1144
//			if (speciesExpertFk != null) {
1145
//				rankTypeExpertsUpdateStmt.setInt(6, speciesExpertFk);
1146
//			} else {
1147
//				rankTypeExpertsUpdateStmt.setObject(6, null);
1148
//			}
1149
//
1150
			if (taxonFk != null) {
1151
				rankTypeExpertsUpdateStmt.setInt(index++, taxonFk);
1152
			} else {
1153
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1154
			}
1155

    
1156
			rankTypeExpertsUpdateStmt.executeUpdate();
1157
			return true;
1158
		} catch (SQLException e) {
1159
			logger.error("Data could not be inserted into database: " + e.getMessage() + "; rankFk = " + rankFk + "; kingdomFk = " + kingdomFk );
1160
			e.printStackTrace();
1161
			return false;
1162
		} catch (Exception e) {
1163
			logger.error("Some exception occurred: " + e.getMessage() + "; rankFk = " + rankFk + "; kingdomFk = " + kingdomFk);
1164
			e.printStackTrace();
1165
			return false;
1166
		}
1167
	}
1168

    
1169
	/**
1170
	 * Deletes all entries of database tables related to <code>Taxon</code>.
1171
	 * @param state The {@link PesiExportState PesiExportState}.
1172
	 * @return Whether the delete operation was successful or not.
1173
	 */
1174
	protected boolean doDelete(PesiExportState state) {
1175
		PesiExportConfigurator pesiConfig = state.getConfig();
1176

    
1177
		String sql;
1178
		Source destination =  pesiConfig.getDestination();
1179

    
1180
		// Clear Taxon
1181
		sql = "DELETE FROM " + dbTableName;
1182
		destination.update(sql);
1183

    
1184
		//TODO due to foreign keys we should also delete all tables linking to Taxon table
1185

    
1186
		return true;
1187
	}
1188

    
1189
	private static Integer getRankFk(TaxonName taxonName, NomenclaturalCode nomenclaturalCode) {
1190
	    Integer kingdomId = PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
1191
	    return getRankFk(taxonName, kingdomId);
1192
	}
1193

    
1194
	/**
1195
	 * Returns the <code>RankFk</code> attribute.
1196
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1197
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1198
	 * @return The <code>RankFk</code> attribute.
1199
	 * @see MethodMapper
1200
	 */
1201
	private static Integer getRankFk(TaxonName taxonName, Integer kingdomId) {
1202
		Integer result = null;
1203
		try {
1204
			if (taxonName != null) {
1205
				if (taxonName.getRank() == null) {
1206
					logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1207
				} else {
1208
					result = PesiTransformer.rank2RankId(taxonName.getRank(), kingdomId);
1209
				}
1210
				if (result == null) {
1211
					logger.warn("Rank could not be determined for PESI-Kingdom-Id " + kingdomId + " and TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1212
				}
1213
			}
1214
		} catch (Exception e) {
1215
			e.printStackTrace();
1216
		}
1217
		return result;
1218
	}
1219

    
1220
	private static String getRankCache(TaxonName taxonName, PesiExportState state) {
1221
	    List<TaxonNode> nodes = getTaxonNodes(taxonName);
1222
        Integer kingdomId = findKingdomIdFromTreeIndex(nodes.iterator().next().getTaxon(), state);
1223
        return getRankCache(taxonName, kingdomId, state);
1224
	}
1225

    
1226

    
1227
	/**
1228
	 * Returns the <code>RankCache</code> attribute.
1229
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1230
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1231
	 * @param state
1232
	 * @return The <code>RankCache</code> attribute.
1233
	 * @see MethodMapper
1234
	 */
1235
	private static String getRankCache(TaxonName taxonName, Integer kingdomFk, PesiExportState state) {
1236
	    if (Rank.DOMAIN().equals(taxonName.getRank())){
1237
            return state.getTransformer().getCacheByRankAndKingdom(Rank.DOMAIN(), null);
1238
        }else if (kingdomFk != null) {
1239
            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), kingdomFk);
1240
        }else if (taxonName.getNameType() != null){
1241
            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), PesiTransformer.nomenclaturalCode2Kingdom(taxonName.getNameType()));
1242
        }else{
1243
			logger.warn("No kingdom ID could be defined for name " + taxonName.getUuid());
1244
			return null;
1245
		}
1246
	}
1247

    
1248
    private static List<TaxonNode> getTaxonNodes(TaxonName taxonName) {
1249
        List<TaxonNode> result = new ArrayList<>();
1250
        for (TaxonBase<?> tb:taxonName.getTaxonBases()){
1251
            Taxon taxon;
1252
            //TODO handle ERMS taxon relationships
1253
            if (tb.isInstanceOf(Taxon.class)){
1254
                taxon = CdmBase.deproxy(tb, Taxon.class);
1255
            }else{
1256
                taxon = CdmBase.deproxy(tb, Synonym.class).getAcceptedTaxon();
1257
            }
1258
            if (taxon != null){
1259
                for (TaxonNode node : taxon.getTaxonNodes()){
1260
                    result.add(node);
1261
                }
1262
            }
1263
        }
1264
        return result;
1265
    }
1266

    
1267
    /**
1268
	 * Returns the <code>DisplayName</code> attribute.
1269
	 * @param taxon The {@link TaxonBase Taxon}.
1270
	 * @return The <code>DisplayName</code> attribute.
1271
	 * @see MethodMapper
1272
	 */
1273
	@SuppressWarnings("unused")  //used by Mapper
1274
	private static String getDisplayName(TaxonBase<?> taxon) {
1275
		TaxonName taxonName = taxon.getName();
1276
		String result = getDisplayName(taxonName);
1277
		if (isMisappliedName(taxon)){
1278
			result = result + " " + getAuthorString(taxon);
1279
		}
1280
		return result;
1281
	}
1282

    
1283
	/**
1284
	 * Returns the <code>AuthorString</code> attribute.
1285
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1286
	 * @return The <code>AuthorString</code> attribute.
1287
	 * @see MethodMapper
1288
	 */
1289
	//used by mapper
1290
	protected static String getAuthorString(TaxonBase<?> taxon) {
1291
		try {
1292
			String result = null;
1293
			boolean isNonViralName = false;
1294
			String authorshipCache = null;
1295
			TaxonName taxonName = taxon.getName();
1296
			if (taxonName != null && taxonName.isNonViral()){
1297
				authorshipCache = taxonName.getAuthorshipCache();
1298
				isNonViralName = true;
1299
			}
1300
			result = authorshipCache;
1301

    
1302
			// For a misapplied names there are special rules
1303
			if (isMisappliedName(taxon)){
1304
				if (taxon.getSec() != null){
1305
					String secTitle = taxon.getSec().getTitleCache();
1306
					if (! secTitle.startsWith("auct")){
1307
						secTitle = "sensu " + secTitle;
1308
					}else if (secTitle.equals("auct")){  //may be removed once the title cache is generated correctly for references with title auct. #
1309
						secTitle = "auct.";
1310
					}
1311
					return secTitle;
1312
				}else if (StringUtils.isBlank(authorshipCache)) {
1313
					// Set authorshipCache to "auct."
1314
					result = PesiTransformer.AUCT_STRING;
1315
				}else{
1316
					result = PesiTransformer.AUCT_STRING;
1317
//					result = authorshipCache;
1318
				}
1319
			}
1320

    
1321
			if (taxonName == null){
1322
				logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1323
			}else if (! isNonViralName){
1324
				logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1325
			}
1326

    
1327
			if (StringUtils.isBlank(result)) {
1328
				return null;
1329
			} else {
1330
				return result;
1331
			}
1332
		} catch (Exception e) {
1333
			e.printStackTrace();
1334
			return null;
1335
		}
1336
	}
1337

    
1338
	/**
1339
	 * Returns the <code>DisplayName</code> attribute.
1340
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1341
	 * @return The <code>DisplayName</code> attribute.
1342
	 * @see MethodMapper
1343
	 */
1344
	 //used by Mapper
1345
	private static String getDisplayName(TaxonName taxonName) {
1346
		// TODO: extension?
1347
		if (taxonName == null) {
1348
			return null;
1349
		}else{
1350
		    taxonName = CdmBase.deproxy(taxonName);
1351
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1352
			HTMLTagRules tagRules = new HTMLTagRules().
1353
					addRule(TagEnum.name, "i").
1354
					addRule(TagEnum.nomStatus, "@status@");
1355

    
1356
			String result;
1357
			if (getSources(taxonName).contains(PesiSource.ERMS)){
1358
			    result = cacheStrategy.getTitleCache(taxonName, tagRules);  //according to SQL script (also in ERMS sources are not abbreviated)
1359
			}else if (getSources(taxonName).contains(PesiSource.EM)){
1360
			    result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
1361
			}else{
1362
			    //TODO define for FE + IF and for multiple sources
1363
			    result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
1364
			}
1365
			return result.replaceAll(",?\\<@status@\\>.*\\</@status@\\>", "");
1366
		}
1367
	}
1368

    
1369
	@SuppressWarnings("unused")
1370
	private static String getGUID(TaxonName taxonName) {
1371
		UUID uuid = taxonName.getUuid();
1372
		String result = "NameUUID:" + uuid.toString();
1373
		return result;
1374
	}
1375

    
1376
	/**
1377
	 * Returns the <code>WebShowName</code> attribute for a taxon.
1378
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1379
	 * @return The <code>WebShowName</code> attribute.
1380
	 * @see MethodMapper
1381
	*/
1382
	@SuppressWarnings("unused")
1383
	private static String getWebShowName(TaxonBase<?> taxon) {
1384
		TaxonName taxonName = taxon.getName();
1385
		String result = getWebShowName(taxonName);
1386
		if (isMisappliedName(taxon)){
1387
			result = result + " " + getAuthorString(taxon);
1388
		}
1389
		return result;
1390
	}
1391

    
1392
	/**
1393
	 * Returns the <code>WebShowName</code> attribute.
1394
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1395
	 * @return The <code>WebShowName</code> attribute.
1396
	 * @see MethodMapper
1397
	 */
1398
	private static String getWebShowName(TaxonName taxonName) {
1399
		//TODO extensions?
1400
		if (taxonName == null) {
1401
			return null;
1402
		}else{
1403
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1404

    
1405
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1406
			String result = cacheStrategy.getTitleCache(taxonName, tagRules);
1407
			return result;
1408
		}
1409
	}
1410

    
1411
	/**
1412
	 * Returns the <code>WebSearchName</code> attribute.
1413
	 * @param taxonName The {@link NonViralName NonViralName}.
1414
	 * @return The <code>WebSearchName</code> attribute.
1415
	 * @see MethodMapper
1416
	 */
1417
	@SuppressWarnings("unused")
1418
	private static String getWebSearchName(TaxonName taxonName) {
1419
		//TODO extensions?
1420
	    TaxonNameDefaultCacheStrategy strategy = getCacheStrategy(taxonName);
1421
		String result = strategy.getNameCache(taxonName);
1422
		return result;
1423
	}
1424

    
1425
	/**
1426
	 * Returns the <code>FullName</code> attribute.
1427
	 * @param taxonName The {@link NonViralName NonViralName}.
1428
	 * @return The <code>FullName</code> attribute.
1429
	 * @see MethodMapper
1430
	 */
1431
	@SuppressWarnings("unused")
1432
	private static String getFullName(TaxonName taxonName) {
1433
		//TODO extensions?
1434
		String result = getCacheStrategy(taxonName).getTitleCache(taxonName);
1435
		Iterator<Taxon> taxa = taxonName.getTaxa().iterator();
1436
		if (taxonName.getTaxa().size() >0){
1437
			if (taxonName.getTaxa().size() == 1){
1438
				Taxon taxon = taxa.next();
1439
				if (isMisappliedName(taxon)){
1440
					result = result + " " + getAuthorString(taxon);
1441
				}
1442
				taxon = null;
1443
			}
1444
		}
1445
		return result;
1446
	}
1447

    
1448
	/**
1449
	 * Returns the SourceNameCache for the AdditionalSource table
1450
	 * @param taxonName
1451
	 * @return
1452
	 */
1453
	static boolean isFirstAbbrevTitle = true;
1454
	@SuppressWarnings("unused")
1455
	private static String getSourceNameCache(TaxonName taxonName) {
1456
		if (taxonName != null){
1457
			Reference nomRef = taxonName.getNomenclaturalReference();
1458
			if (nomRef != null ){
1459
			    if (isFirstAbbrevTitle){
1460
			        //#5388 is definetely not the correct ticket number
1461
			        logger.warn("Semantics of getAbbrevTitleCache has changed. Please check if output is still correct. See #5388");
1462
			        isFirstAbbrevTitle = false;
1463
			    }
1464
			    return nomRef.getAbbrevTitleCache();
1465
			}
1466
		}
1467
		return null;
1468
	}
1469

    
1470
	/**
1471
	 * Returns the nomenclatural reference which is the reference
1472
	 * including the detail (microreference).
1473
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1474
	 * @return The <code>AuthorString</code> attribute.
1475
	 * @see MethodMapper
1476
	 */
1477
	@SuppressWarnings("unused")
1478
	private static String getNomRefString(TaxonName taxonName) {
1479
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1480
		if (ref == null){
1481
			return null;
1482
		}
1483
		String result = null;
1484
		EnumSet<PesiSource> sources = getSources(taxonName);
1485
		if(sources.contains(PesiSource.EM)){
1486
		    if (! ref.isProtectedAbbrevTitleCache()){
1487
		        ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1488
		    }
1489
		    result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1490
		}else if(sources.contains(PesiSource.FE)||sources.contains(PesiSource.IF) ){
1491
            //TODO still need to check if correct for FE + IF
1492
		    if (! ref.isProtectedAbbrevTitleCache()){
1493
                ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1494
            }
1495
            result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1496
            return result;   // according to SQL script
1497
		}else if(sources.contains(PesiSource.ERMS)) {
1498
            //result = null; //according to SQL script
1499
		}else{
1500
		    logger.warn("Source not yet supported");
1501
		}
1502
		return result;
1503
	}
1504

    
1505
	/**
1506
	 * Returns the <code>NameStatusFk</code> attribute.
1507
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1508
	 * @return The <code>NameStatusFk</code> attribute.
1509
	 * @see MethodMapper
1510
	 */
1511
	@SuppressWarnings("unused")
1512
	private static Integer getNameStatusFk(TaxonName taxonName) {
1513
		Integer result = null;
1514

    
1515
		NomenclaturalStatus status = getNameStatus(taxonName);
1516
		if (status != null) {
1517
			result = PesiTransformer.nomStatus2nomStatusFk(status.getType());
1518
		}
1519
		return result;
1520
	}
1521

    
1522
	/**
1523
	 * Returns the <code>NameStatusCache</code> attribute.
1524
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1525
	 * @return The <code>NameStatusCache</code> attribute.
1526
	 * @throws UndefinedTransformerMethodException
1527
	 * @see MethodMapper
1528
	 */
1529
	@SuppressWarnings("unused")
1530
	private static String getNameStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1531
		String result = null;
1532
		NomenclaturalStatus status = getNameStatus(taxonName);
1533
		if (status != null) {
1534
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1535
		}
1536
		return result;
1537
	}
1538

    
1539
	private static NomenclaturalStatus getNameStatus(TaxonName taxonName) {
1540
		try {
1541
			if (taxonName != null) {
1542
			    Set<NomenclaturalStatus> states = taxonName.getStatus();
1543
				if (states.size() == 1) {
1544
					NomenclaturalStatus status = states.iterator().next();
1545
					return status;
1546
				} else if (states.size() > 1) {
1547
					logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1548
				}
1549
			}
1550
		} catch (Exception e) {
1551
			e.printStackTrace();
1552
		}
1553
		return null;
1554
	}
1555
	/**
1556
	 * Returns the <code>TaxonStatusFk</code> attribute.
1557
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1558
	 * @param state The {@link PesiExportState PesiExportState}.
1559
	 * @return The <code>TaxonStatusFk</code> attribute.
1560
	 * @see MethodMapper
1561
	 */
1562
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1563
		Integer result = null;
1564

    
1565
		try {
1566
			if (isMisappliedName(taxon)) {
1567
				Synonym synonym = Synonym.NewInstance(null, null);
1568

    
1569
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1570
				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1571
			} else {
1572
				result = PesiTransformer.taxonBase2statusFk(taxon);
1573
			}
1574
		} catch (Exception e) {
1575
			e.printStackTrace();
1576
		}
1577
		return result;
1578
	}
1579

    
1580
	/**
1581
	 * Returns the <code>TaxonStatusCache</code> attribute.
1582
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1583
	 * @param state The {@link PesiExportState PesiExportState}.
1584
	 * @return The <code>TaxonStatusCache</code> attribute.
1585
	 * @throws UndefinedTransformerMethodException
1586
	 * @see MethodMapper
1587
	 */
1588
	@SuppressWarnings("unused")
1589
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1590
		return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1591
	}
1592

    
1593
    /**
1594
     * Returns the <code>TaxonFk1</code> attribute. It corresponds to a CDM <code>TaxonRelationship</code>.
1595
     * @param relationship The {@link RelationshipBase Relationship}.
1596
     * @param state The {@link PesiExportState PesiExportState}.
1597
     * @return The <code>TaxonFk1</code> attribute.
1598
     * @see MethodMapper
1599
     */
1600
    @SuppressWarnings("unused")
1601
    private static Integer getSynonym(Synonym synonym, PesiExportState state) {
1602
         return state.getDbId(synonym);
1603
    }
1604

    
1605
	/**
1606
	 * Returns the <code>TypeNameFk</code> attribute.
1607
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1608
	 * @param state The {@link PesiExportState PesiExportState}.
1609
	 * @return The <code>TypeNameFk</code> attribute.
1610
	 * @see MethodMapper
1611
	 */
1612
	private static Integer getTypeNameFk(TaxonName taxonName, PesiExportState state) {
1613
		Integer result = null;
1614
		if (taxonName != null) {
1615
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1616
			if (nameTypeDesignations.size() == 1) {
1617
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1618
				if (nameTypeDesignation != null) {
1619
					TaxonName typeName = nameTypeDesignation.getTypeName();
1620
					if (typeName != null) {
1621
					    result = state.getDbId(typeName);
1622
					}
1623
				}
1624
			} else if (nameTypeDesignations.size() > 1) {
1625
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1626
			}
1627
		}
1628
		return result;
1629
	}
1630

    
1631
	/**
1632
	 * Returns the <code>TypeFullnameCache</code> attribute.
1633
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1634
	 * @return The <code>TypeFullnameCache</code> attribute.
1635
	 * @see MethodMapper
1636
	 */
1637
	@SuppressWarnings("unused")
1638
	private static String getTypeFullnameCache(TaxonName taxonName) {
1639
		String result = null;
1640

    
1641
		try {
1642
    		if (taxonName != null) {
1643
    			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1644
    			if (nameTypeDesignations.size() == 1) {
1645
    				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1646
    				if (nameTypeDesignation != null) {
1647
    					TaxonName typeName = nameTypeDesignation.getTypeName();
1648
    					if (typeName != null) {
1649
    						result = typeName.getTitleCache();
1650
    					}
1651
    				}
1652
    			} else if (nameTypeDesignations.size() > 1) {
1653
    				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1654
    			}
1655
    		}
1656
		} catch (Exception e) {
1657
			e.printStackTrace();
1658
		}
1659
		return result;
1660
	}
1661

    
1662

    
1663
	/**
1664
	 * Returns the <code>QualityStatusFk</code> attribute.
1665
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1666
	 * @return The <code>QualityStatusFk</code> attribute.
1667
	 * @see MethodMapper
1668
	 */
1669
	private static Integer getQualityStatusFk(TaxonName taxonName) {
1670
	    EnumSet<PesiSource> sources = getSources(taxonName);
1671
		return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1672
	}
1673

    
1674
	/**
1675
	 * Returns the <code>QualityStatusCache</code> attribute.
1676
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1677
	 * @return The <code>QualityStatusCache</code> attribute.
1678
	 * @throws UndefinedTransformerMethodException
1679
	 * @see MethodMapper
1680
	 */
1681
	@SuppressWarnings("unused")
1682
	private static String getQualityStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1683
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1684
	}
1685

    
1686

    
1687
	/**
1688
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1689
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1690
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1691
	 * @see MethodMapper
1692
	 */
1693
	@SuppressWarnings("unused")
1694
	private static Integer getTypeDesignationStatusFk(TaxonName taxonName) {
1695
		Integer result = null;
1696

    
1697
		try {
1698
		if (taxonName != null) {
1699
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1700
			if (typeDesignations.size() == 1) {
1701
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1702
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1703
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1704
			} else if (typeDesignations.size() > 1) {
1705
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1706
			}
1707
		}
1708

    
1709
		} catch (Exception e) {
1710
			e.printStackTrace();
1711
		}
1712
		return result;
1713
	}
1714

    
1715
	/**
1716
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1717
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1718
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1719
	 * @see MethodMapper
1720
	 */
1721
	@SuppressWarnings("unused")
1722
	private static String getTypeDesignationStatusCache(TaxonName taxonName) {
1723
		String result = null;
1724

    
1725
		try {
1726
		if (taxonName != null) {
1727
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1728
			if (typeDesignations.size() == 1) {
1729
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1730
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1731
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
1732
			} else if (typeDesignations.size() > 1) {
1733
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1734
			}
1735
		}
1736

    
1737
		} catch (Exception e) {
1738
			e.printStackTrace();
1739
		}
1740
		return result;
1741
	}
1742

    
1743
	/**
1744
	 * Returns the <code>FossilStatusFk</code> attribute.
1745
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1746
	 * @return The <code>FossilStatusFk</code> attribute.
1747
	 * @see MethodMapper
1748
	 */
1749
	@SuppressWarnings("unused")
1750
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
1751
		Integer result = null;
1752

    
1753
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
1754
		if (fossilStatuus.size() == 0){
1755
			return null;
1756
		}else if (fossilStatuus.size() > 1){
1757
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
1758
		}
1759
		String fossilStatus = fossilStatuus.iterator().next();
1760

    
1761
		int statusFk = state.getTransformer().fossilStatusCache2FossilStatusFk(fossilStatus);
1762
		return statusFk;
1763
	}
1764

    
1765
	/**
1766
	 * Returns the <code>FossilStatusCache</code> attribute.
1767
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1768
	 * @return The <code>FossilStatusCache</code> attribute.
1769
	 * @see MethodMapper
1770
	 */
1771
	@SuppressWarnings("unused")
1772
	private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
1773
		String result = null;
1774
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
1775
		if (fossilStatuus.size() == 0){
1776
			return null;
1777
		}
1778
		for (String strFossilStatus : fossilStatuus){
1779
			result = CdmUtils.concat(";", result, strFossilStatus);
1780
		}
1781
		return result;
1782
	}
1783

    
1784
	/**
1785
	 * Returns the <code>IdInSource</code> attribute.
1786
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1787
	 * @return The <code>IdInSource</code> attribute.
1788
	 * @see MethodMapper
1789
	 */
1790
	@SuppressWarnings("unused")
1791
	private static String getIdInSource(IdentifiableEntity<?> taxonName) {
1792
		String result = null;
1793

    
1794
		try {
1795
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
1796
			if (sources.size() > 1){
1797
				logger.warn("There is > 1 Pesi source. This is not yet handled.");
1798
			}
1799
			if (sources.size() == 0){
1800
				logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1801
			}
1802
			for (IdentifiableSource source : sources) {
1803
				Reference ref = source.getCitation();
1804
				UUID refUuid = ref.getUuid();
1805
				String idInSource = source.getIdInSource();
1806
				if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
1807
					result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
1808
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
1809
					result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
1810
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
1811
					result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
1812
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
1813
					result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
1814
				}else{
1815
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");}
1816
				}
1817

    
1818
				String sourceIdNameSpace = source.getIdNamespace();
1819
				if (sourceIdNameSpace != null) {
1820
					if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
1821
						result =  idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
1822
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
1823
						result =  idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
1824
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
1825
						result =  idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
1826
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
1827
						result =  idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
1828
					}
1829
				}
1830
				if (result == null) {
1831
					logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
1832
				}
1833
			}
1834
		} catch (Exception e) {
1835
			e.printStackTrace();
1836
			logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
1837
		}
1838

    
1839
		if (result == null) {
1840
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1841
		}
1842
		return result;
1843
	}
1844

    
1845
	/**
1846
	 * Returns the idInSource for a given TaxonName only.
1847
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1848
	 * @return The idInSource.
1849
	 */
1850
	private static String getIdInSourceOnly(IdentifiableEntity<?> identEntity) {
1851
		String result = null;
1852

    
1853
		// Get the sources first
1854
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
1855

    
1856
		// Determine the idInSource
1857
		if (sources.size() == 1) {
1858
			IdentifiableSource source = sources.iterator().next();
1859
			if (source != null) {
1860
				result = source.getIdInSource();
1861
			}
1862
		} else if (sources.size() > 1) {
1863
			int count = 1;
1864
			result = "";
1865
			for (IdentifiableSource source : sources) {
1866
				result += source.getIdInSource();
1867
				if (count < sources.size()) {
1868
					result += "; ";
1869
				}
1870
				count++;
1871
			}
1872

    
1873
		}
1874

    
1875
		return result;
1876
	}
1877

    
1878
	/**
1879
	 * Returns the <code>GUID</code> attribute.
1880
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1881
	 * @return The <code>GUID</code> attribute.
1882
	 * @see MethodMapper
1883
	 */
1884
	private static String getGUID(TaxonBase<?> taxon) {
1885
		if (taxon.getLsid() != null ){
1886
			return taxon.getLsid().getLsid();
1887
		}else if (taxon.hasMarker(PesiTransformer.uuidMarkerGuidIsMissing, true)){
1888
			return null;
1889
		}else{
1890
			return taxon.getUuid().toString();
1891
		}
1892
	}
1893

    
1894
	/**
1895
	 * Returns the <code>DerivedFromGuid</code> attribute.
1896
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1897
	 * @return The <code>DerivedFromGuid</code> attribute.
1898
	 * @see MethodMapper
1899
	 */
1900
	@SuppressWarnings("unused")
1901
	private static String getDerivedFromGuid(TaxonBase<?> taxon) {
1902
		String result = null;
1903
		try {
1904
		// The same as GUID for now
1905
		result = getGUID(taxon);
1906
		} catch (Exception e) {
1907
			e.printStackTrace();
1908
		}
1909
		return result;
1910
	}
1911

    
1912
	/**
1913
	 * Returns the <code>CacheCitation</code> attribute.
1914
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1915
	 * @return The CacheCitation.
1916
	 * @see MethodMapper
1917
	 */
1918
	@SuppressWarnings("unused")
1919
	private static String getCacheCitation(TaxonBase<?> taxon) {
1920
		// !!! See also doPhaseUpdates
1921

    
1922
		TaxonName taxonName = taxon.getName();
1923
		String result = "";
1924
		//TODO implement anew for taxa
1925
		try {
1926
			EnumSet<PesiSource> sources = getSources(taxon);
1927
			if (sources.isEmpty()) {
1928
//				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1929
			} else if (sources.contains(PesiSource.ERMS)) {
1930
				Set<Extension> extensions = taxon.getExtensions();
1931
				for (Extension extension : extensions) {
1932
					if (extension.getType().equals(cacheCitationExtensionType)) {
1933
						result = extension.getValue();
1934
					}
1935
				}
1936
			} else {
1937
				String expertName = getExpertName(taxon);
1938
				String webShowName = getWebShowName(taxonName);
1939

    
1940
				// idInSource only
1941
				String idInSource = getIdInSourceOnly(taxonName);
1942

    
1943
				// build the cacheCitation
1944
				if (expertName != null) {
1945
					result += expertName + ". ";
1946
				} else {
1947
					if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
1948
				}
1949
				if (webShowName != null) {
1950
					result += webShowName + ". ";
1951
				} else {
1952
					logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1953
				}
1954

    
1955
				if (getOriginalDB(taxonName).equals("FaEu")) {
1956
					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
1957
				} else if (getOriginalDB(taxonName).equals("EM")) {
1958
					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
1959
				}
1960

    
1961
				if (idInSource != null) {
1962
					result += idInSource;
1963
				} else {
1964
					logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1965
				}
1966
			}
1967
		} catch (Exception e) {
1968
			e.printStackTrace();
1969
		}
1970

    
1971
		if (StringUtils.isBlank(result)) {
1972
			return null;
1973
		} else {
1974
			return result;
1975
		}
1976
	}
1977

    
1978
	/**
1979
	 * Returns the <code>OriginalDB</code> attribute.
1980
	 * @param identifiableEntity
1981
	 * @return The <code>OriginalDB</code> attribute.
1982
	 * @see MethodMapper
1983
	 */
1984
	@SuppressWarnings("unused")
1985
	private static String getOriginalDB(IdentifiableEntity<?> identifiableEntity) {
1986
		EnumSet<PesiSource> sources  = getSources(identifiableEntity);
1987
		return PesiTransformer.getOriginalDbBySources(sources);
1988
	}
1989

    
1990
	/**
1991
	 * Returns the <code>LastAction</code> attribute.
1992
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1993
	 * @return The <code>LastAction</code> attribute.
1994
	 * @see MethodMapper
1995
	 */
1996
	@SuppressWarnings("unused")
1997
	private static String getLastAction(IdentifiableEntity<?> identEntity) {
1998
		String result = null;
1999
		try {
2000
		Set<Extension> extensions = identEntity.getExtensions();
2001
		for (Extension extension : extensions) {
2002
			if (extension.getType().equals(lastActionExtensionType)) {
2003
				result = extension.getValue();
2004
			}
2005
		}
2006
		} catch (Exception e) {
2007
			e.printStackTrace();
2008
		}
2009
		return result;
2010
	}
2011

    
2012
	/**
2013
	 * Returns the <code>LastActionDate</code> attribute.
2014
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2015
	 * @return The <code>LastActionDate</code> attribute.
2016
	 * @see MethodMapper
2017
	 */
2018
	@SuppressWarnings({ "unused" })
2019
	private static DateTime getLastActionDate(IdentifiableEntity<?> identEntity) {
2020
		DateTime result = null;
2021
		try {
2022
			Set<Extension> extensions = identEntity.getExtensions();
2023
			for (Extension extension : extensions) {
2024
				if (extension.getType().equals(lastActionDateExtensionType)) {
2025
					String dateTime = extension.getValue();
2026
					if (dateTime != null) {
2027
						DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
2028
						result = formatter.parseDateTime(dateTime);
2029
					}
2030
				}
2031
			}
2032
		} catch (Exception e) {
2033
			e.printStackTrace();
2034
		}
2035
		return result;
2036
	}
2037

    
2038
	/**
2039
	 * Returns the <code>ExpertName</code> attribute.
2040
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2041
	 * @return The <code>ExpertName</code> attribute.
2042
	 * @see MethodMapper
2043
	 */
2044
	@SuppressWarnings("unused")
2045
	private static String getExpertName(TaxonBase<?> taxonName) {
2046
		String result = null;
2047
		try {
2048
		Set<Extension> extensions = taxonName.getExtensions();
2049
		for (Extension extension : extensions) {
2050
			if (extension.getType().equals(expertNameExtensionType)) {
2051
				result = extension.getValue();
2052
			}
2053
		}
2054
		} catch (Exception e) {
2055
			e.printStackTrace();
2056
		}
2057
		return result;
2058
	}
2059

    
2060
	/**
2061
	 * Returns the <code>ExpertFk</code> attribute.
2062
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2063
	 * @param state The {@link PesiExportState PesiExportState}.
2064
	 * @return The <code>ExpertFk</code> attribute.
2065
	 * @see MethodMapper
2066
	 */
2067
	private static Integer getExpertFk(Reference reference, PesiExportState state) {
2068
		Integer result = state.getDbId(reference);
2069
		return result;
2070
	}
2071

    
2072
	/**
2073
	 * Returns the <code>SpeciesExpertName</code> attribute.
2074
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2075
	 * @return The <code>SpeciesExpertName</code> attribute.
2076
	 * @see MethodMapper
2077
	 */
2078
	@SuppressWarnings("unused")
2079
	private static String getSpeciesExpertName(TaxonBase<?> taxonName) {
2080
		String result = null;
2081
		try {
2082
		Set<Extension> extensions = taxonName.getExtensions();
2083
		for (Extension extension : extensions) {
2084
			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2085
				result = extension.getValue();
2086
			}
2087
		}
2088
		} catch (Exception e) {
2089
			e.printStackTrace();
2090
		}
2091
		return result;
2092
	}
2093

    
2094
	/**
2095
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2096
	 * @param reference The {@link Reference Reference}.
2097
	 * @param state The {@link PesiExportState PesiExportState}.
2098
	 * @return The <code>SpeciesExpertFk</code> attribute.
2099
	 * @see MethodMapper
2100
	 */
2101
	private static Integer getSpeciesExpertFk(Reference reference, PesiExportState state) {
2102
		Integer result = state.getDbId(reference);
2103
		return result;
2104
	}
2105

    
2106
	protected static TaxonNameDefaultCacheStrategy getCacheStrategy(TaxonName taxonName) {
2107
		taxonName = CdmBase.deproxy(taxonName);
2108
		TaxonNameDefaultCacheStrategy cacheStrategy;
2109
		if (taxonName.isZoological()){
2110
			cacheStrategy = zooNameStrategy;
2111
		}else if (taxonName.isBotanical()) {
2112
			cacheStrategy = nonViralNameStrategy;
2113
		}else if (taxonName.isNonViral()) {
2114
			cacheStrategy = nonViralNameStrategy;
2115
		}else if (taxonName.isBacterial()) {
2116
			cacheStrategy = nonViralNameStrategy;
2117
		}else{
2118
			logger.error("Unhandled taxon name type. Can't define strategy class");
2119
			cacheStrategy = nonViralNameStrategy;
2120
		}
2121
		return cacheStrategy;
2122
	}
2123

    
2124
	/**
2125
	 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2126
	 * @param relationship The {@link RelationshipBase Relationship}.
2127
	 * @return The <code>RelTaxonQualifierFk</code> attribute.
2128
	 * @see MethodMapper
2129
	 */
2130
	@SuppressWarnings("unused")
2131
	private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2132
		return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2133
	}
2134

    
2135
    @SuppressWarnings("unused")
2136
    private static String getSynonymTypeCache(Synonym synonym, PesiExportState state) {
2137
        String result = null;
2138
        NomenclaturalCode code = null;
2139
        code = CdmBase.deproxy(synonym, Synonym.class).getAcceptedTaxon().getName().getNameType();
2140

    
2141
        if (code != null) {
2142
            result = state.getConfig().getTransformer().getCacheBySynonymType(synonym, code);
2143
        } else {
2144
            logger.error("NomenclaturalCode is NULL while creating the following synonym: " + synonym.getUuid());
2145
        }
2146
        return result;
2147
    }
2148

    
2149
// ********************************** MAPPINGS ********************************/
2150

    
2151
	/**
2152
	 * Returns the CDM to PESI specific export mappings.
2153
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2154
	 */
2155
	private PesiExportMapping getMapping() {
2156
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2157

    
2158
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2159
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2160
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2161
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2162

    
2163
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2164

    
2165
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2166
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2167
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misapllied Names are handled differently
2168
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2169

    
2170
		// DisplayName
2171
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2172

    
2173
		// FossilStatus (Fk, Cache)
2174
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2175
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2176

    
2177
		//handled by name mapping
2178
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2179
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2180

    
2181
		//experts
2182
		ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtSpeciesExpertName);
2183
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2184

    
2185
		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
2186
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2187

    
2188
		//handled in Phase02 now
2189
//		mapping.addMapper(MethodMapper.NewInstance("ParentTaxonFk", this, TaxonBase.class, PesiExportState.class));  //by AM, doesn't work, FK exception
2190
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2191

    
2192
		addNameMappers(mapping);
2193

    
2194
		return mapping;
2195
	}
2196

    
2197
	/**
2198
	 * Returns the CDM to PESI specific export mappings.
2199
	 * @param state
2200
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2201
	 * @throws UndefinedTransformerMethodException
2202
	 */
2203
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2204
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2205

    
2206
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2207

    
2208
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonName.class));
2209
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonName.class));
2210
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonName.class, PesiExportState.class));
2211
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
2212
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2213
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
2214
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonName.class));
2215
		mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonName.class));
2216

    
2217
		// DisplayName
2218
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonName.class));
2219

    
2220
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2221
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2222

    
2223
		addNameMappers(mapping);
2224
		//TODO add author mapper, TypeNameFk
2225

    
2226
		return mapping;
2227
	}
2228

    
2229
	private void addNameMappers(PesiExportMapping mapping) {
2230
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2231
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2232
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2233
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2234

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

    
2238
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonName.class));
2239

    
2240

    
2241
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonName.class));
2242

    
2243
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonName.class));
2244
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonName.class, PesiExportState.class));
2245
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
2246
		//TODO TypeNameFk
2247

    
2248
		//quality status
2249
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonName.class));
2250
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonName.class, PesiExportState.class));
2251

    
2252
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2253
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2254

    
2255
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2256

    
2257
	}
2258

    
2259
	private PesiExportMapping getSynRelMapping() {
2260
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2261
		logger.warn("SynRelMapping currently not implemented. Needs to be checked");
2262

    
2263
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getSynonym", Synonym.class, PesiExportState.class));
2264
		mapping.addMapper(DbObjectMapper.NewInstance("acceptedTaxon", "TaxonFk2"));
2265
		mapping.addMapper(DbObjectMapper.NewInstance("type", "RelTaxonQualifierFk"));
2266
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this.getClass(), "getSynonymTypeCache", Synonym.class, PesiExportState.class));
2267
		// TODO
2268
//		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2269

    
2270
		return mapping;
2271
	}
2272

    
2273
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state) {
2274
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2275

    
2276
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2277
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2278

    
2279
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2280
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2281
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonName.class));
2282

    
2283
		//we have only nomenclatural references here
2284
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2285
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2286

    
2287
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2288

    
2289
		return mapping;
2290
	}
2291

    
2292

    
2293
    @Override
2294
    protected boolean doCheck(PesiExportState state) {
2295
        return true;
2296
    }
2297

    
2298
    @Override
2299
    protected boolean isIgnore(PesiExportState state) {
2300
        return ! state.getConfig().isDoTaxa();
2301
    }
2302
}
(13-13/14)