Project

General

Profile

Download (92.5 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.List;
20
import java.util.Map;
21
import java.util.Set;
22
import java.util.UUID;
23
import java.util.regex.Matcher;
24
import java.util.regex.Pattern;
25

    
26
import org.apache.commons.lang.StringUtils;
27
import org.apache.log4j.Logger;
28
import org.springframework.stereotype.Component;
29
import org.springframework.transaction.TransactionStatus;
30

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

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

    
89
    private static final long serialVersionUID = -3412722058790200078L;
90
    private static final Logger logger = Logger.getLogger(PesiTaxonExport.class);
91

    
92
	private static final Class<? extends CdmBase> standardMethodParameter = TaxonBase.class;
93

    
94
	private static int modCount = 1000;
95
	private static final String dbTableName = "Taxon";
96
	private static final String dbTableNameSynRel = "RelTaxon";
97
	private static final String dbTableAdditionalSourceRel = "AdditionalTaxonSource";
98

    
99
	private static final String pluralString = "Taxa";
100
	private static final String parentPluralString = "Taxa";
101
	private static final String pluralStringNames = "Names";
102

    
103
//	private PreparedStatement parentTaxonFk_TreeIndex_KingdomFkStmts;
104
	private PreparedStatement parentTaxonFkStmt;
105
	private PreparedStatement rankTypeExpertsUpdateStmt;
106
	private PreparedStatement rankUpdateStmt;
107
	private Integer kingdomFk;
108

    
109
	private static ExtensionType lastActionExtensionType;
110
	private static ExtensionType lastActionDateExtensionType;
111
	private static ExtensionType expertNameExtensionType;
112
	private static ExtensionType speciesExpertNameExtensionType;
113
	private static ExtensionType cacheCitationExtensionType;
114

    
115
	public static TaxonNameDefaultCacheStrategy zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
116
	public static TaxonNameDefaultCacheStrategy nonViralNameStrategy = TaxonNameDefaultCacheStrategy.NewInstance();
117
	private static int currentTaxonId;
118

    
119
	enum NamePosition {
120
		beginning,
121
		end,
122
		between,
123
		alone,
124
		nowhere
125
	}
126

    
127
	public PesiTaxonExport() {
128
		super();
129
	}
130

    
131
	@Override
132
	public Class<? extends CdmBase> getStandardMethodParameter() {
133
		return standardMethodParameter;
134
	}
135

    
136
	@Override
137
	protected void doInvoke(PesiExportState state) {
138
		try {
139
			logger.info("*** Started Making " + pluralString + " ...");
140

    
141
			initPreparedStatements(state);
142

    
143
			// Stores whether this invoke was successful or not.
144
			boolean success = true;
145

    
146
			// PESI: Clear the database table Taxon.
147
			doDelete(state);
148

    
149
			// Get specific mappings: (CDM) Taxon -> (PESI) Taxon
150
			PesiExportMapping mapping = getMapping();
151
			PesiExportMapping synonymRelMapping = getSynRelMapping();
152
			PesiExportMapping additionalSourceMapping = getAdditionalSourceMapping(state);
153

    
154
			// Initialize the db mapper
155
			mapping.initialize(state);
156
			synonymRelMapping.initialize(state);
157
			additionalSourceMapping.initialize(state);
158

    
159
			// Find extensionTypes
160
			lastActionExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtLastAction);
161
			lastActionDateExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtLastActionDate);
162
			expertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
163
			speciesExpertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtSpeciesExpertName);
164
			cacheCitationExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtCacheCitation);
165

    
166
			//Export Taxa..
167
			success &= doPhase01(state, mapping, additionalSourceMapping);
168

    
169
			//"PHASE 1b: Handle names without taxa ...
170
			success &= doPhase01b_Names(state, additionalSourceMapping);
171

    
172
			// 2nd Round: Add ParentTaxonFk to each taxon
173
			success &= doPhase02(state);
174

    
175
			//PHASE 3: Add Rank data, KingdomFk, TypeNameFk ...
176
			success &= doPhase03(state);
177

    
178
			// 4nd Round: Add TreeIndex to each taxon
179
			success &= doPhase04(state);
180

    
181
			//"PHASE 5: Creating Inferred Synonyms...
182
			success &= doPhase05(state, mapping, synonymRelMapping);
183

    
184
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
185

    
186
			if (!success){
187
				state.getResult().addError("An error occurred in PesiTaxonExport.doInvoke. Success = false");
188
			}
189
			return;
190
		} catch (Exception e) {
191
			e.printStackTrace();
192
			logger.error(e.getMessage());
193
			state.getResult().addException(e);
194
		}
195
	}
196

    
197

    
198
	private void initPreparedStatements(PesiExportState state) throws SQLException {
199
//		initTreeIndexStatement(state);
200
		initRankExpertsUpdateStmt(state);
201
		initRankUpdateStatement(state);
202

    
203
		initParentFkStatement(state);
204
	}
205

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

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

    
220
	private void initRankUpdateStatement(PesiExportState state) throws SQLException {
221
		Connection connection = state.getConfig().getDestination().getConnection();
222
		String rankSql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, KingdomFk = ? WHERE TaxonId = ?";
223
		rankUpdateStmt = connection.prepareStatement(rankSql);
224
	}
225

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

    
232
		String sql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ? " +
233
				" WHERE TaxonId = ?";
234
		rankTypeExpertsUpdateStmt = connection.prepareStatement(sql);
235
	}
236

    
237
	private boolean doPhase01(PesiExportState state, PesiExportMapping mapping, PesiExportMapping additionalSourceMapping){
238

    
239
	    int count = 0;
240
		int pastCount = 0;
241
		boolean success = true;
242
		// Get the limit for objects to save within a single transaction.
243
		int limit = state.getConfig().getLimitSave();
244

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

    
254
		int partitionCount = 0;
255
		List<TaxonBase<?>> list;
256
		while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)) != null   ) {
257

    
258
			logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
259

    
260
			for (TaxonBase<?> taxon : list) {
261
				doCount(count++, modCount, pluralString);
262
				TaxonName taxonName = taxon.getName();
263

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

    
298
				//TODO switch on again, leads to some warnings in ERMS for taxa of not correctly handled kingdoms
299
				validatePhaseOne(taxon, nvn);
300
			}
301

    
302
			// Commit transaction
303
			commitTransaction(txStatus);
304
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 01)");
305
			pastCount = count;
