Project

General

Profile

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

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

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

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

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

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

    
108
//	private PreparedStatement parentTaxonFk_TreeIndex_KingdomFkStmts;
109
	private PreparedStatement parentTaxonFkStmt;
110
	private PreparedStatement rankTypeExpertsUpdateStmt;
111
	private PreparedStatement rankUpdateStmt;
112
	private Integer kingdomFk;
113

    
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

    
120
	public static TaxonNameDefaultCacheStrategy zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
121
	public static TaxonNameDefaultCacheStrategy nonViralNameStrategy = TaxonNameDefaultCacheStrategy.NewInstance();
122
	private static int currentTaxonId;
123

    
124
	enum NamePosition {
125
		beginning,
126
		end,
127
		between,
128
		alone,
129
		nowhere
130
	}
131

    
132
	public PesiTaxonExport() {
133
		super();
134
	}
135

    
136
	@Override
137
	public Class<? extends CdmBase> getStandardMethodParameter() {
138
		return standardMethodParameter;
139
	}
140

    
141
	@Override
142
	protected void doInvoke(PesiExportState state) {
143
		try {
144
			logger.info("*** Started Making " + pluralString + " ...");
145

    
146
			initPreparedStatements(state);
147

    
148
			// Stores whether this invoke was successful or not.
149
			boolean success = true;
150

    
151
			// PESI: Clear the database table Taxon.
152
			doDelete(state);
153

    
154
			// Get specific mappings: (CDM) Taxon -> (PESI) Taxon
155
			PesiExportMapping mapping = getMapping();
156
			PesiExportMapping synonymRelMapping = getSynRelMapping();
157
			PesiExportMapping additionalSourceMapping = getAdditionalSourceMapping(state);
158

    
159
			// Initialize the db mapper
160
			mapping.initialize(state);
161
			synonymRelMapping.initialize(state);
162
			additionalSourceMapping.initialize(state);
163

    
164
			// Find extensionTypes
165
			lastActionExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtLastAction);
166
			lastActionDateExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtLastActionDate);
167
			expertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
168
			speciesExpertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtSpeciesExpertName);
169
			cacheCitationExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtCacheCitation);
170

    
171
			//Export Taxa..
172
			success &= doPhase01(state, mapping, additionalSourceMapping);
173

    
174
			//"PHASE 1b: Handle names without taxa ...
175
			success &= doNames(state, additionalSourceMapping);
176

    
177
			// 2nd Round: Add ParentTaxonFk to each taxon
178
			success &= doPhase02(state);
179

    
180
			//PHASE 3: Add Rank data, KingdomFk, TypeNameFk ...
181
			success &= doPhase03(state);
182

    
183
			// 4nd Round: Add TreeIndex to each taxon
184
			success &= doPhase04(state);
185

    
186
			//"PHASE 5: Creating Inferred Synonyms...
187
			success &= doPhase05(state, mapping, synonymRelMapping);
188

    
189
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
190

    
191
			if (!success){
192
				state.getResult().addError("An error occurred in PesiTaxonExport.doInvoke. Success = false");
193
			}
194
			return;
195
		} catch (Exception e) {
196
			e.printStackTrace();
197
			logger.error(e.getMessage());
198
			state.getResult().addException(e);
199
		}
200
	}
201

    
202

    
203
	private void initPreparedStatements(PesiExportState state) throws SQLException {
204
//		initTreeIndexStatement(state);
205
		initRankExpertsUpdateStmt(state);
206
		initRankUpdateStatement(state);
207

    
208
		initParentFkStatement(state);
209
	}
210

    
211
//	// Prepare TreeIndex-And-KingdomFk-Statement
212
//	private void initTreeIndexStatement(PesiExportState state) throws SQLException {
213
//		Connection connection = state.getConfig().getDestination().getConnection();
214
//		String parentTaxonFk_TreeIndex_KingdomFkSql = "UPDATE Taxon SET ParentTaxonFk = ?, TreeIndex = ? WHERE TaxonId = ?";
215
//		parentTaxonFk_TreeIndex_KingdomFkStmt = connection.prepareStatement(parentTaxonFk_TreeIndex_KingdomFkSql);
216
//	}
217

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

    
225
	private void initRankUpdateStatement(PesiExportState state) throws SQLException {
226
		Connection connection = state.getConfig().getDestination().getConnection();
227
		String rankSql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, KingdomFk = ? WHERE TaxonId = ?";
228
		rankUpdateStmt = connection.prepareStatement(rankSql);
229
	}
230

    
231
	private void initRankExpertsUpdateStmt(PesiExportState state) throws SQLException {
232
//		String sql_old = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ?, " +
233
//				"ExpertFk = ?, SpeciesExpertFk = ? WHERE TaxonId = ?";
234
		//TODO handle experts GUIDs
235
		Connection connection = state.getConfig().getDestination().getConnection();
236

    
237
		String sql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ? " +
238
				" WHERE TaxonId = ?";
239
		rankTypeExpertsUpdateStmt = connection.prepareStatement(sql);
240
	}
241

    
242
	private boolean doPhase01(PesiExportState state, PesiExportMapping mapping, PesiExportMapping additionalSourceMapping){
243

    
244
	    int count = 0;
245
		int pastCount = 0;
246
		boolean success = true;
247
		// Get the limit for objects to save within a single transaction.
248
		int limit = state.getConfig().getLimitSave();
249

    
250
		logger.info("PHASE 1: Export Taxa...limit is " + limit);
251
		// Start transaction
252
		TransactionStatus txStatus = startTransaction(true);
253
		if (logger.isDebugEnabled()) {
254
            logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
255
            logger.info("Taking snapshot at the beginning of phase 1 of taxonExport");
256
            //ProfilerController.memorySnapshot();
257
        }
258

    
259
		int partitionCount = 0;
260
		List<TaxonBase<?>> list;
261
		while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)) != null   ) {
262

    
263
			logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
264

    
265
			for (TaxonBase<?> taxon : list) {
266
				doCount(count++, modCount, pluralString);
267
				TaxonName taxonName = taxon.getName();
268

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

    
303
				//TODO switch on again, leads to some warnings in ERMS for taxa of not correctly handled kingdoms
304
				validatePhaseOne(taxon, nvn);
305
			}
306

    
307
			// Commit transaction
308
			commitTransaction(txStatus);
309
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 01)");
310
			pastCount = count;
311

    
312
			// Start new transaction
313
			txStatus = startTransaction(true);
314
			if (logger.isDebugEnabled()) {
315
                logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
316
            }
317

    
318
		}
319
		logger.debug("No " + pluralString + " left to fetch.");
320

    
321
		// Commit transaction
322
		commitTransaction(txStatus);
323
		txStatus = null;
324

    
325
		return success;
326
	}
327

    
328
	private void validatePhaseOne(TaxonBase<?> taxon, TaxonName taxonName) {
329

    
330
	    // Check whether some rules are violated
331
		String genusOrUninomial = taxonName.getGenusOrUninomial();
332
		String specificEpithet = taxonName.getSpecificEpithet();
333
		String infraSpecificEpithet = taxonName.getInfraSpecificEpithet();
334
		String infraGenericEpithet = taxonName.getInfraGenericEpithet();
335
		Rank rank =  taxonName.getRank();
336

    
337
		//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
338
//		Integer rankFk = getRankFk(taxonName, taxonName.getNameType());
339
//		if (rankFk == null) {
340
//			logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
341
//		} else {
342

    
343
			// Check whether infraGenericEpithet is set correctly
344
			// 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
345
			// 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
346

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

    
366
			if (infraGenericEpithet == null && rank.isInfraGenericButNotSpeciesGroup()) {
367
				logger.warn("InfraGenericEpithet was not determined although it should exist for infra generic names: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
368
			}
369
			if (specificEpithet != null && (rank.isInfraGenericButNotSpeciesGroup()||rank.isGenus()||rank.isSupraGeneric())) {
370
				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() + ")");
371
			}
372
			if (infraSpecificEpithet != null && !rank.isInfraSpecific()) {
373
				String message = "InfraSpecificEpithet '" +infraSpecificEpithet + "' was determined for rank " + rank.getTitleCache() + " although it should only exist for rank species and higher: "  + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")";
374
				if (StringUtils.isNotBlank(infraSpecificEpithet)){
375
					logger.warn(message);
376
				}else{
377
					logger.warn(message);
378
				}
379
			}
380
//		}
381
		if (infraSpecificEpithet != null && specificEpithet == null) {
382
			logger.warn("An infraSpecificEpithet was determined, but a specificEpithet was not determined: "  + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
383
		}
384
		if (genusOrUninomial == null) {
385
			logger.warn("GenusOrUninomial was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
386
		}
387
	}
388

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

    
401
		// Get the limit for objects to save within a single transaction.
402
		int limit = state.getConfig().getLimitSave();
403

    
404
		insertBiota(state);
405

    
406
		logger.info("PHASE 2: Make ParentFk and Biota ... limit is " + limit);
407
		// Start transaction
408
		TransactionStatus txStatus = startTransaction(true);
409
		int partitionCount = 0;
410

    
411
//		ProfilerController.memorySnapshot();
412
		List<Taxon> list;
413
		while ((list = getNextTaxonPartition(Taxon.class, limit, partitionCount++, null)) != null   ) {
414

    
415
			if(logger.isDebugEnabled()) {
416
                logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
417
            }
418
			for (Taxon taxon : list) {
419
				for (TaxonNode node : taxon.getTaxonNodes()){
420
					doCount(count++, modCount, pluralString);
421
					TaxonNode parentNode = node.getParent();
422
					if (parentNode != null && isPesiTaxon(parentNode.getTaxon())){//exclude root taxa and unpublished parents (relevant for "Valueless" parent for E+M Rubus taxa). Usually a parent should not be unpublished
423
						int childId = state.getDbId( taxon);
424
						int parentId = state.getDbId(parentNode.getTaxon());
425
						success &= invokeParentTaxonFk(parentId, childId);
426
					}
427
				}
428
			}
429

    
430
			// Commit transaction
431
			commitTransaction(txStatus);
432
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 2)");
433
			pastCount = count;
434
			// Start transaction
435
			txStatus = startTransaction(true);
436
			if (logger.isDebugEnabled()){
437
			    logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
438
			}
439
		}
440
		logger.debug("No " + pluralString + " left to fetch.");
441

    
442
		// Commit transaction
443
		commitTransaction(txStatus);
444

    
445
		return success;
446
	}