306

    
307
			// Start new transaction
308
			txStatus = startTransaction(true);
309
			if (logger.isDebugEnabled()) {
310
                logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
311
            }
312

    
313
		}
314
		logger.debug("No " + pluralString + " left to fetch.");
315

    
316
		// Commit transaction
317
		commitTransaction(txStatus);
318
		txStatus = null;
319

    
320
		return success;
321
	}
322

    
323
	private void validatePhaseOne(TaxonBase<?> taxon, TaxonName taxonName) {
324

    
325
	    // Check whether some rules are violated
326
		String genusOrUninomial = taxonName.getGenusOrUninomial();
327
		String specificEpithet = taxonName.getSpecificEpithet();
328
		String infraSpecificEpithet = taxonName.getInfraSpecificEpithet();
329
		String infraGenericEpithet = taxonName.getInfraGenericEpithet();
330
		Rank rank =  taxonName.getRank();
331

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

    
339
			// Check whether infraGenericEpithet is set correctly
340
			// 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
341
			// 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
342

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

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

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

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

    
403
		insertBiota(state);
404

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

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

    
414
			if(logger.isDebugEnabled()) {
415
                logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
416
            }
417
			for (Taxon taxon : list) {
418
				for (TaxonNode node : taxon.getTaxonNodes()){
419
					doCount(count++, modCount, pluralString);
420
					TaxonNode parentNode = node.getParent();
421
					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
422
						int childId = state.getDbId( taxon);
423
						int parentId = state.getDbId(parentNode.getTaxon());
424
						success &= invokeParentTaxonFk(parentId, childId);
425
					}
426
				}
427
			}
428

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

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

    
444
		return success;
445
	}
446

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

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

    
475
		addValuelessTaxonToKingdomMap(state);
476

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
564
        return success;
565
    }
566

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
831

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

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

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

    
883
	/**
884
	 * Handles names that do not appear in taxa.
885
	 */
886
	private boolean doPhase01b_Names(PesiExportState state, PesiExportMapping additionalSourceMapping) {
887

    
888
		boolean success = true;
889
		if (! state.getConfig().isDoPureNames()){
890
			logger.info ("Ignore PHASE 1b: PureNames");
891
			return success;
892
		}
893

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

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

    
908
			int partitionCount = 0;
909
			List<TaxonName> list;
910
			while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null   ) {
911

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

    
922
				// Commit transaction
923
				commitTransaction(txStatus);
924
				logger.debug("Committed transaction.");
925
				logger.info("Exported " + (count - pastCount) + " " + pluralStringNames + ". Total: " + count + ". Partition: " + partitionCount);
926
				pastCount = count;
927

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

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

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

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

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

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

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

    
1049

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

    
1062
	    try {
1063
			Integer rankFk = getRankFk(taxonName, kingdomFk);
1064
			if (rankFk != null) {
1065
				rankUpdateStmt.setInt(1, rankFk);
1066
			} else {
1067
				rankUpdateStmt.setObject(1, null);
1068
			}
1069

    
1070
			String rankCache = getRankCache(taxonName, kingdomFk, state);
1071
			if (rankCache != null) {
1072
				rankUpdateStmt.setString(2, rankCache);
1073
			} else {
1074
				rankUpdateStmt.setObject(2, null);
1075
			}
1076

    
1077
			if (kingdomFk != null) {
1078

    
1079
				rankUpdateStmt.setInt(3, kingdomFk);
1080
			} else {
1081
				rankUpdateStmt.setObject(3, null);
1082
			}
1083

    
1084
			if (taxonFk != null) {
1085
				rankUpdateStmt.setInt(4, taxonFk);
1086
			} else {
1087
				rankUpdateStmt.setObject(4, null);
1088
			}
1089

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

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

    
1115
	    try {
1116
			int index = 1;
1117
			if (rankFk != null) {
1118
				rankTypeExpertsUpdateStmt.setInt(index++, rankFk);
1119
			} else {
1120
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1121
			}
1122

    
1123
			String rankCache = getRankCache(taxonName, kingdomFk, state);
1124
			if (rankCache != null) {
1125
				rankTypeExpertsUpdateStmt.setString(index++, rankCache);
1126
			} else {
1127
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1128
			}
1129

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

    
1136
			if (kingdomFk != null) {
1137
				rankTypeExpertsUpdateStmt.setInt(index++, kingdomFk);
1138
			} else {
1139
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1140
			}
1141

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

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

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

    
1183
		Source destination =  state.getConfig().getDestination();
1184

    
1185
		String[] tables = new String[]{"AdditionalTaxonSource","CommonNameSource","CommonName",
1186
		        "Image","NoteSource","Note","OccurrenceSource","Occurrence","RelTaxon","Taxon"};
1187

    
1188
		for(String table : tables){
1189
		    String sql = "DELETE FROM " + table;
1190
		    destination.update(sql);
1191
		}
1192

    
1193
		return true;
1194
	}
1195

    
1196
	private static Integer getRankFk(TaxonName taxonName, NomenclaturalCode nomenclaturalCode) {
1197
	    Integer kingdomId = PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
1198
	    return getRankFk(taxonName, kingdomId);
1199
	}
1200

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

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

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

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

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

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

    
1300
	/**
1301
	 * Returns the <code>AuthorString</code> attribute.
1302
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1303
	 * @return The <code>AuthorString</code> attribute.
1304
	 * @see MethodMapper
1305
	 */
1306
	//used by mapper
1307
	protected static String getAuthorString(TaxonBase<?> taxon) {
1308
		try {
1309
		    // For misapplied names there are special rules
1310
            if (isMisappliedName(taxon)){
1311
                return getMisappliedNameAuthorship(taxon);
1312
            }else{
1313
                boolean isNonViralName = false;
1314
                String authorshipCache = null;
1315
                TaxonName taxonName = taxon.getName();
1316
                if (taxonName != null && taxonName.isNonViral()){
1317
                    authorshipCache = taxonName.getAuthorshipCache();
1318
                    isNonViralName = true;
1319
                }
1320
                String result = authorshipCache;
1321

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

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

    
1340
	private static String getMisappliedNameAuthorship(TaxonBase<?> taxon){
1341
        String result;
1342
	    String relAppendedPhrase = taxon.getAppendedPhrase();
1343
        Reference sec = taxon.getSec();
1344
        String secTitle = sec != null ? sec.getTitleCache(): null;
1345
        if(relAppendedPhrase == null && sec == null) {
1346
            result = "auct.";
1347
        }else if (relAppendedPhrase != null && sec == null){
1348
            result = relAppendedPhrase;
1349
        }else if (relAppendedPhrase == null && sec != null){
1350
            result = "sensu " + secTitle;
1351
        }else{  //append!=null && sec!=null
1352
            result = relAppendedPhrase + " " + secTitle;
1353
        }
1354
        String authorship = taxon.getName().getAuthorshipCache();
1355
        if (isNotBlank(authorship)){
1356
            result += ", non " + authorship;
1357
        }
1358
        return result;
1359
	}
1360

    
1361
    /**
1362
     * Returns the <code>DisplayName</code> attribute.
1363
     * @param taxon The {@link TaxonBase Taxon}.
1364
     * @return The <code>DisplayName</code> attribute.
1365
     * @see MethodMapper
1366
     */
1367
    //used by Mapper
1368
    private static String getDisplayName(TaxonBase<?> taxon) {
1369
        boolean isMisapplied = isMisappliedName(taxon);
1370
        TaxonName taxonName = taxon.getName();
1371
        String result = getDisplayName(taxonName, isMisapplied);
1372
        if (isMisapplied){
1373
            result = result + " " + getMisappliedNameAuthorship(taxon);
1374
        }
1375
        return result;
1376
    }
1377

    
1378
    /**
1379
     * Returns the <code>DisplayName</code> attribute.
1380
     * @param taxonName The {@link TaxonNameBase TaxonName}.
1381
     * @return The <code>DisplayName</code> attribute.
1382
     * @see MethodMapper
1383
     */
1384
    @SuppressWarnings("unused")  //used by Mapper
1385
    private static String getDisplayName(TaxonName taxonName) {
1386
        return getDisplayName(taxonName, false);
1387
    }
1388

    
1389
	private static String getDisplayName(TaxonName taxonName, boolean useNameCache) {
1390
		// TODO: extension?
1391
		if (taxonName == null) {
1392
			return null;
1393
		}else{
1394
		    taxonName = CdmBase.deproxy(taxonName);
1395
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1396
			HTMLTagRules tagRules = new HTMLTagRules().
1397
					addRule(TagEnum.name, "i").
1398
					addRule(TagEnum.nomStatus, "@status@");
1399

    
1400
			String result;
1401
			if (getSources(taxonName).contains(PesiSource.ERMS)){
1402
			    result = cacheStrategy.getTitleCache(taxonName, tagRules);  //according to SQL script (also in ERMS sources are not abbreviated)
1403
			}else if (getSources(taxonName).contains(PesiSource.EM)){
1404
			    if (useNameCache){
1405
			        result = cacheStrategy.getNameCache(taxonName, tagRules);
1406
			    }else{
1407
			        result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
1408
			    }
1409
			}else{
1410
			    //TODO define for FE + IF and for multiple sources
1411
	             if (useNameCache){
1412
                    result = cacheStrategy.getNameCache(taxonName, tagRules);
1413
                }else{
1414
                    result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
1415
                }
1416
			}
1417
			return result.replaceAll("(, ?)?\\<@status@\\>.*\\</@status@\\>", "").trim();
1418
		}
1419
	}
1420

    
1421
	@SuppressWarnings("unused")
1422
	private static String getGUID(TaxonName taxonName) {
1423
		UUID uuid = taxonName.getUuid();
1424
		String result = "NameUUID:" + uuid.toString();
1425
		return result;
1426
	}
1427

    
1428
	/**
1429
	 * Returns the <code>WebShowName</code> attribute for a taxon.
1430
	 * See {@link #getWebShowName(TaxonName)} for further explanations.
1431
	 * @param taxon The {@link TaxonBase taxon}.
1432
	 * @return The <code>WebShowName</code> attribute.
1433
	 * @see #getWebShowName(TaxonName)
1434
	 * @see #getDisplayName(TaxonBase)
1435
	 * @see #getFullName(TaxonBase)
1436
	 * @see MethodMapper
1437
	*/
1438
	@SuppressWarnings("unused")
1439
	private static String getWebShowName(TaxonBase<?> taxon) {
1440
	    if (isMisappliedName(taxon)){
1441
	        //for misapplications the webshowname is the same as the displayname as they do not show the nom.ref. in displayname
1442
	        return getDisplayName(taxon);
1443
	    }else{
1444
	        TaxonName taxonName = taxon.getName();
1445
	        return getWebShowName(taxonName);
1446
	    }
1447
	}
1448

    
1449
	/**
1450
	 * Returns the <code>WebShowName</code> attribute for a name. The
1451
	 * <code>WebShowName</code> is like fullName but with
1452
	 * tagged (<i>) name part. It is also similar to
1453
	 * <code>DisplayName</code> but for titleCache not fullTitleCache.
1454
	 * For misapplications it slightly differs (see {@link #getWebShowName(TaxonBase)} )
1455
	 *
1456
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1457
	 * @return The <code>WebShowName</code> attribute.
1458
	 * @see #getDisplayName(TaxonName)
1459
	 * @see #getFullName(TaxonName)
1460
	 * @see #getWebShowName(TaxonBase)
1461
	 * @see MethodMapper
1462
	 */
1463
	private static String getWebShowName(TaxonName taxonName) {
1464
		//TODO extensions?
1465
		if (taxonName == null) {
1466
			return null;
1467
		}else{
1468
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1469

    
1470
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1471
			String result = cacheStrategy.getTitleCache(taxonName, tagRules);
1472
			return result;
1473
		}
1474
	}
1475

    
1476
	/**
1477
	 * Returns the <code>WebSearchName</code> attribute.
1478
	 * @param taxonName The {@link NonViralName NonViralName}.
1479
	 * @return The <code>WebSearchName</code> attribute.
1480
	 * @see MethodMapper
1481
	 */
1482
	@SuppressWarnings("unused")
1483
	private static String getWebSearchName(TaxonName taxonName) {
1484
		//TODO extensions?
1485
	    TaxonNameDefaultCacheStrategy strategy = getCacheStrategy(taxonName);
1486
		String result = strategy.getNameCache(taxonName);
1487
		return result;
1488
	}
1489

    
1490
    @SuppressWarnings("unused")     //used by mapper
1491
    private static String getFullName(TaxonBase<?> taxon) {
1492
        if (isMisappliedName(taxon)){
1493
            String result = getCacheStrategy(taxon.getName()).getNameCache(taxon.getName());
1494
            result = result + " " + getMisappliedNameAuthorship(taxon);
1495
            return result;
1496
        }else{
1497
            return getFullName(taxon.getName());
1498
        }
1499
    }
1500

    
1501
	/**
1502
	 * Returns the <code>FullName</code> attribute.
1503
	 * @param taxonName The {@link NonViralName NonViralName}.
1504
	 * @return The <code>FullName</code> attribute.
1505
	 * @see MethodMapper
1506
	 */
1507
    //used by mapper
1508
	private static String getFullName(TaxonName taxonName) {
1509
		//TODO extensions?
1510
		String result = getCacheStrategy(taxonName).getTitleCache(taxonName);
1511
		//misapplied names are now handled differently in getFullName(TaxonBase)
1512
//		Iterator<Taxon> taxa = taxonName.getTaxa().iterator();
1513
//		if (taxonName.getTaxa().size() >0){
1514
//			if (taxonName.getTaxa().size() == 1){
1515
//				Taxon taxon = taxa.next();
1516
//				if (isMisappliedName(taxon)){
1517
//					result = result + " " + getAuthorString(taxon);
1518
//				}
1519
//			}
1520
//		}
1521
		return result;
1522
	}
1523

    
1524
	/**
1525
	 * Returns the SourceNameCache for the AdditionalSource table
1526
	 * @param taxonName
1527
	 * @return
1528
	 */
1529
	static boolean isFirstAbbrevTitle = true;
1530
	@SuppressWarnings("unused")
1531
	private static String getSourceNameCache(TaxonName taxonName) {
1532
		if (taxonName != null){
1533
			Reference nomRef = taxonName.getNomenclaturalReference();
1534
			if (nomRef != null ){
1535
			    if (isFirstAbbrevTitle){
1536
			        //#5388 is definetely not the correct ticket number
1537
			        logger.warn("Semantics of getAbbrevTitleCache has changed. Please check if output is still correct. See #5388");
1538
			        isFirstAbbrevTitle = false;
1539
			    }
1540
			    return nomRef.getAbbrevTitleCache();
1541
			}
1542
		}
1543
		return null;
1544
	}
1545

    
1546
	/**
1547
	 * Returns the nomenclatural reference which is the reference
1548
	 * including the detail (microreference).
1549
	 * @param taxonName The {@link TaxonName taxon name}.
1550
	 * @see MethodMapper
1551
	 */
1552
	@SuppressWarnings("unused")
1553
	private static String getNomRefString(TaxonName taxonName) {
1554
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1555
		if (ref == null){
1556
			return null;
1557
		}
1558
		String result = null;
1559
		EnumSet<PesiSource> sources = getSources(taxonName);
1560
		if(sources.contains(PesiSource.EM)){
1561
		    if (! ref.isProtectedAbbrevTitleCache()){
1562
		        ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1563
		    }
1564
		    result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1565
		}else if(sources.contains(PesiSource.FE)||sources.contains(PesiSource.IF) ){
1566
            //TODO still need to check if correct for FE + IF
1567
		    if (! ref.isProtectedAbbrevTitleCache()){
1568
                ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1569
            }
1570
            result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1571
            return result;   // according to SQL script
1572
		}else if(sources.contains(PesiSource.ERMS)) {
1573
            //result = null; //according to SQL script
1574
		}else{
1575
		    logger.warn("Source not yet supported");
1576
		}
1577
		return result;
1578
	}
1579

    
1580
	/**
1581
	 * Returns the <code>NameStatusFk</code> attribute.
1582
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1583
	 * @return The <code>NameStatusFk</code> attribute.
1584
	 * @see MethodMapper
1585
	 */
1586
	@SuppressWarnings("unused")
1587
	private static Integer getNameStatusFk(TaxonName taxonName) {
1588
		Integer result = null;
1589

    
1590
		NomenclaturalStatus status = getNameStatus(taxonName);
1591
		if (status != null) {
1592
			result = PesiTransformer.nomStatus2nomStatusFk(status.getType());
1593
		}
1594
		return result;
1595
	}
1596

    
1597
	/**
1598
	 * Returns the <code>NameStatusCache</code> attribute.
1599
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1600
	 * @return The <code>NameStatusCache</code> attribute.
1601
	 * @throws UndefinedTransformerMethodException
1602
	 * @see MethodMapper
1603
	 */
1604
	@SuppressWarnings("unused")
1605
	private static String getNameStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1606
		String result = null;
1607
		NomenclaturalStatus status = getNameStatus(taxonName);
1608
		if (status != null) {
1609
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1610
		}
1611
		return result;
1612
	}
1613

    
1614
	private static NomenclaturalStatus getNameStatus(TaxonName taxonName) {
1615
		try {
1616
			if (taxonName != null) {
1617
			    Set<NomenclaturalStatus> states = taxonName.getStatus();
1618
				if (states.size() >= 1) {
1619
				    if (states.size() > 1) {
1620
				        String statusStr = null;
1621
				        for (NomenclaturalStatus status: states){
1622
				            statusStr = CdmUtils.concat(",", statusStr, status.getType()== null? null:status.getType().getTitleCache());
1623
				        }
1624
				        //a known case is ad43508a-8a10-480a-8519-2a76de2c0a0f (Labiatae Juss.) from E+M
1625
	                    logger.warn("This TaxonName has more than one nomenclatural status. This may happen in very rare cases but is not handled by the PESI data warehouse. Taxon name: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")Status:" + statusStr);
1626
	                }
1627
				    NomenclaturalStatus status = states.iterator().next();
1628
					return status;
1629
				}
1630
			}
1631
		} catch (Exception e) {
1632
			e.printStackTrace();
1633
		}
1634
		return null;
1635
	}
1636

    
1637
	/**
1638
	 * Returns the <code>TaxonStatusFk</code> attribute.
1639
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1640
	 * @param state The {@link PesiExportState PesiExportState}.
1641
	 * @return The <code>TaxonStatusFk</code> attribute.
1642
	 * @see MethodMapper
1643
	 */
1644
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1645
		try {
1646
			return PesiTransformer.taxonBase2statusFk(taxon);
1647
		} catch (Exception e) {
1648
			e.printStackTrace();
1649
			return null;
1650
		}
1651
	}
1652

    
1653
	/**
1654
	 * Returns the <code>TaxonStatusCache</code> attribute.
1655
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1656
	 * @param state The {@link PesiExportState PesiExportState}.
1657
	 * @return The <code>TaxonStatusCache</code> attribute.
1658
	 * @throws UndefinedTransformerMethodException
1659
	 * @see MethodMapper
1660
	 */
1661
	@SuppressWarnings("unused")
1662
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1663
		return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1664
	}
1665

    
1666
    /**
1667
     * Returns the <code>TaxonFk1</code> attribute. It corresponds to a CDM <code>TaxonRelationship</code>.
1668
     * @param relationship The {@link RelationshipBase Relationship}.
1669
     * @param state The {@link PesiExportState PesiExportState}.
1670
     * @return The <code>TaxonFk1</code> attribute.
1671
     * @see MethodMapper
1672
     */
1673
    @SuppressWarnings("unused")
1674
    private static Integer getSynonym(Synonym synonym, PesiExportState state) {
1675
         return state.getDbId(synonym);
1676
    }
1677

    
1678
	/**
1679
	 * Returns the <code>TypeNameFk</code> attribute.
1680
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1681
	 * @param state The {@link PesiExportState PesiExportState}.
1682
	 * @return The <code>TypeNameFk</code> attribute.
1683
	 * @see MethodMapper
1684
	 */
1685
	private static Integer getTypeNameFk(TaxonName taxonName, PesiExportState state) {
1686
		Integer result = null;
1687
		if (taxonName != null) {
1688
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1689
			if (nameTypeDesignations.size() == 1) {
1690
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1691
				if (nameTypeDesignation != null) {
1692
					TaxonName typeName = nameTypeDesignation.getTypeName();
1693
					if (typeName != null) {
1694
					    if (typeName.getTaxonBases().isEmpty()){
1695
					        logger.warn("Type name does not belong to a taxon and therefore is not expected to be a European taxon. Type name not added. Type name: " + typeName.getTitleCache() + ", typified name: " + taxonName.getTitleCache());
1696
					    }else{
1697
					        result = state.getDbId(typeName);
1698
					    }
1699
					}
1700
				}
1701
			} else if (nameTypeDesignations.size() > 1) {
1702
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1703
			}
1704
		}
1705
		return result;
1706
	}
1707

    
1708
	/**
1709
	 * Returns the <code>TypeFullnameCache</code> attribute.
1710
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1711
	 * @return The <code>TypeFullnameCache</code> attribute.
1712
	 * @see MethodMapper
1713
	 */
1714
	@SuppressWarnings("unused")
1715
	private static String getTypeFullnameCache(TaxonName taxonName) {
1716
		String result = null;
1717

    
1718
		try {
1719
    		if (taxonName != null) {
1720
    			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1721
    			if (nameTypeDesignations.size() == 1) {
1722
    				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1723
    				if (nameTypeDesignation != null) {
1724
    					TaxonName typeName = nameTypeDesignation.getTypeName();
1725
    					if (typeName != null) {
1726
    						result = typeName.getTitleCache();
1727
    					}
1728
    				}
1729
    			} else if (nameTypeDesignations.size() > 1) {
1730
    				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1731
    			}
1732
    		}
1733
		} catch (Exception e) {
1734
			e.printStackTrace();
1735
		}
1736
		return result;
1737
	}
1738

    
1739

    
1740
	/**
1741
	 * Returns the <code>QualityStatusFk</code> attribute.
1742
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1743
	 * @return The <code>QualityStatusFk</code> attribute.
1744
	 * @see MethodMapper
1745
	 */
1746
	private static Integer getQualityStatusFk(TaxonName taxonName) {
1747
	    EnumSet<PesiSource> sources = getSources(taxonName);
1748
		return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1749
	}
1750

    
1751
	/**
1752
	 * Returns the <code>QualityStatusCache</code> attribute.
1753
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1754
	 * @return The <code>QualityStatusCache</code> attribute.
1755
	 * @throws UndefinedTransformerMethodException
1756
	 * @see MethodMapper
1757
	 */
1758
	@SuppressWarnings("unused")
1759
	private static String getQualityStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1760
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1761
	}
1762

    
1763

    
1764
	/**
1765
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1766
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1767
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1768
	 * @see MethodMapper
1769
	 */
1770
	//TODO seems not to be used
1771
	private static Integer getTypeDesignationStatusFk(TaxonName taxonName) {
1772
		Integer result = null;
1773

    
1774
		try {
1775
    		if (taxonName != null) {
1776
    			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1777
    			if (typeDesignations.size() == 1) {
1778
    				Object obj = typeDesignations.iterator().next().getTypeStatus();
1779
    				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1780
    				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1781
    			} else if (typeDesignations.size() > 1) {
1782
    				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1783
    			}
1784
    		}
1785
		} catch (Exception e) {
1786
			e.printStackTrace();
1787
		}
1788
		return result;
1789
	}
1790

    
1791
	/**
1792
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1793
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1794
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1795
	 * @see MethodMapper
1796
	 */
1797
	//TODO seems not to be used
1798
	private static String getTypeDesignationStatusCache(TaxonName taxonName) {
1799
		String result = null;
1800

    
1801
		try {
1802
    		if (taxonName != null) {
1803
    			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1804
    			if (typeDesignations.size() == 1) {
1805
    				Object obj = typeDesignations.iterator().next().getTypeStatus();
1806
    				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1807
    				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
1808
    			} else if (typeDesignations.size() > 1) {
1809
    				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1810
    			}
1811
    		}
1812
		} catch (Exception e) {
1813
			e.printStackTrace();
1814
		}
1815
		return result;
1816
	}
1817

    
1818
	/**
1819
	 * Returns the <code>FossilStatusFk</code> attribute.
1820
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1821
	 * @return The <code>FossilStatusFk</code> attribute.
1822
	 * @see MethodMapper
1823
	 */
1824
	@SuppressWarnings("unused")
1825
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
1826
		Integer result = null;
1827

    
1828
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
1829
		if (fossilStatuus.size() == 0){
1830
			return null;
1831
		}else if (fossilStatuus.size() > 1){
1832
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
1833
		}
1834
		String fossilStatus = fossilStatuus.iterator().next();
1835

    
1836
		int statusFk = state.getTransformer().fossilStatusCache2FossilStatusFk(fossilStatus);
1837
		return statusFk;
1838
	}