447

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

    
466
	//PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...
467
	private boolean doPhase03(PesiExportState state) {
468
		int count = 0;
469
		int pastCount = 0;
470
		boolean success = true;
471
		if (! state.getConfig().isDoTreeIndex()){
472
			logger.info ("Ignore PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
473
			return success;
474
		}
475

    
476
		addValuelessTaxonToKingdomMap(state);
477

    
478
		// Get the limit for objects to save within a single transaction.
479
		int limit = state.getConfig().getLimitSave();
480

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

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

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

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

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

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

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

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

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

    
539
    private void addValuelessTaxonToKingdomMap(PesiExportState state) {
540
        TransactionStatus txStatus = startTransaction();
541
        Taxon valuelessTaxon = (Taxon)getTaxonService().find(PesiTransformer.uuidTaxonValuelessEuroMed);
542
        if (valuelessTaxon != null){
543
            String treeIndex = valuelessTaxon.getTaxonNodes().iterator().next().treeIndex();
544
            Integer kingdomId = PesiTransformer.pesiKingdomMap.get("Plantae");
545
            state.getTreeIndexKingdomMap().put(treeIndex, kingdomId);
546
        }
547
        commitTransaction(txStatus);
548
    }
549

    
550
    // 4th round: Add TreeIndex to each taxon
551
    private boolean doPhase04(PesiExportState state) {
552
        boolean success = true;
553

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

    
556
        //TODO test if possible to move to phase 02
557
        String sql = " UPDATE Taxon SET ParentTaxonFk = (SELECT TaxonId FROM Taxon WHERE RankFk = 0) " +
558
                " WHERE (RankFk = 10) and TaxonStatusFk = 1 ";
559
        state.getConfig().getDestination().update(sql);
560

    
561
        state.getConfig().getDestination().update("EXEC dbo.recalculateallstoredpaths");
562

    
563
        logger.info("PHASE 4: Make TreeIndex DONE");
564

    
565
        return success;
566
    }
567

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

    
591
                Pattern pattern = Pattern.compile("#t[0-9]+#([0-9]+#){3}");
592
                Matcher matcher = pattern.matcher(treeIndex);
593
                Integer kingdomID = null;
594
                if(matcher.find()) {
595
                    String treeIndexKingdom = matcher.group(0);
596
                    kingdomID = state.getTreeIndexKingdomMap().get(treeIndexKingdom);
597
                }
598
                if (kingdomID == null){
599
                    pattern = Pattern.compile("#t[0-9]+#([0-9]+#){2}");
600
                    matcher = pattern.matcher(treeIndex);
601
                    if(matcher.find()) {
602
                        String treeIndexKingdom = matcher.group(0);
603
                        Map<String, Integer> map = state.getTreeIndexKingdomMap();
604
                        kingdomID = map.get(treeIndexKingdom);
605
                    }
606
                }
607
                if(Rank.DOMAIN().equals(taxon.getName().getRank())){
608
                    return 0;
609
                }
610
                if(kingdomID == null){
611
                    logger.warn("Kingdom could not be defined for treeindex " + treeIndex);
612
                }
613
                return kingdomID;
614
            }
615
        }
616
    }
617

    
618
    private static Taxon checkPseudoOrRelatedTaxon(Taxon taxon) {
619
        if (!taxon.getTaxonNodes().isEmpty()){
620
            return taxon;
621
        }else if(hasPseudoTaxonRelationship(taxon)){
622
            return acceptedPseudoTaxon(taxon);
623
        }else if(isMisappliedNameOrProParteSynonym(taxon)){
624
            return acceptedTaxonConcept(taxon);
625
        }else{
626
            return taxon;
627
        }
628
    }
629

    
630
    private static Taxon acceptedPseudoTaxon(Taxon taxon) {
631
        for (TaxonRelationship rel : taxon.getRelationsFromThisTaxon()){
632
            if (TaxonRelationshipType.pseudoTaxonUuids().contains(rel.getType().getUuid())){
633
                return rel.getToTaxon();
634
            }
635
        }
636
        return taxon;
637
    }
638

    
639
    private static Taxon acceptedTaxonConcept(Taxon taxon) {
640
       for (TaxonRelationship rel : taxon.getRelationsFromThisTaxon()){
641
            if (TaxonRelationshipType.misappliedNameUuids().contains(rel.getType().getUuid())||
642
                    TaxonRelationshipType.proParteOrPartialSynonymUuids().contains(rel.getType().getUuid())){
643
                return rel.getToTaxon();
644
            }
645
        }
646
        return taxon;
647
    }
648

    
649
    private static boolean hasPseudoTaxonRelationship(Taxon taxon) {
650
        for (TaxonRelationship rel : taxon.getRelationsFromThisTaxon()){
651
            if (TaxonRelationshipType.pseudoTaxonUuids().contains(rel.getType().getUuid())){
652
                return true;
653
            }
654
        }
655
        return false;
656
    }
657

    
658
    private static boolean isMisappliedNameOrProParteSynonym(Taxon taxon) {
659
        for (TaxonRelationship rel : taxon.getRelationsFromThisTaxon()){
660
            if (TaxonRelationshipType.misappliedNameUuids().contains(rel.getType().getUuid())||
661
                    TaxonRelationshipType.proParteOrPartialSynonymUuids().contains(rel.getType().getUuid())){
662
                return true;
663
            }
664
        }
665
        return false;
666
    }
667

    
668
    //	"PHASE 5: Creating Inferred Synonyms..."
669
	private boolean doPhase05(PesiExportState state, PesiExportMapping mapping, PesiExportMapping synRelMapping) {
670
		int count;
671
		int pastCount;
672
		boolean success = true;
673
		// Get the limit for objects to save within a single transaction.
674
		if (! state.getConfig().isDoInferredSynonyms()){
675
			logger.info ("Ignore PHASE 5: Creating Inferred Synonyms...");
676
			return success;
677
		}
678

    
679
		int limit = state.getConfig().getLimitSave();
680
		// Create inferred synonyms for accepted taxa
681
		logger.info("PHASE 5: Creating Inferred Synonyms...");
682

    
683
		// Determine the count of elements in data warehouse database table Taxon
684
		currentTaxonId = determineTaxonCount(state);
685
		currentTaxonId++;
686

    
687
		count = 0;
688
		pastCount = 0;
689
		int pageSize = limit/10;
690
		int pageNumber = 1;
691
		String inferredSynonymPluralString = "Inferred Synonyms";
692

    
693
		// Start transaction
694
		TransactionStatus txStatus = startTransaction(true);
695
		if (logger.isDebugEnabled()) {
696
            logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
697
        }
698

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

    
702
		    Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
703

    
704
			if (logger.isDebugEnabled()) {
705
                logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
706
            }
707
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
708
					synRelMapping, taxonList));
709

    
710
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
711
			// Commit transaction
712
			commitTransaction(txStatus);
713
			if (logger.isDebugEnabled()){logger.debug("Committed transaction.");}
714
			logger.info("Exported " + (taxonList.size()) + " " + inferredSynonymPluralString + ". Total: " + count);
715
			//pastCount = count;
716

    
717
			// Save Rank Data and KingdomFk for inferred synonyms
718
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
719
			    TaxonName taxonName = inferredSynonymsDataToBeSaved.get(taxonFk);
720
                invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), taxonFk, kingdomFk, state);
721
			}
722

    
723
			// Start transaction
724
			txStatus = startTransaction(true);
725
			if (logger.isDebugEnabled()) {
726
                logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
727
            }
728

    
729
			// Increment pageNumber
730
			pageNumber++;
731
		}
732
		taxonList = null;
733
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SUBSPECIES(), pageSize, pageNumber)).size() > 0) {
734
			Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
735

    
736
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
737
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
738
					synRelMapping, taxonList));
739

    
740
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
741
			// Commit transaction
742
			commitTransaction(txStatus);
743
			logger.debug("Committed transaction.");
744
			logger.info("Exported " + taxonList.size()+ " " + inferredSynonymPluralString + ". Total: " + count);
745
			//pastCount = count;
746

    
747
			// Save Rank Data and KingdomFk for inferred synonyms