1839

    
1840
	/**
1841
	 * Returns the <code>FossilStatusCache</code> attribute.
1842
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1843
	 * @return The <code>FossilStatusCache</code> attribute.
1844
	 * @see MethodMapper
1845
	 */
1846
	@SuppressWarnings("unused")
1847
	private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
1848
		String result = null;
1849
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
1850
		if (fossilStatuus.size() == 0){
1851
			return null;
1852
		}
1853
		for (String strFossilStatus : fossilStatuus){
1854
			result = CdmUtils.concat(";", result, strFossilStatus);
1855
		}
1856
		return result;
1857
	}
1858

    
1859
	/**
1860
	 * Returns the <code>IdInSource</code> attribute.
1861
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1862
	 * @return The <code>IdInSource</code> attribute.
1863
	 * @see MethodMapper
1864
	 */
1865
	@SuppressWarnings("unused")
1866
	private static String getIdInSource(IdentifiableEntity<?> taxonName) {
1867
		String result = null;
1868

    
1869
		try {
1870
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
1871
			if (sources.size() > 1){
1872
				logger.warn("There is > 1 Pesi source. This is not yet handled.");
1873
			}
1874
			if (sources.size() == 0){
1875
				logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1876
			}
1877
			for (IdentifiableSource source : sources) {
1878
				Reference ref = source.getCitation();
1879
				UUID refUuid = ref.getUuid();
1880
				String idInSource = source.getIdInSource();
1881
				if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed)){
1882
					result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
1883
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
1884
					result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
1885
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
1886
					result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
1887
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
1888
					result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
1889
				}else{
1890
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");}
1891
				}
1892

    
1893
				String sourceIdNameSpace = source.getIdNamespace();
1894
				if (sourceIdNameSpace != null) {
1895
					if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
1896
						result =  idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
1897
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
1898
						result =  idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
1899
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
1900
						result =  idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
1901
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
1902
						result =  idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
1903
					}
1904
				}