748
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
749
			    TaxonName taxonName = inferredSynonymsDataToBeSaved.get(taxonFk);
750
			    invokeRankDataAndKingdomFk(taxonName, taxonFk, kingdomFk, state);
751
			}
752

    
753
			// Start transaction
754
			txStatus = startTransaction(true);
755
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
756

    
757
			// Increment pageNumber
758
			pageNumber++;
759
			inferredSynonymsDataToBeSaved = null;
760
		}
761
		if (taxonList.size() == 0) {
762
			logger.info("No " + parentPluralString + " left to fetch.");
763
		}
764

    
765
		taxonList = null;
766
//		logger.warn("Taking snapshot at the end of phase 5 of taxonExport");
767
//		ProfilerController.memorySnapshot();
768

    
769
		// Commit transaction
770
		commitTransaction(txStatus);
771
		System.gc();
772
		logger.debug("Taking snapshot at the end of phase 5 after gc() of taxonExport");
773
		//ProfilerController.memorySnapshot();
774
		logger.debug("Committed transaction.");
775
		return success;
776
	}
777

    
778
	private Map<Integer, TaxonName> createInferredSynonymsForTaxonList(PesiExportState state,
779
			PesiExportMapping mapping, PesiExportMapping synRelMapping,	 List<TaxonBase> taxonList) {
780

    
781
		Taxon acceptedTaxon;
782
		Classification classification = null;
783
		List<Synonym> inferredSynonyms = null;
784
		boolean localSuccess = true;
785

    
786
		Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
787

    
788
		for (TaxonBase<?> taxonBase : taxonList) {
789

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

    
794
				if (taxonName.isZoological()) {
795
					kingdomFk = findKingdomIdFromTreeIndex(taxonBase, state);
796

    
797
					Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
798
					TaxonNode singleNode = null;
799

    
800
					if (taxonNodes.size() > 0) {
801
						// Determine the classification of the current TaxonNode
802

    
803
						singleNode = taxonNodes.iterator().next();
804
						if (singleNode != null) {
805
							classification = singleNode.getClassification();
806
						} else {
807
							logger.error("A TaxonNode belonging to this accepted Taxon is NULL: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() +")");
808
						}
809
					} else {
810
						// Classification could not be determined directly from this TaxonNode
811
						// The stored classification from another TaxonNode is used. It's a simple, but not a failsafe fallback solution.
812
						if (taxonNodes.size() == 0) {
813
							//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");
814
						}
815
					}
816

    
817
					if (classification != null) {
818
						try{
819
						    TaxonName name = acceptedTaxon.getName();
820
							//if (name.isSpecies() || name.isInfraSpecific()){
821
								inferredSynonyms  = getTaxonService().createAllInferredSynonyms(acceptedTaxon, classification, true);
822
							//}
823
//								inferredSynonyms = getTaxonService().createInferredSynonyms(classification, acceptedTaxon, SynonymType.INFERRED_GENUS_OF());
824
							if (inferredSynonyms != null) {
825
								for (Synonym synonym : inferredSynonyms) {
826
//									TaxonName synonymName = synonym.getName();
827
									MarkerType markerType =getUuidMarkerType(PesiTransformer.uuidMarkerGuidIsMissing, state);
828
									synonym.addMarker(Marker.NewInstance(markerType, true));
829
									// Both Synonym and its TaxonName have no valid Id yet
830
									synonym.setId(currentTaxonId++);
831

    
832

    
833
									localSuccess &= mapping.invoke(synonym);
834
									//get SynonymRelationship and export
835
									if (synonym.getAcceptedTaxon() == null ){
836
										IdentifiableSource source = synonym.getSources().iterator().next();
837
										if (source.getIdNamespace().contains("Potential combination")){
838
											acceptedTaxon.addSynonym(synonym, SynonymType.POTENTIAL_COMBINATION_OF());
839
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to potential combination");
840
										} else if (source.getIdNamespace().contains("Inferred Genus")){
841
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_GENUS_OF());
842
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred genus");
843
										} else if (source.getIdNamespace().contains("Inferred Epithet")){
844
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_EPITHET_OF());
845
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred epithet");
846
										} else{
847
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_SYNONYM_OF());
848
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred synonym");
849
										}
850

    
851
										localSuccess &= synRelMapping.invoke(synonym);
852
										if (!localSuccess) {
853
											logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
854
										}
855
									} else {
856
										localSuccess &= synRelMapping.invoke(synonym);
857
										if (!localSuccess) {
858
											logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
859
										} else {
860
											logger.info("Synonym relationship successfully exported: " + synonym.getTitleCache() + "  " +acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
861
										}
862
									}
863

    
864
									inferredSynonymsDataToBeSaved.put(synonym.getId(), synonym.getName());
865
								}
866
							}
867
						}catch(Exception e){
868
							logger.error(e.getMessage());
869
							e.printStackTrace();
870
						}
871
					} else {
872
						logger.error("Classification is NULL. Inferred Synonyms could not be created for this Taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() + ")");
873
					}
874
				} else {
875
//							logger.error("TaxonName is not a ZoologicalName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
876
				}
877
			} else {
878
				logger.error("This TaxonBase is not a Taxon even though it should be: " + taxonBase.getUuid() + " (" + taxonBase.getTitleCache() + ")");
879
			}
880
		}
881
		return inferredSynonymsDataToBeSaved;
882
	}
883

    
884

    
885
	/**
886
	 * Handles names that do not appear in taxa
887
	 * @param state
888
	 * @param mapping
889
	 */
890
	private boolean doNames(PesiExportState state, PesiExportMapping additionalSourceMapping)  throws SQLException {
891

    
892
		boolean success = true;
893
		if (! state.getConfig().isDoPureNames()){
894
			logger.info ("Ignore PHASE 1b: PureNames");
895
			return success;
896
		}
897

    
898
		try {
899
			PesiExportMapping mapping = getPureNameMapping(state);
900
			mapping.initialize(state);
901
			int count = 0;
902
			int pastCount = 0;
903
			success = true;
904
			// Get the limit for objects to save within a single transaction.
905
			int limit = state.getConfig().getLimitSave();
906

    
907
			logger.info("PHASE 1b: Export Pure Names ...");
908
			// Start transaction
909
			TransactionStatus txStatus = startTransaction(true);
910
			logger.info("Started new transaction for Pure Names. Fetching some " + pluralString + " (max: " + limit + ") ...");
911

    
912
			int partitionCount = 0;
913
			List<TaxonName> list;
914
			while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null   ) {
915

    
916
				logger.debug("Fetched " + list.size() + pluralStringNames + " without taxa. Exporting...");
917
				for (TaxonName taxonName : list) {
918
					doCount(count++, modCount, pluralString);
919
					success &= mapping.invoke(taxonName);
920
					//additional source
921
					if (taxonName.getNomenclaturalReference() != null || StringUtils.isNotBlank(taxonName.getNomenclaturalMicroReference() )){
922
						additionalSourceMapping.invoke(taxonName);
923
					}
924
				}
925

    
926
				// Commit transaction
927
				commitTransaction(txStatus);
928
				logger.debug("Committed transaction.");
929
				logger.info("Exported " + (count - pastCount) + " " + pluralStringNames + ". Total: " + count + ". Partition: " + partitionCount);
930
				pastCount = count;
931

    
932
				// Start transaction
933
				txStatus = startTransaction(true);
934
				logger.debug("Started new transaction for PureNames. Fetching some " + pluralString + " (max: " + limit + ") ...");
935
			}
936
			logger.debug("No " + pluralString + " left to fetch.");
937

    
938
			// Commit transaction
939
			commitTransaction(txStatus);
940
			logger.debug("Committed transaction.");
941
		} catch (Exception e) {
942
			logger.error("Error occurred in pure name export");
943
			e.printStackTrace();
944
			success = false;
945
		}
946
		return success;
947
	}
948

    
949
	/**
950
	 * Determines the current number of entries in the DataWarehouse database table <code>Taxon</code>.
951
	 * @param state The {@link PesiExportState PesiExportState}.
952
	 * @return The count.
953
	 */
954
	private Integer determineTaxonCount(PesiExportState state) {
955
		Integer result = null;
956
		PesiExportConfigurator pesiConfig = state.getConfig();
957

    
958
		String sql;
959
		Source destination =  pesiConfig.getDestination();
960
		sql = "SELECT max(taxonId) FROM Taxon";
961
		destination.setQuery(sql);
962
		ResultSet resultSet = destination.getResultSet();
963
		try {
964
			resultSet.next();
965
			result = resultSet.getInt(1);
966
		} catch (SQLException e) {
967
			logger.error("TaxonCount could not be determined: " + e.getMessage());
968
			e.printStackTrace();
969
		}
970
		resultSet = null;
971
		return result;
972
	}
973

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

    
1020
	/**
1021
	 * Returns the AnnotationType for a given UUID.
1022
	 * @param uuid The Annotation UUID.
1023
	 * @param label The Annotation label.
1024
	 * @param text The Annotation text.
1025
	 * @param labelAbbrev The Annotation label abbreviation.
1026
	 * @return The AnnotationType.
1027
	 */
1028
	protected AnnotationType getAnnotationType(UUID uuid, String label, String text, String labelAbbrev){
1029
		AnnotationType annotationType = (AnnotationType)getTermService().find(uuid);
1030
		if (annotationType == null) {
1031
			annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
1032
			annotationType.setUuid(uuid);
1033
//			annotationType.setVocabulary(AnnotationType.EDITORIAL().getVocabulary());
1034
			getTermService().save(annotationType);
1035
		}
1036
		return annotationType;
1037
	}
1038

    
1039
	private boolean invokeParentTaxonFk(Integer parentId, Integer childId) {
1040
		try {
1041
			parentTaxonFkStmt.setInt(1, parentId);
1042
			parentTaxonFkStmt.setInt(2, childId);
1043
			parentTaxonFkStmt.executeUpdate();
1044
			return true;
1045
		} catch (SQLException e) {
1046
			logger.warn("ParentTaxonFk (" + (parentId ==null? "-":parentId) + ") could not be inserted into database "
1047
			        + "for taxon "+ (childId == null? "-" :childId) + ": " + e.getMessage());
1048
			e.printStackTrace();
1049
			return false;
1050
		}
1051
	}
1052

    
1053

    
1054
	/**
1055
	 * Inserts Rank data and KingdomFk into the Taxon database table.
1056
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1057
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1058
	 * @param taxonFk The TaxonFk to store the values for.
1059
	 * @param state
1060
	 * @param kindomFk The KingdomFk.
1061
	 * @return Whether save was successful or not.
1062
	 */
1063
	private boolean invokeRankDataAndKingdomFk(TaxonName taxonName,
1064
	        Integer taxonFk, Integer kingdomFk, PesiExportState state) {
1065

    
1066
	    try {
1067
			Integer rankFk = getRankFk(taxonName, kingdomFk);
1068
			if (rankFk != null) {
1069
				rankUpdateStmt.setInt(1, rankFk);
1070
			} else {
1071
				rankUpdateStmt.setObject(1, null);
1072
			}
1073

    
1074
			String rankCache = getRankCache(taxonName, kingdomFk, state);
1075
			if (rankCache != null) {
1076
				rankUpdateStmt.setString(2, rankCache);
1077
			} else {
1078
				rankUpdateStmt.setObject(2, null);
1079
			}
1080

    
1081
			if (kingdomFk != null) {
1082

    
1083
				rankUpdateStmt.setInt(3, kingdomFk);
1084
			} else {
1085
				rankUpdateStmt.setObject(3, null);
1086
			}
1087

    
1088
			if (taxonFk != null) {
1089
				rankUpdateStmt.setInt(4, taxonFk);
1090
			} else {
1091
				rankUpdateStmt.setObject(4, null);
1092
			}
1093

    
1094
			rankUpdateStmt.executeUpdate();
1095
			return true;
1096
		} catch (SQLException e) {
1097
			logger.error("Data (RankFk, RankCache, KingdomFk) could not be inserted into database: " + e.getMessage());
1098
			e.printStackTrace();
1099
			return false;
1100
		}
1101
	}
1102

    
1103
	/**
1104
	 * Inserts Rank data, TypeNameFk, KingdomFk, expertFk and speciesExpertFk into the Taxon database table.
1105
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1106
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1107
	 * @param taxonFk The TaxonFk to store the values for.
1108
	 * @param typeNameFk The TypeNameFk.
1109
	 * @param rankFk
1110
	 * @param state
1111
	 * @param kindomFk The KingdomFk.
1112
	 * @param expertFk The ExpertFk.
1113
	 * @param speciesExpertFk The SpeciesExpertFk.
1114
	 * @return Whether save was successful or not.
1115
	 */
1116
	private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonName taxonName,
1117
			Integer taxonFk, Integer typeNameFk, Integer kingdomFk, Integer rankFk, PesiExportState state) {
1118

    
1119
	    try {
1120
			int index = 1;
1121
			if (rankFk != null) {
1122
				rankTypeExpertsUpdateStmt.setInt(index++, rankFk);
1123
			} else {
1124
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1125
			}
1126

    
1127
			String rankCache = getRankCache(taxonName, kingdomFk, state);
1128
			if (rankCache != null) {
1129
				rankTypeExpertsUpdateStmt.setString(index++, rankCache);
1130
			} else {
1131
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1132
			}
1133

    
1134
			if (typeNameFk != null) {
1135
				rankTypeExpertsUpdateStmt.setInt(index++, typeNameFk);
1136
			} else {
1137
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1138
			}
1139

    
1140
			if (kingdomFk != null) {
1141
				rankTypeExpertsUpdateStmt.setInt(index++, kingdomFk);
1142
			} else {
1143
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1144
			}
1145

    
1146
//			if (expertFk != null) {
1147
//				rankTypeExpertsUpdateStmt.setInt(5, expertFk);
1148
//			} else {
1149
//				rankTypeExpertsUpdateStmt.setObject(5, null);
1150
//			}
1151
//
1152
//			//TODO handle experts GUIDS
1153
//			if (speciesExpertFk != null) {
1154
//				rankTypeExpertsUpdateStmt.setInt(6, speciesExpertFk);
1155
//			} else {
1156
//				rankTypeExpertsUpdateStmt.setObject(6, null);
1157
//			}
1158
//
1159
			if (taxonFk != null) {
1160
				rankTypeExpertsUpdateStmt.setInt(index++, taxonFk);
1161
			} else {
1162
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1163
			}
1164

    
1165
			rankTypeExpertsUpdateStmt.executeUpdate();
1166
			return true;
1167
		} catch (SQLException e) {
1168
		    String name = taxonName == null? null:taxonName.getTitleCache();
1169
			logger.error("Data could not be inserted into database: " + e.getMessage() + "; rankFk = " + rankFk + "; kingdomFk = " + kingdomFk  + "; taxonFk = "+ taxonFk  + "; typeNameFk = "  + typeNameFk + "; name = " + name);
1170
			e.printStackTrace();
1171
			return false;
1172
		} catch (Exception e) {
1173
		    String name = taxonName == null? null:taxonName.getTitleCache();
1174
            logger.error("Some exception occurred: " + e.getMessage() + "; rankFk = " + rankFk + "; kingdomFk = " + kingdomFk  + "; taxonFk = "+ taxonFk + "; typeNameFk = " + typeNameFk + "; name = " + name);
1175
			e.printStackTrace();
1176
			return false;
1177
		}
1178
	}
1179

    
1180
	/**
1181
	 * Deletes all entries of database tables related to <code>Taxon</code>.
1182
	 * @param state The {@link PesiExportState PesiExportState}.
1183
	 * @return Whether the delete operation was successful or not.
1184
	 */
1185
	protected boolean doDelete(PesiExportState state) {
1186

    
1187
		Source destination =  state.getConfig().getDestination();
1188

    
1189
		String[] tables = new String[]{"AdditionalTaxonSource","CommonNameSource","CommonName",
1190
		        "Image","NoteSource","Note","OccurrenceSource","Occurrence","RelTaxon","Taxon"};
1191

    
1192
		for(String table : tables){
1193
		    String sql = "DELETE FROM " + table;
1194
		    destination.update(sql);
1195
		}
1196

    
1197
		return true;
1198
	}
1199

    
1200
	private static Integer getRankFk(TaxonName taxonName, NomenclaturalCode nomenclaturalCode) {
1201
	    Integer kingdomId = PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
1202
	    return getRankFk(taxonName, kingdomId);
1203
	}
1204

    
1205
	/**
1206
	 * Returns the <code>RankFk</code> attribute.
1207
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1208
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1209
	 * @return The <code>RankFk</code> attribute.
1210
	 * @see MethodMapper
1211
	 */
1212
	private static Integer getRankFk(TaxonName taxonName, Integer kingdomId) {
1213
		Integer result = null;
1214
		try {
1215
			if (taxonName != null) {
1216
				if (taxonName.getRank() == null) {
1217
					logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1218
				} else {
1219
					result = PesiTransformer.rank2RankId(taxonName.getRank(), kingdomId);
1220
				}
1221
				if (result == null) {
1222
					logger.warn("Rank could not be determined for PESI-Kingdom-Id " + kingdomId + " and TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1223
				}
1224
			}
1225
		} catch (Exception e) {
1226
			e.printStackTrace();
1227
		}
1228
		return result;
1229
	}
1230

    
1231
	@SuppressWarnings("unused")
1232
    private static String getRankCache(TaxonName taxonName, PesiExportState state) {
1233
	    List<TaxonNode> nodes = getTaxonNodes(taxonName);
1234
	    Integer kingdomId;
1235
	    if (nodes == null||nodes.isEmpty()){
1236
	        kingdomId = getKingdomFk(taxonName);
1237
	    }else{
1238
	        //should not happen, method exists only pure names
1239
	        kingdomId = findKingdomIdFromTreeIndex(nodes.iterator().next().getTaxon(), state);
1240
	    }
1241
        return getRankCache(taxonName, kingdomId, state);
1242
	}
1243

    
1244
	private static String getRankCache(TaxonName taxonName, Integer kingdomFk, PesiExportState state) {
1245
	    if (Rank.DOMAIN().equals(taxonName.getRank())){
1246
            return state.getTransformer().getCacheByRankAndKingdom(Rank.DOMAIN(), null);
1247
        }else if (kingdomFk != null) {
1248
            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), kingdomFk);
1249
        }else if (taxonName.getNameType() != null){
1250
            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), PesiTransformer.nomenclaturalCode2Kingdom(taxonName.getNameType()));