1905
				if (result == null) {
1906
					logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
1907
				}
1908
			}
1909
		} catch (Exception e) {
1910
			e.printStackTrace();
1911
			logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
1912
		}
1913

    
1914
		if (result == null) {
1915
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1916
		}
1917
		return result;
1918
	}
1919

    
1920
	/**
1921
	 * Returns the idInSource for a given TaxonName only.
1922
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1923
	 * @return The idInSource.
1924
	 */
1925
	private static String getIdInSourceOnly(IdentifiableEntity<?> identEntity) {
1926
		String result = null;
1927

    
1928
		// Get the sources first
1929
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
1930

    
1931
		// Determine the idInSource
1932
		if (sources.size() == 1) {
1933
			IdentifiableSource source = sources.iterator().next();
1934
			if (source != null) {
1935
				result = source.getIdInSource();
1936
			}
1937
		} else if (sources.size() > 1) {
1938
			int count = 1;
1939
			result = "";
1940
			for (IdentifiableSource source : sources) {
1941
				result += source.getIdInSource();
1942
				if (count < sources.size()) {
1943
					result += "; ";
1944
				}
1945
				count++;
1946
			}
1947

    
1948
		}
1949

    
1950
		return result;
1951
	}
1952

    
1953
	/**
1954
	 * Returns the <code>GUID</code> attribute.
1955
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1956
	 * @return The <code>GUID</code> attribute.
1957
	 * @see MethodMapper
1958
	 */