1251
        }else{
1252
			logger.warn("No kingdom ID could be defined for name " + taxonName.getUuid());
1253
			return null;
1254
		}
1255
	}
1256

    
1257
    private static List<TaxonNode> getTaxonNodes(TaxonName taxonName) {
1258
        List<TaxonNode> result = new ArrayList<>();
1259
        for (TaxonBase<?> tb:taxonName.getTaxonBases()){
1260
            Taxon taxon;
1261
            //TODO handle ERMS taxon relationships
1262
            if (tb.isInstanceOf(Taxon.class)){
1263
                taxon = CdmBase.deproxy(tb, Taxon.class);
1264
            }else{
1265
                taxon = CdmBase.deproxy(tb, Synonym.class).getAcceptedTaxon();
1266
            }
1267
            if (isPesiTaxon(taxon)){
1268
                for (TaxonNode node : taxon.getTaxonNodes()){
1269
                    result.add(node);
1270
                }
1271
            }
1272
        }
1273
        return result;
1274
    }
1275

    
1276
//    @SuppressWarnings("unused")  //used by pure name mapper and by getRankFk
1277
    private static Integer getKingdomFk(TaxonName taxonName){
1278
        EnumSet<PesiSource> origin = getSources(taxonName);
1279
        if (origin.size() == 1 && origin.contains(PesiSource.EM)){
1280
            //maybe simply replace by
1281
            //return PesiTransformer.KINGDOM_PLANTAE;
1282
            return PesiTransformer.nomenclaturalCode2Kingdom(taxonName.getNameType());
1283
        }else{
1284
            logger.warn("getKingdomFk not yet implemented for non-EuroMed pure names");
1285
            return null;
1286
        }
1287
    }
1288

    
1289
    /**
1290
     * Returns the rankFk for the taxon name based on the names nomenclatural code.
1291
     * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1292
     */
1293
    @SuppressWarnings("unused")  //used by pure name mapper
1294
    private static Integer getRankFk(TaxonName taxonName) {
1295
        EnumSet<PesiSource> origin = getSources(taxonName);
1296
        if (origin.size() == 1 && origin.contains(PesiSource.EM)){
1297
            return getRankFk(taxonName, getKingdomFk(taxonName));
1298
        }else{
1299
            logger.warn("getRankFk not yet implemented for non-EuroMed pure names");
1300
            return null;
1301
        }
1302
    }
1303

    
1304
    /**
1305
	 * Returns the <code>DisplayName</code> attribute.
1306
	 * @param taxon The {@link TaxonBase Taxon}.
1307
	 * @return The <code>DisplayName</code> attribute.
1308
	 * @see MethodMapper
1309
	 */
1310
	@SuppressWarnings("unused")  //used by Mapper
1311
	private static String getDisplayName(TaxonBase<?> taxon) {
1312
		TaxonName taxonName = taxon.getName();
1313
		String result = getDisplayName(taxonName);
1314
		if (isMisappliedName(taxon)){
1315
			result = result + " " + getAuthorString(taxon);
1316
		}
1317
		return result;
1318
	}
1319

    
1320
	/**
1321
	 * Returns the <code>AuthorString</code> attribute.
1322
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1323
	 * @return The <code>AuthorString</code> attribute.
1324
	 * @see MethodMapper
1325
	 */
1326
	//used by mapper
1327
	protected static String getAuthorString(TaxonBase<?> taxon) {
1328
		try {
1329
			String result = null;
1330
			boolean isNonViralName = false;
1331
			String authorshipCache = null;
1332
			TaxonName taxonName = taxon.getName();
1333
			if (taxonName != null && taxonName.isNonViral()){
1334
				authorshipCache = taxonName.getAuthorshipCache();
1335
				isNonViralName = true;
1336
			}
1337
			result = authorshipCache;
1338

    
1339
			// For a misapplied names there are special rules
1340
			if (isMisappliedName(taxon)){
1341
				if (taxon.getSec() != null){
1342
					String secTitle = taxon.getSec().getTitleCache();
1343
					if (! secTitle.startsWith("auct")){
1344
						secTitle = "sensu " + secTitle;
1345
					}else if (secTitle.equals("auct")){  //may be removed once the title cache is generated correctly for references with title auct. #
1346
						secTitle = "auct.";
1347
					}
1348
					return secTitle;
1349
				}else if (StringUtils.isBlank(authorshipCache)) {
1350
					// Set authorshipCache to "auct."
1351
					result = PesiTransformer.AUCT_STRING;
1352
				}else{
1353
					result = PesiTransformer.AUCT_STRING;
1354
//					result = authorshipCache;
1355
				}
1356
			}
1357

    
1358
			if (taxonName == null){
1359
				logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1360
			}else if (! isNonViralName){
1361
				logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1362
			}
1363

    
1364
			if (StringUtils.isBlank(result)) {
1365
				return null;
1366
			} else {
1367
				return result;
1368
			}
1369
		} catch (Exception e) {
1370
			e.printStackTrace();
1371
			return null;
1372
		}
1373
	}
1374

    
1375
	/**
1376
	 * Returns the <code>DisplayName</code> attribute.
1377
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1378
	 * @return The <code>DisplayName</code> attribute.
1379
	 * @see MethodMapper
1380
	 */
1381
	 //used by Mapper
1382
	private static String getDisplayName(TaxonName taxonName) {
1383
		// TODO: extension?
1384
		if (taxonName == null) {
1385
			return null;
1386
		}else{
1387
		    taxonName = CdmBase.deproxy(taxonName);
1388
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1389
			HTMLTagRules tagRules = new HTMLTagRules().
1390
					addRule(TagEnum.name, "i").
1391
					addRule(TagEnum.nomStatus, "@status@");
1392

    
1393
			String result;
1394
			if (getSources(taxonName).contains(PesiSource.ERMS)){
1395
			    result = cacheStrategy.getTitleCache(taxonName, tagRules);  //according to SQL script (also in ERMS sources are not abbreviated)
1396
			}else if (getSources(taxonName).contains(PesiSource.EM)){
1397
			    result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
1398
			}else{
1399
			    //TODO define for FE + IF and for multiple sources
1400
			    result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
1401
			}
1402
			return result.replaceAll(",?\\<@status@\\>.*\\</@status@\\>", "");
1403
		}
1404
	}
1405

    
1406
	@SuppressWarnings("unused")
1407
	private static String getGUID(TaxonName taxonName) {
1408
		UUID uuid = taxonName.getUuid();
1409
		String result = "NameUUID:" + uuid.toString();
1410
		return result;
1411
	}
1412

    
1413
	/**
1414
	 * Returns the <code>WebShowName</code> attribute for a taxon.
1415
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1416
	 * @return The <code>WebShowName</code> attribute.
1417
	 * @see MethodMapper
1418
	*/
1419
	@SuppressWarnings("unused")
1420
	private static String getWebShowName(TaxonBase<?> taxon) {
1421
		TaxonName taxonName = taxon.getName();
1422
		String result = getWebShowName(taxonName);
1423
		if (isMisappliedName(taxon)){
1424
			result = result + " " + getAuthorString(taxon);
1425
		}
1426
		return result;
1427
	}
1428

    
1429
	/**
1430
	 * Returns the <code>WebShowName</code> attribute.
1431
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1432
	 * @return The <code>WebShowName</code> attribute.
1433
	 * @see MethodMapper
1434
	 */
1435
	private static String getWebShowName(TaxonName taxonName) {
1436
		//TODO extensions?
1437
		if (taxonName == null) {
1438
			return null;
1439
		}else{
1440
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1441

    
1442
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1443
			String result = cacheStrategy.getTitleCache(taxonName, tagRules);
1444
			return result;
1445
		}
1446
	}
1447

    
1448
	/**
1449
	 * Returns the <code>WebSearchName</code> attribute.
1450
	 * @param taxonName The {@link NonViralName NonViralName}.
1451
	 * @return The <code>WebSearchName</code> attribute.
1452
	 * @see MethodMapper
1453
	 */
1454
	@SuppressWarnings("unused")
1455
	private static String getWebSearchName(TaxonName taxonName) {
1456
		//TODO extensions?
1457
	    TaxonNameDefaultCacheStrategy strategy = getCacheStrategy(taxonName);
1458
		String result = strategy.getNameCache(taxonName);
1459
		return result;
1460
	}
1461

    
1462
	/**
1463
	 * Returns the <code>FullName</code> attribute.
1464
	 * @param taxonName The {@link NonViralName NonViralName}.
1465
	 * @return The <code>FullName</code> attribute.
1466
	 * @see MethodMapper
1467
	 */
1468
	@SuppressWarnings("unused")
1469
	private static String getFullName(TaxonName taxonName) {
1470
		//TODO extensions?
1471
		String result = getCacheStrategy(taxonName).getTitleCache(taxonName);
1472
		Iterator<Taxon> taxa = taxonName.getTaxa().iterator();
1473
		if (taxonName.getTaxa().size() >0){
1474
			if (taxonName.getTaxa().size() == 1){
1475
				Taxon taxon = taxa.next();
1476
				if (isMisappliedName(taxon)){
1477
					result = result + " " + getAuthorString(taxon);
1478
				}
1479
				taxon = null;
1480
			}
1481
		}
1482
		return result;
1483
	}
1484

    
1485
	/**
1486
	 * Returns the SourceNameCache for the AdditionalSource table
1487
	 * @param taxonName
1488
	 * @return
1489
	 */
1490
	static boolean isFirstAbbrevTitle = true;
1491
	@SuppressWarnings("unused")
1492
	private static String getSourceNameCache(TaxonName taxonName) {
1493
		if (taxonName != null){
1494
			Reference nomRef = taxonName.getNomenclaturalReference();
1495
			if (nomRef != null ){
1496
			    if (isFirstAbbrevTitle){
1497
			        //#5388 is definetely not the correct ticket number
1498
			        logger.warn("Semantics of getAbbrevTitleCache has changed. Please check if output is still correct. See #5388");
1499
			        isFirstAbbrevTitle = false;
1500
			    }
1501
			    return nomRef.getAbbrevTitleCache();
1502
			}
1503
		}
1504
		return null;
1505
	}
1506

    
1507
	/**
1508
	 * Returns the nomenclatural reference which is the reference
1509
	 * including the detail (microreference).
1510
	 * @param taxonName The {@link TaxonName taxon name}.
1511
	 * @see MethodMapper
1512
	 */
1513
	@SuppressWarnings("unused")
1514
	private static String getNomRefString(TaxonName taxonName) {
1515
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1516
		if (ref == null){
1517
			return null;
1518
		}
1519
		String result = null;
1520
		EnumSet<PesiSource> sources = getSources(taxonName);
1521
		if(sources.contains(PesiSource.EM)){
1522
		    if (! ref.isProtectedAbbrevTitleCache()){
1523
		        ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1524
		    }
1525
		    result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1526
		}else if(sources.contains(PesiSource.FE)||sources.contains(PesiSource.IF) ){
1527
            //TODO still need to check if correct for FE + IF
1528
		    if (! ref.isProtectedAbbrevTitleCache()){
1529
                ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1530
            }
1531
            result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1532
            return result;   // according to SQL script
1533
		}else if(sources.contains(PesiSource.ERMS)) {
1534
            //result = null; //according to SQL script
1535
		}else{
1536
		    logger.warn("Source not yet supported");
1537
		}
1538
		return result;
1539
	}
1540

    
1541
	/**
1542
	 * Returns the <code>NameStatusFk</code> attribute.
1543
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1544
	 * @return The <code>NameStatusFk</code> attribute.
1545
	 * @see MethodMapper
1546
	 */
1547
	@SuppressWarnings("unused")
1548
	private static Integer getNameStatusFk(TaxonName taxonName) {
1549
		Integer result = null;
1550

    
1551
		NomenclaturalStatus status = getNameStatus(taxonName);
1552
		if (status != null) {
1553
			result = PesiTransformer.nomStatus2nomStatusFk(status.getType());
1554
		}
1555
		return result;
1556
	}
1557

    
1558
	/**
1559
	 * Returns the <code>NameStatusCache</code> attribute.
1560
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1561
	 * @return The <code>NameStatusCache</code> attribute.
1562
	 * @throws UndefinedTransformerMethodException
1563
	 * @see MethodMapper
1564
	 */
1565
	@SuppressWarnings("unused")
1566
	private static String getNameStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1567
		String result = null;
1568
		NomenclaturalStatus status = getNameStatus(taxonName);
1569
		if (status != null) {
1570
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1571
		}
1572
		return result;
1573
	}
1574

    
1575
	private static NomenclaturalStatus getNameStatus(TaxonName taxonName) {
1576
		try {
1577
			if (taxonName != null) {
1578
			    Set<NomenclaturalStatus> states = taxonName.getStatus();
1579
				if (states.size() == 1) {
1580
					NomenclaturalStatus status = states.iterator().next();
1581
					return status;
1582
				} else if (states.size() > 1) {
1583
					logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1584
				}
1585
			}
1586
		} catch (Exception e) {
1587
			e.printStackTrace();
1588
		}
1589
		return null;
1590
	}
1591
	/**
1592
	 * Returns the <code>TaxonStatusFk</code> attribute.
1593
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1594
	 * @param state The {@link PesiExportState PesiExportState}.
1595
	 * @return The <code>TaxonStatusFk</code> attribute.
1596
	 * @see MethodMapper
1597
	 */
1598
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1599
		Integer result = null;
1600

    
1601
		try {
1602
			if (isMisappliedName(taxon)) {
1603
				Synonym synonym = Synonym.NewInstance(null, null);
1604

    
1605
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1606
				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1607
			} else {
1608
				result = PesiTransformer.taxonBase2statusFk(taxon);
1609
			}
1610
		} catch (Exception e) {
1611
			e.printStackTrace();
1612
		}
1613
		return result;
1614
	}
1615

    
1616
	/**
1617
	 * Returns the <code>TaxonStatusCache</code> attribute.
1618
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1619
	 * @param state The {@link PesiExportState PesiExportState}.
1620
	 * @return The <code>TaxonStatusCache</code> attribute.
1621
	 * @throws UndefinedTransformerMethodException
1622
	 * @see MethodMapper
1623
	 */
1624
	@SuppressWarnings("unused")
1625
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1626
		return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1627
	}
1628

    
1629
    /**
1630
     * Returns the <code>TaxonFk1</code> attribute. It corresponds to a CDM <code>TaxonRelationship</code>.
1631
     * @param relationship The {@link RelationshipBase Relationship}.
1632
     * @param state The {@link PesiExportState PesiExportState}.
1633
     * @return The <code>TaxonFk1</code> attribute.
1634
     * @see MethodMapper
1635
     */
1636
    @SuppressWarnings("unused")
1637
    private static Integer getSynonym(Synonym synonym, PesiExportState state) {
1638
         return state.getDbId(synonym);
1639
    }
1640

    
1641
	/**
1642
	 * Returns the <code>TypeNameFk</code> attribute.
1643
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1644
	 * @param state The {@link PesiExportState PesiExportState}.
1645
	 * @return The <code>TypeNameFk</code> attribute.
1646
	 * @see MethodMapper
1647
	 */
1648
	private static Integer getTypeNameFk(TaxonName taxonName, PesiExportState state) {
1649
		Integer result = null;
1650
		if (taxonName != null) {
1651
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1652
			if (nameTypeDesignations.size() == 1) {
1653
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1654
				if (nameTypeDesignation != null) {
1655
					TaxonName typeName = nameTypeDesignation.getTypeName();
1656
					if (typeName != null) {
1657
					    if (typeName.getTaxonBases().isEmpty()){
1658
					        logger.warn("type name does not belong to a taxon and therefore is expected to not be a European taxon. Type name not added. Type name: " + typeName.getTitleCache() + ", typified name: " + taxonName.getTitleCache());
1659
					    }else{
1660
					        result = state.getDbId(typeName);
1661
					    }
1662
					}
1663
				}
1664
			} else if (nameTypeDesignations.size() > 1) {
1665
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1666
			}
1667
		}
1668
		return result;
1669
	}
1670

    
1671
	/**
1672
	 * Returns the <code>TypeFullnameCache</code> attribute.
1673
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1674
	 * @return The <code>TypeFullnameCache</code> attribute.
1675
	 * @see MethodMapper
1676
	 */
1677
	@SuppressWarnings("unused")
1678
	private static String getTypeFullnameCache(TaxonName taxonName) {
1679
		String result = null;
1680

    
1681
		try {
1682
    		if (taxonName != null) {
1683
    			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1684
    			if (nameTypeDesignations.size() == 1) {
1685
    				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1686
    				if (nameTypeDesignation != null) {
1687
    					TaxonName typeName = nameTypeDesignation.getTypeName();
1688
    					if (typeName != null) {
1689
    						result = typeName.getTitleCache();
1690
    					}
1691
    				}
1692
    			} else if (nameTypeDesignations.size() > 1) {
1693
    				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1694
    			}
1695
    		}
1696
		} catch (Exception e) {
1697
			e.printStackTrace();
1698
		}
1699
		return result;
1700
	}
1701

    
1702

    
1703
	/**
1704
	 * Returns the <code>QualityStatusFk</code> attribute.
1705
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1706
	 * @return The <code>QualityStatusFk</code> attribute.
1707
	 * @see MethodMapper
1708
	 */
1709
	private static Integer getQualityStatusFk(TaxonName taxonName) {
1710
	    EnumSet<PesiSource> sources = getSources(taxonName);
1711
		return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1712
	}
1713

    
1714
	/**
1715
	 * Returns the <code>QualityStatusCache</code> attribute.
1716
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1717
	 * @return The <code>QualityStatusCache</code> attribute.
1718
	 * @throws UndefinedTransformerMethodException
1719
	 * @see MethodMapper
1720
	 */
1721
	@SuppressWarnings("unused")
1722
	private static String getQualityStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1723
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1724
	}
1725

    
1726

    
1727
	/**
1728
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1729
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1730
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1731
	 * @see MethodMapper
1732
	 */
1733
	//TODO seems not to be used