1959
	private static String getGUID(TaxonBase<?> taxon) {
1960
		if (taxon.getLsid() != null ){
1961
			return taxon.getLsid().getLsid();
1962
		}else if (taxon.hasMarker(PesiTransformer.uuidMarkerGuidIsMissing, true)){
1963
			return null;
1964
		}else{
1965
			return taxon.getUuid().toString();
1966
		}
1967
	}
1968

    
1969
	/**
1970
	 * Returns the <code>DerivedFromGuid</code> attribute.
1971
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1972
	 * @return The <code>DerivedFromGuid</code> attribute.
1973
	 * @see MethodMapper
1974
	 */
1975
	@SuppressWarnings("unused")
1976
	private static String getDerivedFromGuid(TaxonBase<?> taxon) {
1977
		String result = null;
1978
		try {
1979
		// The same as GUID for now
1980
		result = getGUID(taxon);
1981
		} catch (Exception e) {
1982
			e.printStackTrace();
1983
		}
1984
		return result;
1985
	}
1986

    
1987
	/**
1988
	 * Returns the <code>CacheCitation</code> attribute.
1989
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1990
	 * @return The CacheCitation.
1991
	 * @see MethodMapper
1992
	 */
1993
	@SuppressWarnings("unused")
1994
	private static String getCacheCitation(TaxonBase<?> taxon) {
1995
		// !!! See also doPhaseUpdates
1996

    
1997
		TaxonName taxonName = taxon.getName();
1998
		String result = "";
1999
		//TODO implement anew for taxa
2000
		try {
2001
			EnumSet<PesiSource> sources = getSources(taxon);
2002
			//TODO what if 2 sources? In PESI 2014 they were pipe separated
2003
			//TODO why does ERMS use accessed through eu-nomen, while E+M uses accessed through E+M
2004
			if (sources.isEmpty()) {
2005
//				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2006
			} else if (sources.contains(PesiSource.ERMS)) {
2007
				//TODO check if correct, compare with PESI 2014
2008
			    Set<Extension> extensions = taxon.getExtensions();
2009
				for (Extension extension : extensions) {
2010
					if (extension.getType().equals(cacheCitationExtensionType)) {
2011
						result = extension.getValue();
2012
					}
2013
				}
2014
			} else if (sources.contains(PesiSource.EM)) {
2015
			    //TODO
2016
			    boolean isMisapplied = isMisappliedName(taxon);
2017
			    boolean isProParteSyn = isProParteOrPartialSynonym(taxon);
2018
			    Reference sec = null;
2019
			    if(!isMisapplied && !isProParteSyn){
2020
			        sec = taxon.getSec();
2021
			    }else if (isMisapplied){
2022
			        sec = getAcceptedTaxonForMisappliedName(taxon).getSec();
2023
			    }else if (isProParteSyn){
2024
                    sec = getAcceptedTaxonForProParteSynonym(taxon).getSec();
2025
                }
2026
			    if (sec == null){
2027
			        logger.warn("Sec could not be defined for taxon " + taxon.getTitleCache()+ "; " + taxon.getUuid());
2028
			    }
2029
			    String author = sec == null? "" : sec.getTitleCache();
2030
			    String webShowName = isMisapplied? getDisplayName(taxon):getWebShowName(taxonName);  //for misapplied we need also the sensu and non author part, for ordinary names name + author is enough
2031
			    String accessed = ". Accessed through: Euro+Med PlantBase at https://www.europlusmed.org/cdm_dataportal/taxon/";
2032
			    result = CdmUtils.removeTrailingDot(author)
2033
			            + ". " + CdmUtils.removeTrailingDot(webShowName)
2034
			            + accessed + taxon.getUuid();
2035
			} else {
2036
				//TODO check for IF + FE
2037

    
2038
			    String expertName = getExpertName(taxon);
2039
				String webShowName = getWebShowName(taxonName);
2040

    
2041
				// idInSource only
2042
				String idInSource = getIdInSourceOnly(taxonName);
2043

    
2044
				// build the cacheCitation
2045
				if (expertName != null) {
2046
					result += expertName + ". ";
2047
				} else {
2048
					if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
2049
				}
2050
				if (webShowName != null) {
2051
					result += webShowName + ". ";
2052
				} else {
2053
					logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2054
				}
2055

    
2056
				if (getOriginalDB(taxonName).equals("FaEu")) {
2057
					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
2058
				} else if (getOriginalDB(taxonName).equals("EM")) {
2059
					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
2060
				}
2061

    
2062
				if (idInSource != null) {
2063
					result += idInSource;
2064
				} else {
2065
					logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2066
				}
2067
			}
2068
		} catch (Exception e) {
2069
			e.printStackTrace();
2070
		}
2071

    
2072
		if (StringUtils.isBlank(result)) {
2073
			return null;
2074
		} else {
2075
			return result;
2076
		}
2077
	}