1734
	private static Integer getTypeDesignationStatusFk(TaxonName taxonName) {
1735
		Integer result = null;
1736

    
1737
		try {
1738
    		if (taxonName != null) {
1739
    			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1740
    			if (typeDesignations.size() == 1) {
1741
    				Object obj = typeDesignations.iterator().next().getTypeStatus();
1742
    				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1743
    				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1744
    			} else if (typeDesignations.size() > 1) {
1745
    				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1746
    			}
1747
    		}
1748
		} catch (Exception e) {
1749
			e.printStackTrace();
1750
		}
1751
		return result;
1752
	}
1753

    
1754
	/**
1755
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1756
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1757
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1758
	 * @see MethodMapper
1759
	 */
1760
	//TODO seems not to be used
1761
	private static String getTypeDesignationStatusCache(TaxonName taxonName) {
1762
		String result = null;
1763

    
1764
		try {
1765
    		if (taxonName != null) {
1766
    			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1767
    			if (typeDesignations.size() == 1) {
1768
    				Object obj = typeDesignations.iterator().next().getTypeStatus();
1769
    				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1770
    				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
1771
    			} else if (typeDesignations.size() > 1) {
1772
    				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1773
    			}
1774
    		}
1775
		} catch (Exception e) {
1776
			e.printStackTrace();
1777
		}
1778
		return result;
1779
	}
1780

    
1781
	/**
1782
	 * Returns the <code>FossilStatusFk</code> attribute.
1783
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1784
	 * @return The <code>FossilStatusFk</code> attribute.
1785
	 * @see MethodMapper
1786
	 */
1787
	@SuppressWarnings("unused")
1788
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
1789
		Integer result = null;
1790

    
1791
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
1792
		if (fossilStatuus.size() == 0){
1793
			return null;
1794
		}else if (fossilStatuus.size() > 1){
1795
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
1796
		}
1797
		String fossilStatus = fossilStatuus.iterator().next();
1798

    
1799
		int statusFk = state.getTransformer().fossilStatusCache2FossilStatusFk(fossilStatus);
1800
		return statusFk;
1801
	}
1802

    
1803
	/**
1804
	 * Returns the <code>FossilStatusCache</code> attribute.
1805
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1806
	 * @return The <code>FossilStatusCache</code> attribute.
1807
	 * @see MethodMapper
1808
	 */
1809
	@SuppressWarnings("unused")
1810
	private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
1811
		String result = null;
1812
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
1813
		if (fossilStatuus.size() == 0){
1814
			return null;
1815
		}
1816
		for (String strFossilStatus : fossilStatuus){
1817
			result = CdmUtils.concat(";", result, strFossilStatus);
1818
		}
1819
		return result;
1820
	}
1821

    
1822
	/**
1823
	 * Returns the <code>IdInSource</code> attribute.
1824
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1825
	 * @return The <code>IdInSource</code> attribute.
1826
	 * @see MethodMapper
1827
	 */
1828
	@SuppressWarnings("unused")
1829
	private static String getIdInSource(IdentifiableEntity<?> taxonName) {
1830
		String result = null;
1831

    
1832
		try {
1833
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
1834
			if (sources.size() > 1){
1835
				logger.warn("There is > 1 Pesi source. This is not yet handled.");
1836
			}
1837
			if (sources.size() == 0){
1838
				logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1839
			}
1840
			for (IdentifiableSource source : sources) {
1841
				Reference ref = source.getCitation();
1842
				UUID refUuid = ref.getUuid();
1843
				String idInSource = source.getIdInSource();
1844
				if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed)){
1845
					result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
1846
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
1847
					result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
1848
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
1849
					result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
1850
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
1851
					result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
1852
				}else{
1853
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");}
1854
				}
1855

    
1856
				String sourceIdNameSpace = source.getIdNamespace();
1857
				if (sourceIdNameSpace != null) {
1858
					if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
1859
						result =  idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
1860
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
1861
						result =  idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
1862
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
1863
						result =  idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
1864
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
1865
						result =  idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
1866
					}
1867
				}
1868
				if (result == null) {
1869
					logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
1870
				}
1871
			}
1872
		} catch (Exception e) {
1873
			e.printStackTrace();
1874
			logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
1875
		}
1876

    
1877
		if (result == null) {
1878
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1879
		}
1880
		return result;
1881
	}
1882

    
1883
	/**
1884
	 * Returns the idInSource for a given TaxonName only.
1885
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1886
	 * @return The idInSource.
1887
	 */
1888
	private static String getIdInSourceOnly(IdentifiableEntity<?> identEntity) {
1889
		String result = null;
1890

    
1891
		// Get the sources first
1892
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
1893

    
1894
		// Determine the idInSource
1895
		if (sources.size() == 1) {
1896
			IdentifiableSource source = sources.iterator().next();
1897
			if (source != null) {
1898
				result = source.getIdInSource();
1899
			}
1900
		} else if (sources.size() > 1) {
1901
			int count = 1;
1902
			result = "";
1903
			for (IdentifiableSource source : sources) {
1904
				result += source.getIdInSource();
1905
				if (count < sources.size()) {
1906
					result += "; ";
1907
				}
1908
				count++;
1909
			}
1910

    
1911
		}
1912

    
1913
		return result;
1914
	}
1915

    
1916
	/**
1917
	 * Returns the <code>GUID</code> attribute.
1918
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1919
	 * @return The <code>GUID</code> attribute.
1920
	 * @see MethodMapper
1921
	 */
1922
	private static String getGUID(TaxonBase<?> taxon) {
1923
		if (taxon.getLsid() != null ){
1924
			return taxon.getLsid().getLsid();
1925
		}else if (taxon.hasMarker(PesiTransformer.uuidMarkerGuidIsMissing, true)){
1926
			return null;
1927
		}else{
1928
			return taxon.getUuid().toString();
1929
		}
1930
	}
1931

    
1932
	/**
1933
	 * Returns the <code>DerivedFromGuid</code> attribute.
1934
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1935
	 * @return The <code>DerivedFromGuid</code> attribute.
1936
	 * @see MethodMapper
1937
	 */
1938
	@SuppressWarnings("unused")
1939
	private static String getDerivedFromGuid(TaxonBase<?> taxon) {
1940
		String result = null;
1941
		try {
1942
		// The same as GUID for now
1943
		result = getGUID(taxon);
1944
		} catch (Exception e) {
1945
			e.printStackTrace();
1946
		}
1947
		return result;
1948
	}
1949

    
1950
	/**
1951
	 * Returns the <code>CacheCitation</code> attribute.
1952
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1953
	 * @return The CacheCitation.
1954
	 * @see MethodMapper
1955
	 */
1956
	@SuppressWarnings("unused")
1957
	private static String getCacheCitation(TaxonBase<?> taxon) {
1958
		// !!! See also doPhaseUpdates
1959

    
1960
		TaxonName taxonName = taxon.getName();
1961
		String result = "";
1962
		//TODO implement anew for taxa
1963
		try {
1964
			EnumSet<PesiSource> sources = getSources(taxon);
1965
			if (sources.isEmpty()) {
1966
//				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1967
			} else if (sources.contains(PesiSource.ERMS)) {
1968
				Set<Extension> extensions = taxon.getExtensions();
1969
				for (Extension extension : extensions) {
1970
					if (extension.getType().equals(cacheCitationExtensionType)) {
1971
						result = extension.getValue();
1972
					}
1973
				}
1974
			} else {
1975
				String expertName = getExpertName(taxon);
1976
				String webShowName = getWebShowName(taxonName);
1977

    
1978
				// idInSource only
1979
				String idInSource = getIdInSourceOnly(taxonName);
1980

    
1981
				// build the cacheCitation
1982
				if (expertName != null) {
1983
					result += expertName + ". ";
1984
				} else {
1985
					if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
1986
				}
1987
				if (webShowName != null) {
1988
					result += webShowName + ". ";
1989
				} else {
1990
					logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1991
				}
1992

    
1993
				if (getOriginalDB(taxonName).equals("FaEu")) {
1994
					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
1995
				} else if (getOriginalDB(taxonName).equals("EM")) {
1996
					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
1997
				}
1998

    
1999
				if (idInSource != null) {
2000
					result += idInSource;
2001
				} else {
2002
					logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2003
				}
2004
			}
2005
		} catch (Exception e) {
2006
			e.printStackTrace();
2007
		}
2008

    
2009
		if (StringUtils.isBlank(result)) {
2010
			return null;
2011
		} else {
2012
			return result;
2013
		}
2014
	}
2015

    
2016
	/**
2017
	 * Returns the <code>OriginalDB</code> attribute.
2018
	 * @param identifiableEntity
2019
	 * @return The <code>OriginalDB</code> attribute.
2020
	 * @see MethodMapper
2021
	 */
2022
	@SuppressWarnings("unused")
2023
	private static String getOriginalDB(IdentifiableEntity<?> identifiableEntity) {
2024
		EnumSet<PesiSource> sources  = getSources(identifiableEntity);
2025
		return PesiTransformer.getOriginalDbBySources(sources);
2026
	}
2027

    
2028
	/**
2029
	 * Returns the <code>LastAction</code> attribute.
2030
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2031
	 * @return The <code>LastAction</code> attribute.
2032
	 * @see MethodMapper
2033
	 */
2034
	//TODO still in use?
2035
	private static String getLastAction(IdentifiableEntity<?> identEntity) {
2036
		String result = null;
2037
		try {
2038
    		Set<Extension> extensions = identEntity.getExtensions();
2039
    		for (Extension extension : extensions) {
2040
    			if (extension.getType().equals(lastActionExtensionType)) {
2041
    				result = extension.getValue();
2042
    			}
2043
    		}
2044
		} catch (Exception e) {
2045
			e.printStackTrace();
2046
		}
2047
		return result;
2048
	}
2049

    
2050
	/**
2051
	 * Returns the <code>LastActionDate</code> attribute.
2052
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2053
	 * @return The <code>LastActionDate</code> attribute.
2054
	 * @see MethodMapper
2055
	 */
2056
	//TODO still in use?
2057
	private static DateTime getLastActionDate(IdentifiableEntity<?> identEntity) {
2058
		DateTime result = null;
2059
		try {
2060
			Set<Extension> extensions = identEntity.getExtensions();
2061
			for (Extension extension : extensions) {
2062
				if (extension.getType().equals(lastActionDateExtensionType)) {
2063
					String dateTime = extension.getValue();
2064
					if (dateTime != null) {
2065
						DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
2066
						result = formatter.parseDateTime(dateTime);
2067
					}
2068
				}
2069
			}
2070
		} catch (Exception e) {
2071
			e.printStackTrace();
2072
		}
2073
		return result;
2074
	}
2075

    
2076
	/**
2077
	 * Returns the <code>ExpertName</code> attribute.
2078
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2079
	 * @return The <code>ExpertName</code> attribute.
2080
	 * @see MethodMapper
2081
	 */
2082
	@SuppressWarnings("unused")  //for some reason it is also called by getCacheCitation
2083
	private static String getExpertName(TaxonBase<?> taxonName) {
2084
	    String result = null;
2085
		try {
2086
    		if(expertNameExtensionType!=null){  //some databases do not have this extension type
2087
    		    Set<Extension> extensions = taxonName.getExtensions();
2088
    		    for (Extension extension : extensions) {
2089
    		        if (extension.getType().equals(expertNameExtensionType)) {
2090
    		            result = extension.getValue();
2091
    		        }
2092
    		    }
2093
    		}
2094
		} catch (Exception e) {
2095
			e.printStackTrace();
2096
		}
2097
		return result;
2098
	}
2099

    
2100
	/**
2101
	 * Returns the <code>ExpertFk</code> attribute.
2102
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2103
	 * @param state The {@link PesiExportState PesiExportState}.
2104
	 * @return The <code>ExpertFk</code> attribute.
2105
	 * @see MethodMapper
2106
	 */
2107
	private static Integer getExpertFk(Reference reference, PesiExportState state) {
2108
		Integer result = state.getDbId(reference);
2109
		return result;
2110
	}
2111

    
2112
	/**
2113
	 * Returns the <code>SpeciesExpertName</code> attribute.
2114
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2115
	 * @return The <code>SpeciesExpertName</code> attribute.
2116
	 * @see MethodMapper
2117
	 */
2118
	//TODO still in use?
2119
	private static String getSpeciesExpertName(TaxonBase<?> taxonName) {
2120
		String result = null;
2121
		try {
2122
    		Set<Extension> extensions = taxonName.getExtensions();
2123
    		for (Extension extension : extensions) {
2124
    			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2125
    				result = extension.getValue();
2126
    			}
2127
    		}
2128
		} catch (Exception e) {
2129
			e.printStackTrace();
2130
		}
2131
		return result;
2132
	}
2133

    
2134
	/**
2135
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2136
	 * @param reference The {@link Reference Reference}.
2137
	 * @param state The {@link PesiExportState PesiExportState}.
2138
	 * @return The <code>SpeciesExpertFk</code> attribute.
2139
	 * @see MethodMapper
2140
	 */
2141
	private static Integer getSpeciesExpertFk(Reference reference, PesiExportState state) {
2142
		Integer result = state.getDbId(reference);
2143
		return result;
2144
	}
2145

    
2146
	protected static TaxonNameDefaultCacheStrategy getCacheStrategy(TaxonName taxonName) {
2147
		taxonName = CdmBase.deproxy(taxonName);
2148
		TaxonNameDefaultCacheStrategy cacheStrategy;
2149
		if (taxonName.isZoological()){
2150
			cacheStrategy = zooNameStrategy;
2151
		}else if (taxonName.isBotanical()) {
2152
			cacheStrategy = nonViralNameStrategy;
2153
		}else if (taxonName.isNonViral()) {
2154
			cacheStrategy = nonViralNameStrategy;
2155
		}else if (taxonName.isBacterial()) {
2156
			cacheStrategy = nonViralNameStrategy;
2157
		}else{
2158
			logger.error("Unhandled taxon name type. Can't define strategy class");
2159
			cacheStrategy = nonViralNameStrategy;
2160
		}
2161
		return cacheStrategy;
2162
	}
2163

    
2164
	/**
2165
	 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2166
	 * @param relationship The {@link RelationshipBase Relationship}.
2167
	 * @return The <code>RelTaxonQualifierFk</code> attribute.
2168
	 * @see MethodMapper
2169
	 */
2170
	@SuppressWarnings("unused")
2171
	private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2172
		return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2173
	}
2174

    
2175
    //TODO still in use?
2176
    private static String getSynonymTypeCache(Synonym synonym, PesiExportState state) {
2177
        String result = null;
2178
        NomenclaturalCode code = null;
2179
        code = CdmBase.deproxy(synonym, Synonym.class).getAcceptedTaxon().getName().getNameType();
2180

    
2181
        if (code != null) {
2182
            result = state.getConfig().getTransformer().getCacheBySynonymType(synonym, code);
2183
        } else {
2184
            logger.error("NomenclaturalCode is NULL while creating the following synonym: " + synonym.getUuid());
2185
        }
2186
        return result;
2187
    }
2188

    
2189
// ********************************** MAPPINGS ********************************/
2190

    
2191
	/**
2192
	 * Returns the CDM to PESI specific export mappings.
2193
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2194
	 */
2195
	private PesiExportMapping getMapping() {
2196
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2197

    
2198
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2199
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2200
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2201
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2202

    
2203
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2204

    
2205
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2206
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2207
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misapllied Names are handled differently
2208
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2209

    
2210
		// DisplayName
2211
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2212

    
2213
		// FossilStatus (Fk, Cache)
2214
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2215
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2216

    
2217
		//handled by name mapping
2218
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2219
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2220

    
2221
		//experts
2222
		ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtSpeciesExpertName);
2223
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2224
		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
2225
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2226

    
2227
		//ParentTaxonFk handled in Phase02 now
2228
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2229

    
2230
		addNameMappers(mapping);
2231

    
2232
		return mapping;
2233
	}
2234

    
2235
	/**
2236
	 * Returns the CDM to PESI specific export mappings.
2237
	 * @param state
2238
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2239
	 * @throws UndefinedTransformerMethodException
2240
	 */
2241
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2242
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2243

    
2244
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2245

    
2246
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonName.class));
2247
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonName.class));
2248
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonName.class, PesiExportState.class));
2249
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER, PesiTransformer.T_STATUS_UNACCEPTED));
2250
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR, state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2251
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
2252
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonName.class));
2253
		mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonName.class));
2254

    
2255
		// DisplayName
2256
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonName.class));
2257

    
2258
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2259
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2260

    
2261
		addNameMappers(mapping);
2262
		//TODO add author mapper
2263

    
2264
		return mapping;
2265
	}
2266

    
2267
	private void addNameMappers(PesiExportMapping mapping) {
2268

    
2269
	    //epithets
2270
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2271
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2272
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2273
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2274

    
2275
		//full name
2276
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2277
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonName.class));
2278
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonName.class));
2279

    
2280
		//nom ref
2281
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonName.class));
2282

    
2283
		//status
2284
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonName.class));
2285
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonName.class, PesiExportState.class));
2286
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonName.class));
2287
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonName.class, PesiExportState.class));
2288

    
2289
		//types
2290
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
2291
		//TypeNameFk handled in Phase3
2292

    
2293
		//supplemental
2294
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2295
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2296

    
2297
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2298

    
2299
	}
2300

    
2301
	private PesiExportMapping getSynRelMapping() {
2302
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2303
		logger.warn("SynRelMapping currently not implemented. Needs to be checked");
2304

    
2305
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getSynonym", Synonym.class, PesiExportState.class));
2306
		mapping.addMapper(DbObjectMapper.NewInstance("acceptedTaxon", "TaxonFk2"));
2307
		mapping.addMapper(DbObjectMapper.NewInstance("type", "RelTaxonQualifierFk"));
2308
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this.getClass(), "getSynonymTypeCache", Synonym.class, PesiExportState.class));
2309
		// TODO
2310
//		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2311

    
2312
		return mapping;
2313
	}
2314

    
2315
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state) {
2316
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2317

    
2318
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2319
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2320

    
2321
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2322
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2323
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonName.class));
2324

    
2325
		//we have only nomenclatural references here
2326
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2327
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2328

    
2329
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2330

    
2331
		return mapping;
2332
	}
2333

    
2334

    
2335
    @Override
2336
    protected boolean doCheck(PesiExportState state) {
2337
        return true;
2338
    }
2339

    
2340
    @Override
2341
    protected boolean isIgnore(PesiExportState state) {
2342
        return ! state.getConfig().isDoTaxa();
2343
    }
2344
}
(13-13/14)