2078

    
2079
	/**
2080
	 * Returns the <code>OriginalDB</code> attribute.
2081
	 * @param identifiableEntity
2082
	 * @return The <code>OriginalDB</code> attribute.
2083
	 * @see MethodMapper
2084
	 */
2085
//	@SuppressWarnings("unused")
2086
	private static String getOriginalDB(IdentifiableEntity<?> identifiableEntity) {
2087
		EnumSet<PesiSource> sources  = getSources(identifiableEntity);
2088
		return PesiTransformer.getOriginalDbBySources(sources);
2089
	}
2090

    
2091
	/**
2092
	 * Returns the <code>ExpertName</code> attribute.
2093
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2094
	 * @return The <code>ExpertName</code> attribute.
2095
	 * @see MethodMapper
2096
	 */
2097
	@SuppressWarnings("unused")  //for some reason it is also called by getCacheCitation
2098
	private static String getExpertName(TaxonBase<?> taxon) {
2099
		try {
2100
		    String result = null;
2101
    		if(expertNameExtensionType!=null){  //some databases do not have this extension type
2102
    		    Set<Extension> extensions = taxon.getExtensions();
2103
    		    for (Extension extension : extensions) {
2104
    		        if (extension.getType().equals(expertNameExtensionType)) {
2105
    		            result = extension.getValue();
2106
    		        }
2107
    		    }
2108
    		}
2109
    		if (getPesiSources(taxon).contains(PesiSource.EM)){
2110
                return taxon.getSec().getTitleCache();
2111
            }
2112
            return null;
2113
		} catch (Exception e) {
2114
			e.printStackTrace();
2115
			return null;
2116
		}
2117
	}
2118

    
2119
	//TODO change to ExpertGUID
2120
	private static Integer getExpertFk(Reference reference, PesiExportState state) {
2121
		Integer result = state.getDbId(reference);
2122
		return result;
2123
	}
2124

    
2125
	/**
2126
	 * Returns the <code>SpeciesExpertName</code> attribute.
2127
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2128
	 * @return The <code>SpeciesExpertName</code> attribute.
2129
	 * @see MethodMapper
2130
	 */
2131
	@SuppressWarnings("unused")
2132
	private static String getSpeciesExpertName(TaxonBase<?> taxon) {
2133
		try {
2134
    		Set<Extension> extensions = taxon.getExtensions();
2135
    		if(speciesExpertNameExtensionType != null){  //some databases do not have this extension type
2136
                for (Extension extension : extensions) {
2137
        			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2138
        				return extension.getValue();
2139
        			}
2140
        		}
2141
    		}
2142
    		if (getPesiSources(taxon).contains(PesiSource.EM)){
2143
    		    return taxon.getSec().getTitleCache();
2144
            }
2145
    		return null;
2146
		} catch (Exception e) {
2147
			e.printStackTrace();
2148
			return null;
2149
		}
2150
	}
2151

    
2152
	/**
2153
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2154
	 * @param reference The {@link Reference Reference}.
2155
	 * @param state The {@link PesiExportState PesiExportState}.
2156
	 * @return The <code>SpeciesExpertFk</code> attribute.
2157
	 * @see MethodMapper
2158
	 */
2159
	//TODO should be changed to SpeciesExpertGUID
2160
	private static Integer getSpeciesExpertFk(Reference reference, PesiExportState state) {
2161
		Integer result = state.getDbId(reference);
2162
		return result;
2163
	}
2164

    
2165
	protected static TaxonNameDefaultCacheStrategy getCacheStrategy(TaxonName taxonName) {
2166
		taxonName = CdmBase.deproxy(taxonName);
2167
		TaxonNameDefaultCacheStrategy cacheStrategy;
2168
		if (taxonName.isZoological()){
2169
			cacheStrategy = zooNameStrategy;
2170
		}else if (taxonName.isBotanical()) {
2171
			cacheStrategy = nonViralNameStrategy;
2172
		}else if (taxonName.isNonViral()) {
2173
			cacheStrategy = nonViralNameStrategy;
2174
		}else if (taxonName.isBacterial()) {
2175
			cacheStrategy = nonViralNameStrategy;
2176
		}else{
2177
			logger.error("Unhandled taxon name type. Can't define strategy class");
2178
			cacheStrategy = nonViralNameStrategy;
2179
		}
2180
		return cacheStrategy;
2181
	}
2182

    
2183
	/**
2184
	 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2185
	 * @param relationship The {@link RelationshipBase Relationship}.
2186
	 * @return The <code>RelTaxonQualifierFk</code> attribute.
2187
	 * @see MethodMapper
2188
	 */
2189
	@SuppressWarnings("unused")
2190
	private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2191
		return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2192
	}
2193

    
2194
    //TODO still in use?
2195
    private static String getSynonymTypeCache(Synonym synonym, PesiExportState state) {
2196
        String result = null;
2197
        NomenclaturalCode code = null;
2198
        code = CdmBase.deproxy(synonym, Synonym.class).getAcceptedTaxon().getName().getNameType();
2199

    
2200
        if (code != null) {
2201
            result = state.getConfig().getTransformer().getCacheBySynonymType(synonym, code);
2202
        } else {
2203
            logger.error("NomenclaturalCode is NULL while creating the following synonym: " + synonym.getUuid());
2204
        }
2205
        return result;
2206
    }
2207

    
2208
// ********************************** MAPPINGS ********************************/
2209

    
2210
	/**
2211
	 * Returns the CDM to PESI specific export mappings.
2212
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2213
	 */
2214
	private PesiExportMapping getMapping() {
2215
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2216

    
2217
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2218
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2219
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2220
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2221

    
2222
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2223

    
2224
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2225
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2226
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because misapplied names are handled differently
2227
		mapping.addMapper(MethodMapper.NewInstance("FullName", this));    //For Taxon because misapplied names are handled differently
2228
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2229

    
2230
		// DisplayName
2231
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2232

    
2233
		// FossilStatus (Fk, Cache)
2234
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2235
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2236

    
2237
		//handled by name mapping
2238
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2239
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2240

    
2241
		//experts
2242
//		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2243
		mapping.addMapper(MethodMapper.NewInstance("SpeciesExpertName", this, TaxonBase.class));
2244
//		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
2245
//		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2246
		mapping.addMapper(MethodMapper.NewInstance("ExpertName", this, TaxonBase.class));
2247

    
2248
		//ParentTaxonFk handled in Phase02 now
2249
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2250

    
2251
		addNameMappers(mapping);
2252

    
2253
		return mapping;
2254
	}
2255

    
2256
	/**
2257
	 * Returns the CDM to PESI specific export mappings.
2258
	 * @param state
2259
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2260
	 * @throws UndefinedTransformerMethodException
2261
	 */
2262
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2263
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2264

    
2265
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2266

    
2267
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonName.class));
2268
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonName.class));
2269
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonName.class, PesiExportState.class));
2270
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER, PesiTransformer.T_STATUS_UNACCEPTED));
2271
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR, state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2272
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
2273
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonName.class));
2274
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonName.class));
2275
		mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonName.class));
2276

    
2277
		// DisplayName
2278
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonName.class));
2279

    
2280
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2281
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2282

    
2283
		addNameMappers(mapping);
2284
		return mapping;
2285
	}
2286

    
2287
	private void addNameMappers(PesiExportMapping mapping) {
2288

    
2289
	    //epithets
2290
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2291
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2292
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2293
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2294

    
2295
		//full name
2296
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2297
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonName.class));
2298

    
2299
		//nom ref
2300
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonName.class));
2301

    
2302
		//status
2303
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonName.class));
2304
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonName.class, PesiExportState.class));
2305
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonName.class));
2306
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonName.class, PesiExportState.class));
2307

    
2308
		//types
2309
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
2310
		//TypeNameFk handled in Phase3
2311

    
2312
		//supplemental
2313
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2314
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2315

    
2316
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2317

    
2318
	}
2319

    
2320
	private PesiExportMapping getSynRelMapping() {
2321
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2322
		logger.warn("SynRelMapping currently not implemented. Needs to be checked");
2323

    
2324
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getSynonym", Synonym.class, PesiExportState.class));
2325
		mapping.addMapper(DbObjectMapper.NewInstance("acceptedTaxon", "TaxonFk2"));
2326
		mapping.addMapper(DbObjectMapper.NewInstance("type", "RelTaxonQualifierFk"));
2327
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this.getClass(), "getSynonymTypeCache", Synonym.class, PesiExportState.class));
2328
		// TODO
2329
//		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2330

    
2331
		return mapping;
2332
	}
2333

    
2334
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state) {
2335
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2336

    
2337
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2338
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2339

    
2340
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2341
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2342
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonName.class));
2343

    
2344
		//we have only nomenclatural references here
2345
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2346
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2347

    
2348
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2349

    
2350
		return mapping;
2351
	}
2352

    
2353

    
2354
    @Override
2355
    protected boolean doCheck(PesiExportState state) {
2356
        return true;
2357
    }
2358

    
2359
    @Override
2360
    protected boolean isIgnore(PesiExportState state) {
2361
        return ! state.getConfig().isDoTaxa();
2362
    }
2363
}
(13-13/14)