Project

General

Profile

Download (94.8 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
		if (taxonName == null) {
1391
			return null;
1392
		}else{
1393
		    taxonName = CdmBase.deproxy(taxonName);
1394
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1395
			HTMLTagRules tagRules = new HTMLTagRules().
1396
					addRule(TagEnum.name, "i").
1397
					addRule(TagEnum.nomStatus, "@status@");
1398

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

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

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

    
1464
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1465
			String result = cacheStrategy.getTitleCache(taxonName, tagRules);
1466
			result = replaceTagForInfraSpecificMarkerForProtectedTitleCache(taxonName, result);
1467
			return result;
1468
		}
1469
	}
1470

    
1471
    private static String replaceTagForInfraSpecificMarkerForProtectedTitleCache(TaxonName taxonName, String result) {
1472
        if (taxonName.isProtectedTitleCache()||taxonName.isProtectedNameCache()){
1473
            if (!taxonName.isAutonym()){
1474
                result = result
1475
                        .replace(" subsp. ", "</i> subsp. <i>")
1476
                        .replace(" var. ", "</i> var. <i>")
1477
                        .replace(" subvar. ", "</i> subvar. <i>")
1478
                        .replace(" f. ", "</i> f. <i>")
1479
                        .replace(" subf. ", "</i> subf. <i>")  //does this exist?
1480
                        ;
1481
            }
1482
        }
1483
        return result;
1484
    }
1485

    
1486
    /**
1487
	 * Returns the <code>WebSearchName</code> attribute.
1488
	 * @param taxonName The {@link NonViralName NonViralName}.
1489
	 * @return The <code>WebSearchName</code> attribute.
1490
	 * @see MethodMapper
1491
	 */
1492
	@SuppressWarnings("unused")
1493
	private static String getWebSearchName(TaxonName taxonName) {
1494
		//TODO extensions?
1495
	    TaxonNameDefaultCacheStrategy strategy = getCacheStrategy(taxonName);
1496
		String result = strategy.getNameCache(taxonName);
1497
		return result;
1498
	}
1499

    
1500
    @SuppressWarnings("unused")     //used by mapper
1501
    private static String getFullName(TaxonBase<?> taxon) {
1502
        if (isMisappliedName(taxon)){
1503
            String result = getCacheStrategy(taxon.getName()).getNameCache(taxon.getName());
1504
            result = result + " " + getMisappliedNameAuthorship(taxon);
1505
            return result;
1506
        }else{
1507
            return getFullName(taxon.getName());
1508
        }
1509
    }
1510

    
1511
	/**
1512
	 * Returns the <code>FullName</code> attribute.
1513
	 * @param taxonName The {@link NonViralName NonViralName}.
1514
	 * @return The <code>FullName</code> attribute.
1515
	 * @see MethodMapper
1516
	 */
1517
    //used by mapper
1518
	private static String getFullName(TaxonName taxonName) {
1519
		//TODO extensions?
1520
		String result = getCacheStrategy(taxonName).getTitleCache(taxonName);
1521
		//misapplied names are now handled differently in getFullName(TaxonBase)
1522
//		Iterator<Taxon> taxa = taxonName.getTaxa().iterator();
1523
//		if (taxonName.getTaxa().size() >0){
1524
//			if (taxonName.getTaxa().size() == 1){
1525
//				Taxon taxon = taxa.next();
1526
//				if (isMisappliedName(taxon)){
1527
//					result = result + " " + getAuthorString(taxon);
1528
//				}
1529
//			}
1530
//		}
1531
		return result;
1532
	}
1533

    
1534
    @SuppressWarnings("unused")
1535
    private static String getGUID(TaxonName taxonName) {
1536
        UUID uuid = taxonName.getUuid();
1537
        String result = "NameUUID:" + uuid.toString();
1538
        return result;
1539
    }
1540

    
1541
    static boolean isFirstAbbrevTitle = true;
1542
	/**
1543
	 * Returns the SourceNameCache for the AdditionalSource table
1544
	 */
1545
	@SuppressWarnings("unused")
1546
	private static String getSourceNameCache(TaxonName taxonName) {
1547
		if (taxonName != null){
1548
			Reference nomRef = taxonName.getNomenclaturalReference();
1549
			if (nomRef != null ){
1550
			    if (isFirstAbbrevTitle){
1551
			        //#5388 is definetely not the correct ticket number
1552
			        logger.warn("Semantics of getAbbrevTitleCache has changed. Please check if output is still correct. See #5388");
1553
			        isFirstAbbrevTitle = false;
1554
			    }
1555
			    return nomRef.getAbbrevTitleCache();
1556
			}
1557
		}
1558
		return null;
1559
	}
1560

    
1561
	/**
1562
	 * Returns the nomenclatural reference which is the reference
1563
	 * including the detail (microreference).
1564
	 * @param taxonName The {@link TaxonName taxon name}.
1565
	 * @see MethodMapper
1566
	 */
1567
	@SuppressWarnings("unused")
1568
	private static String getNomRefString(TaxonName taxonName) {
1569
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1570
		if (ref == null){
1571
			return null;
1572
		}
1573
		String result = null;
1574
		EnumSet<PesiSource> sources = getSources(taxonName);
1575
		if(sources.contains(PesiSource.EM)){
1576
		    if (! ref.isProtectedAbbrevTitleCache()){
1577
		        ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1578
		    }
1579
		    result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1580
		}else if(sources.contains(PesiSource.FE)||sources.contains(PesiSource.IF) ){
1581
            //TODO still need to check if correct for FE + IF
1582
		    if (! ref.isProtectedAbbrevTitleCache()){
1583
                ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1584
            }
1585
            result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1586
            return result;   // according to SQL script
1587
		}else if(sources.contains(PesiSource.ERMS)) {
1588
            //result = null; //according to SQL script
1589
		}else{
1590
		    logger.warn("Source not yet supported");
1591
		}
1592
		return result;
1593
	}
1594

    
1595
	/**
1596
	 * Returns the <code>NameStatusFk</code> attribute.
1597
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1598
	 * @return The <code>NameStatusFk</code> attribute.
1599
	 * @see MethodMapper
1600
	 */
1601
	@SuppressWarnings("unused")
1602
	private static Integer getNameStatusFk(TaxonName taxonName) {
1603
		Integer result = null;
1604

    
1605
		NomenclaturalStatus status = getNameStatus(taxonName);
1606
		if (status != null) {
1607
			result = PesiTransformer.nomStatus2nomStatusFk(status.getType());
1608
		}
1609
		return result;
1610
	}
1611

    
1612
	/**
1613
	 * Returns the <code>NameStatusCache</code> attribute.
1614
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1615
	 * @return The <code>NameStatusCache</code> attribute.
1616
	 * @throws UndefinedTransformerMethodException
1617
	 * @see MethodMapper
1618
	 */
1619
	@SuppressWarnings("unused")
1620
	private static String getNameStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1621
		String result = null;
1622
		NomenclaturalStatus status = getNameStatus(taxonName);
1623
		if (status != null) {
1624
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1625
		}
1626
		return result;
1627
	}
1628

    
1629
	private static NomenclaturalStatus getNameStatus(TaxonName taxonName) {
1630
		try {
1631
			if (taxonName != null) {
1632
			    Set<NomenclaturalStatus> states = taxonName.getStatus();
1633
				if (states.size() >= 1) {
1634
				    if (states.size() > 1) {
1635
				        String statusStr = null;
1636
				        for (NomenclaturalStatus status: states){
1637
				            statusStr = CdmUtils.concat(",", statusStr, status.getType()== null? null:status.getType().getTitleCache());
1638
				        }
1639
				        //a known case is ad43508a-8a10-480a-8519-2a76de2c0a0f (Labiatae Juss.) from E+M
1640
	                    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);
1641
	                }
1642
				    NomenclaturalStatus status = states.iterator().next();
1643
					return status;
1644
				}
1645
			}
1646
		} catch (Exception e) {
1647
			e.printStackTrace();
1648
		}
1649
		return null;
1650
	}
1651

    
1652
	/**
1653
	 * Returns the <code>TaxonStatusFk</code> attribute.
1654
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1655
	 * @param state The {@link PesiExportState PesiExportState}.
1656
	 * @return The <code>TaxonStatusFk</code> attribute.
1657
	 * @see MethodMapper
1658
	 */
1659
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1660
		try {
1661
			return PesiTransformer.taxonBase2statusFk(taxon);
1662
		} catch (Exception e) {
1663
			e.printStackTrace();
1664
			return null;
1665
		}
1666
	}
1667

    
1668
	/**
1669
	 * Returns the <code>TaxonStatusCache</code> attribute.
1670
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1671
	 * @param state The {@link PesiExportState PesiExportState}.
1672
	 * @return The <code>TaxonStatusCache</code> attribute.
1673
	 * @throws UndefinedTransformerMethodException
1674
	 * @see MethodMapper
1675
	 */
1676
	@SuppressWarnings("unused")
1677
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1678
		return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1679
	}
1680

    
1681
    /**
1682
     * Returns the <code>TaxonFk1</code> attribute. It corresponds to a CDM <code>TaxonRelationship</code>.
1683
     * @param relationship The {@link RelationshipBase Relationship}.
1684
     * @param state The {@link PesiExportState PesiExportState}.
1685
     * @return The <code>TaxonFk1</code> attribute.
1686
     * @see MethodMapper
1687
     */
1688
    @SuppressWarnings("unused")
1689
    private static Integer getSynonym(Synonym synonym, PesiExportState state) {
1690
         return state.getDbId(synonym);
1691
    }
1692

    
1693
	/**
1694
	 * Returns the <code>TypeNameFk</code> attribute.
1695
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1696
	 * @param state The {@link PesiExportState PesiExportState}.
1697
	 * @return The <code>TypeNameFk</code> attribute.
1698
	 * @see MethodMapper
1699
	 */
1700
	private static Integer getTypeNameFk(TaxonName taxonName, PesiExportState state) {
1701
		Integer result = null;
1702
		if (taxonName != null) {
1703
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1704
			if (nameTypeDesignations.size() == 1) {
1705
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1706
				if (nameTypeDesignation != null) {
1707
					TaxonName typeName = nameTypeDesignation.getTypeName();
1708
					if (typeName != null) {
1709
					    if (typeName.getTaxonBases().isEmpty()){
1710
					        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());
1711
					    }else{
1712
					        result = state.getDbId(typeName);
1713
					    }
1714
					}
1715
				}
1716
			} else if (nameTypeDesignations.size() > 1) {
1717
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1718
			}
1719
		}
1720
		return result;
1721
	}
1722

    
1723
	/**
1724
	 * Returns the <code>TypeFullnameCache</code> attribute.
1725
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1726
	 * @return The <code>TypeFullnameCache</code> attribute.
1727
	 * @see MethodMapper
1728
	 */
1729
	@SuppressWarnings("unused")
1730
	private static String getTypeFullnameCache(TaxonName taxonName) {
1731
		String result = null;
1732

    
1733
		try {
1734
    		if (taxonName != null) {
1735
    			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1736
    			if (nameTypeDesignations.size() == 1) {
1737
    				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1738
    				if (nameTypeDesignation != null) {
1739
    					TaxonName typeName = nameTypeDesignation.getTypeName();
1740
    					if (typeName != null) {
1741
    						result = typeName.getTitleCache();
1742
    					}
1743
    				}
1744
    			} else if (nameTypeDesignations.size() > 1) {
1745
    				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1746
    			}
1747
    		}
1748
		} catch (Exception e) {
1749
			e.printStackTrace();
1750
		}
1751
		return result;
1752
	}
1753

    
1754

    
1755
	/**
1756
	 * Returns the <code>QualityStatusFk</code> attribute.
1757
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1758
	 * @return The <code>QualityStatusFk</code> attribute.
1759
	 * @see MethodMapper
1760
	 */
1761
	private static Integer getQualityStatusFk(TaxonName taxonName) {
1762
	    EnumSet<PesiSource> sources = getSources(taxonName);
1763
		return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1764
	}
1765

    
1766
	/**
1767
	 * Returns the <code>QualityStatusCache</code> attribute.
1768
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1769
	 * @return The <code>QualityStatusCache</code> attribute.
1770
	 * @throws UndefinedTransformerMethodException
1771
	 * @see MethodMapper
1772
	 */
1773
	@SuppressWarnings("unused")
1774
	private static String getQualityStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1775
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1776
	}
1777

    
1778

    
1779
	/**
1780
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1781
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1782
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1783
	 * @see MethodMapper
1784
	 */
1785
	//TODO seems not to be used
1786
	private static Integer getTypeDesignationStatusFk(TaxonName taxonName) {
1787
		Integer result = null;
1788

    
1789
		try {
1790
    		if (taxonName != null) {
1791
    			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1792
    			if (typeDesignations.size() == 1) {
1793
    				Object obj = typeDesignations.iterator().next().getTypeStatus();
1794
    				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1795
    				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1796
    			} else if (typeDesignations.size() > 1) {
1797
    				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1798
    			}
1799
    		}
1800
		} catch (Exception e) {
1801
			e.printStackTrace();
1802
		}
1803
		return result;
1804
	}
1805

    
1806
	/**
1807
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1808
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1809
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1810
	 * @see MethodMapper
1811
	 */
1812
	//TODO seems not to be used
1813
	private static String getTypeDesignationStatusCache(TaxonName taxonName) {
1814
		String result = null;
1815

    
1816
		try {
1817
    		if (taxonName != null) {
1818
    			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1819
    			if (typeDesignations.size() == 1) {
1820
    				Object obj = typeDesignations.iterator().next().getTypeStatus();
1821
    				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1822
    				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
1823
    			} else if (typeDesignations.size() > 1) {
1824
    				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1825
    			}
1826
    		}
1827
		} catch (Exception e) {
1828
			e.printStackTrace();
1829
		}
1830
		return result;
1831
	}
1832

    
1833
	/**
1834
	 * Returns the <code>FossilStatusFk</code> attribute.
1835
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1836
	 * @return The <code>FossilStatusFk</code> attribute.
1837
	 * @see MethodMapper
1838
	 */
1839
	@SuppressWarnings("unused")
1840
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
1841
		Integer result = null;
1842

    
1843
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
1844
		if (fossilStatuus.size() == 0){
1845
			return null;
1846
		}else if (fossilStatuus.size() > 1){
1847
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
1848
		}
1849
		String fossilStatus = fossilStatuus.iterator().next();
1850

    
1851
		int statusFk = state.getTransformer().fossilStatusCache2FossilStatusFk(fossilStatus);
1852
		return statusFk;
1853
	}
1854

    
1855
	/**
1856
	 * Returns the <code>FossilStatusCache</code> attribute.
1857
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1858
	 * @return The <code>FossilStatusCache</code> attribute.
1859
	 * @see MethodMapper
1860
	 */
1861
	@SuppressWarnings("unused")
1862
	private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
1863
		String result = null;
1864
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
1865
		if (fossilStatuus.size() == 0){
1866
			return null;
1867
		}
1868
		for (String strFossilStatus : fossilStatuus){
1869
			result = CdmUtils.concat(";", result, strFossilStatus);
1870
		}
1871
		return result;
1872
	}
1873

    
1874
	/**
1875
     * Returns the <code>sourceFk</code> attribute which is
1876
     * a link to a reference.
1877
     * @see #8796
1878
     * @return The <code>sourceFk</code> attribute.
1879
     * @see MethodMapper
1880
     */
1881
    @SuppressWarnings("unused")  //used by methodmapper
1882
    private static Integer getSourceFk(TaxonBase<?> taxonBase, PesiExportState state) {
1883
        if (taxonBase.getSec() != null){
1884
            return state.getDbId(taxonBase.getSec());
1885
        }else{
1886
            Set<IdentifiableSource> sources = getPesiSources(taxonBase);
1887
            for (IdentifiableSource source : sources){
1888
                Reference ref = source.getCitation();
1889
                if (ref != null){
1890
                    return state.getDbId(ref);
1891
                }
1892
            }
1893
        }
1894
        logger.warn("No source found for " + taxonBase.getTitleCache());
1895
        return null;
1896
    }
1897

    
1898
    /**
1899
     * Returns the <code>sourceFk</code> attribute which is
1900
     * a link to a reference.
1901
     *
1902
     * @return The <code>sourceFk</code> attribute.
1903
     * @see MethodMapper
1904
     * @see #8796
1905
     */
1906
    @SuppressWarnings("unused")  //used by methodmapper
1907
    private static Integer getSourceFk(TaxonName taxonName, PesiExportState state) {
1908
        //for now pure names (only coming from E+M) have no source
1909
        //according to SQL scripts (#8796)
1910
        return null;
1911
    }
1912

    
1913
	/**
1914
	 * Returns the <code>IdInSource</code> attribute.
1915
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1916
	 * @return The <code>IdInSource</code> attribute.
1917
	 * @see MethodMapper
1918
	 */
1919
	@SuppressWarnings("unused")
1920
	private static String getIdInSource(IdentifiableEntity<?> taxonName) {
1921
		String result = null;
1922

    
1923
		try {
1924
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
1925
			if (sources.size() > 1){
1926
				logger.warn("There is > 1 Pesi source. This is not yet handled.");
1927
			}
1928
			if (sources.size() == 0){
1929
				logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1930
			}
1931
			for (IdentifiableSource source : sources) {
1932
				Reference ref = source.getCitation();
1933
				UUID refUuid = ref.getUuid();
1934
				String idInSource = source.getIdInSource();
1935
				if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed)){
1936
					result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
1937
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
1938
					result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
1939
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
1940
					result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
1941
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
1942
					result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
1943
				}else{
1944
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");}
1945
				}
1946

    
1947
				String sourceIdNameSpace = source.getIdNamespace();
1948
				if (sourceIdNameSpace != null) {
1949
					if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
1950
						result =  idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
1951
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
1952
						result =  idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
1953
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
1954
						result =  idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
1955
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
1956
						result =  idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
1957
					}
1958
				}
1959
				if (result == null) {
1960
					logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
1961
				}
1962
			}
1963
		} catch (Exception e) {
1964
			e.printStackTrace();
1965
			logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
1966
		}
1967

    
1968
		if (result == null) {
1969
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1970
		}
1971
		return result;
1972
	}
1973

    
1974
	/**
1975
	 * Returns the idInSource for a given TaxonName only.
1976
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1977
	 * @return The idInSource.
1978
	 */
1979
	private static String getIdInSourceOnly(IdentifiableEntity<?> identEntity) {
1980
		String result = null;
1981

    
1982
		// Get the sources first
1983
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
1984

    
1985
		// Determine the idInSource
1986
		if (sources.size() == 1) {
1987
			IdentifiableSource source = sources.iterator().next();
1988
			if (source != null) {
1989
				result = source.getIdInSource();
1990
			}
1991
		} else if (sources.size() > 1) {
1992
			int count = 1;
1993
			result = "";
1994
			for (IdentifiableSource source : sources) {
1995
				result += source.getIdInSource();
1996
				if (count < sources.size()) {
1997
					result += "; ";
1998
				}
1999
				count++;
2000
			}
2001

    
2002
		}
2003

    
2004
		return result;
2005
	}
2006

    
2007
	/**
2008
	 * Returns the <code>GUID</code> attribute.
2009
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2010
	 * @return The <code>GUID</code> attribute.
2011
	 * @see MethodMapper
2012
	 */
2013
	private static String getGUID(TaxonBase<?> taxon) {
2014
		if (taxon.getLsid() != null ){
2015
			return taxon.getLsid().getLsid();
2016
		}else if (taxon.hasMarker(PesiTransformer.uuidMarkerGuidIsMissing, true)){
2017
			return null;
2018
		}else{
2019
			return taxon.getUuid().toString();
2020
		}
2021
	}
2022

    
2023
	/**
2024
	 * Returns the <code>DerivedFromGuid</code> attribute.
2025
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2026
	 * @return The <code>DerivedFromGuid</code> attribute.
2027
	 * @see MethodMapper
2028
	 */
2029
	@SuppressWarnings("unused")
2030
	private static String getDerivedFromGuid(TaxonBase<?> taxon) {
2031
		String result = null;
2032
		try {
2033
		// The same as GUID for now
2034
		result = getGUID(taxon);
2035
		} catch (Exception e) {
2036
			e.printStackTrace();
2037
		}
2038
		return result;
2039
	}
2040

    
2041
	/**
2042
	 * Returns the <code>CacheCitation</code> attribute.
2043
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2044
	 * @return The CacheCitation.
2045
	 * @see MethodMapper
2046
	 */
2047
	@SuppressWarnings("unused")
2048
	private static String getCacheCitation(TaxonBase<?> taxon) {
2049
		// !!! See also doPhaseUpdates
2050

    
2051
		TaxonName taxonName = taxon.getName();
2052
		String result = "";
2053
		//TODO implement anew for taxa
2054
		try {
2055
			EnumSet<PesiSource> sources = getSources(taxon);
2056
			//TODO what if 2 sources? In PESI 2014 they were pipe separated
2057
			//TODO why does ERMS use accessed through eu-nomen, while E+M uses accessed through E+M
2058
			if (sources.isEmpty()) {
2059
//				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2060
			} else if (sources.contains(PesiSource.ERMS)) {
2061
				//TODO check if correct, compare with PESI 2014
2062
			    Set<Extension> extensions = taxon.getExtensions();
2063
				for (Extension extension : extensions) {
2064
					if (extension.getType().equals(cacheCitationExtensionType)) {
2065
						result = extension.getValue();
2066
					}
2067
				}
2068
			} else if (sources.contains(PesiSource.EM)) {
2069
			    //TODO
2070
			    boolean isMisapplied = isMisappliedName(taxon);
2071
			    boolean isProParteSyn = isProParteOrPartialSynonym(taxon);
2072
			    Reference sec = null;
2073
			    if(!isMisapplied && !isProParteSyn){
2074
			        sec = taxon.getSec();
2075
			    }else if (isMisapplied){
2076
			        sec = getAcceptedTaxonForMisappliedName(taxon).getSec();
2077
			    }else if (isProParteSyn){
2078
                    sec = getAcceptedTaxonForProParteSynonym(taxon).getSec();
2079
                }
2080
			    if (sec == null){
2081
			        logger.warn("Sec could not be defined for taxon " + taxon.getTitleCache()+ "; " + taxon.getUuid());
2082
			    }
2083
			    String author = sec == null? "" : sec.getTitleCache();
2084
			    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
2085
			    String accessed = ". Accessed through: Euro+Med PlantBase at https://www.europlusmed.org/cdm_dataportal/taxon/";
2086
			    result = CdmUtils.removeTrailingDot(author)
2087
			            + ". " + CdmUtils.removeTrailingDot(webShowName)
2088
			            + accessed + taxon.getUuid();
2089
			} else {
2090
				//TODO check for IF + FE
2091

    
2092
			    String expertName = getExpertName(taxon);
2093
				String webShowName = getWebShowName(taxonName);
2094

    
2095
				// idInSource only
2096
				String idInSource = getIdInSourceOnly(taxonName);
2097

    
2098
				// build the cacheCitation
2099
				if (expertName != null) {
2100
					result += expertName + ". ";
2101
				} else {
2102
					if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
2103
				}
2104
				if (webShowName != null) {
2105
					result += webShowName + ". ";
2106
				} else {
2107
					logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2108
				}
2109

    
2110
				if (getOriginalDB(taxonName).equals("FaEu")) {
2111
					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
2112
				} else if (getOriginalDB(taxonName).equals("EM")) {
2113
					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
2114
				}
2115

    
2116
				if (idInSource != null) {
2117
					result += idInSource;
2118
				} else {
2119
					logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2120
				}
2121
			}
2122
		} catch (Exception e) {
2123
			e.printStackTrace();
2124
		}
2125

    
2126
		if (StringUtils.isBlank(result)) {
2127
			return null;
2128
		} else {
2129
			return result;
2130
		}
2131
	}
2132

    
2133
	/**
2134
	 * Returns the <code>OriginalDB</code> attribute.
2135
	 * @param identifiableEntity
2136
	 * @return The <code>OriginalDB</code> attribute.
2137
	 * @see MethodMapper
2138
	 */
2139
//	@SuppressWarnings("unused")
2140
	private static String getOriginalDB(IdentifiableEntity<?> identifiableEntity) {
2141
		EnumSet<PesiSource> sources  = getSources(identifiableEntity);
2142
		return PesiTransformer.getOriginalDbBySources(sources);
2143
	}
2144

    
2145
	/**
2146
	 * Returns the <code>ExpertName</code> attribute.
2147
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2148
	 * @return The <code>ExpertName</code> attribute.
2149
	 * @see MethodMapper
2150
	 */
2151
	@SuppressWarnings("unused")  //for some reason it is also called by getCacheCitation
2152
	private static String getExpertName(TaxonBase<?> taxon) {
2153
		try {
2154
		    String result = null;
2155
    		if(expertNameExtensionType!=null){  //some databases do not have this extension type
2156
    		    Set<Extension> extensions = taxon.getExtensions();
2157
    		    for (Extension extension : extensions) {
2158
    		        if (extension.getType().equals(expertNameExtensionType)) {
2159
    		            result = extension.getValue();
2160
    		        }
2161
    		    }
2162
    		}
2163
    		if (getPesiSources(taxon).contains(PesiSource.EM)){
2164
                return taxon.getSec().getTitleCache();
2165
            }
2166
            return null;
2167
		} catch (Exception e) {
2168
			e.printStackTrace();
2169
			return null;
2170
		}
2171
	}
2172

    
2173
	//TODO change to ExpertGUID
2174
	private static Integer getExpertFk(Reference reference, PesiExportState state) {
2175
		Integer result = state.getDbId(reference);
2176
		return result;
2177
	}
2178

    
2179
	/**
2180
	 * Returns the <code>SpeciesExpertName</code> attribute.
2181
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2182
	 * @return The <code>SpeciesExpertName</code> attribute.
2183
	 * @see MethodMapper
2184
	 */
2185
	@SuppressWarnings("unused")
2186
	private static String getSpeciesExpertName(TaxonBase<?> taxon) {
2187
		try {
2188
    		Set<Extension> extensions = taxon.getExtensions();
2189
    		if(speciesExpertNameExtensionType != null){  //some databases do not have this extension type
2190
                for (Extension extension : extensions) {
2191
        			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2192
        				return extension.getValue();
2193
        			}
2194
        		}
2195
    		}
2196
    		if (getPesiSources(taxon).contains(PesiSource.EM)){
2197
    		    return taxon.getSec().getTitleCache();
2198
            }
2199
    		return null;
2200
		} catch (Exception e) {
2201
			e.printStackTrace();
2202
			return null;
2203
		}
2204
	}
2205

    
2206
	/**
2207
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2208
	 * @param reference The {@link Reference Reference}.
2209
	 * @param state The {@link PesiExportState PesiExportState}.
2210
	 * @return The <code>SpeciesExpertFk</code> attribute.
2211
	 * @see MethodMapper
2212
	 */
2213
	//TODO should be changed to SpeciesExpertGUID
2214
	private static Integer getSpeciesExpertFk(Reference reference, PesiExportState state) {
2215
		Integer result = state.getDbId(reference);
2216
		return result;
2217
	}
2218

    
2219
	protected static TaxonNameDefaultCacheStrategy getCacheStrategy(TaxonName taxonName) {
2220
		taxonName = CdmBase.deproxy(taxonName);
2221
		TaxonNameDefaultCacheStrategy cacheStrategy;
2222
		if (taxonName.isZoological()){
2223
			cacheStrategy = zooNameStrategy;
2224
		}else if (taxonName.isBotanical()) {
2225
			cacheStrategy = nonViralNameStrategy;
2226
		}else if (taxonName.isNonViral()) {
2227
			cacheStrategy = nonViralNameStrategy;
2228
		}else if (taxonName.isBacterial()) {
2229
			cacheStrategy = nonViralNameStrategy;
2230
		}else{
2231
			logger.error("Unhandled taxon name type. Can't define strategy class");
2232
			cacheStrategy = nonViralNameStrategy;
2233
		}
2234
		return cacheStrategy;
2235
	}
2236

    
2237
	/**
2238
	 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2239
	 * @param relationship The {@link RelationshipBase Relationship}.
2240
	 * @return The <code>RelTaxonQualifierFk</code> attribute.
2241
	 * @see MethodMapper
2242
	 */
2243
	@SuppressWarnings("unused")
2244
	private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2245
		return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2246
	}
2247

    
2248
    //TODO still in use?
2249
    private static String getSynonymTypeCache(Synonym synonym, PesiExportState state) {
2250
        String result = null;
2251
        NomenclaturalCode code = null;
2252
        code = CdmBase.deproxy(synonym, Synonym.class).getAcceptedTaxon().getName().getNameType();
2253

    
2254
        if (code != null) {
2255
            result = state.getConfig().getTransformer().getCacheBySynonymType(synonym, code);
2256
        } else {
2257
            logger.error("NomenclaturalCode is NULL while creating the following synonym: " + synonym.getUuid());
2258
        }
2259
        return result;
2260
    }
2261

    
2262
// ********************************** MAPPINGS ********************************/
2263

    
2264
	/**
2265
	 * Returns the CDM to PESI specific export mappings.
2266
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2267
	 */
2268
	private PesiExportMapping getMapping() {
2269
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2270

    
2271
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2272
		mapping.addMapper(MethodMapper.NewInstance("SourceFk", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2273
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2274
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2275

    
2276
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2277

    
2278
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2279
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2280
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because misapplied names are handled differently
2281
		mapping.addMapper(MethodMapper.NewInstance("FullName", this));    //For Taxon because misapplied names are handled differently
2282
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2283

    
2284
		// DisplayName
2285
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2286

    
2287
		// FossilStatus (Fk, Cache)
2288
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2289
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2290

    
2291
		//handled by name mapping
2292
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2293
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2294

    
2295
		//experts
2296
//		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2297
		mapping.addMapper(MethodMapper.NewInstance("SpeciesExpertName", this, TaxonBase.class));
2298
//		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
2299
//		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2300
		mapping.addMapper(MethodMapper.NewInstance("ExpertName", this, TaxonBase.class));
2301

    
2302
		//ParentTaxonFk handled in Phase02 now
2303
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2304

    
2305
		addNameMappers(mapping);
2306

    
2307
		return mapping;
2308
	}
2309

    
2310
	/**
2311
	 * Returns the CDM to PESI specific export mappings.
2312
	 * @param state
2313
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2314
	 * @throws UndefinedTransformerMethodException
2315
	 */
2316
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2317
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2318

    
2319
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2320

    
2321
		mapping.addMapper(MethodMapper.NewInstance("SourceFk", this, TaxonName.class, PesiExportState.class));  //for now is only null
2322
        mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonName.class));
2323
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonName.class));
2324
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonName.class, PesiExportState.class));
2325
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER, PesiTransformer.T_STATUS_UNACCEPTED));
2326
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR, state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2327
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
2328
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonName.class));
2329
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonName.class));
2330
		mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonName.class));
2331

    
2332
		// DisplayName
2333
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonName.class));
2334

    
2335
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2336
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2337

    
2338
		addNameMappers(mapping);
2339
		return mapping;
2340
	}
2341

    
2342
	private void addNameMappers(PesiExportMapping mapping) {
2343

    
2344
	    //epithets
2345
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2346
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2347
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2348
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2349

    
2350
		//full name
2351
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2352
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonName.class));
2353

    
2354
		//nom ref
2355
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonName.class));
2356

    
2357
		//status
2358
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonName.class));
2359
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonName.class, PesiExportState.class));
2360
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonName.class));
2361
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonName.class, PesiExportState.class));
2362

    
2363
		//types
2364
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
2365
		//TypeNameFk handled in Phase3
2366

    
2367
		//supplemental
2368
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2369
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2370

    
2371
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2372

    
2373
	}
2374

    
2375
	private PesiExportMapping getSynRelMapping() {
2376
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2377
		logger.warn("SynRelMapping currently not implemented. Needs to be checked");
2378

    
2379
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getSynonym", Synonym.class, PesiExportState.class));
2380
		mapping.addMapper(DbObjectMapper.NewInstance("acceptedTaxon", "TaxonFk2"));
2381
		mapping.addMapper(DbObjectMapper.NewInstance("type", "RelTaxonQualifierFk"));
2382
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this.getClass(), "getSynonymTypeCache", Synonym.class, PesiExportState.class));
2383
		// TODO
2384
//		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2385

    
2386
		return mapping;
2387
	}
2388

    
2389
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state) {
2390
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2391

    
2392
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2393
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2394

    
2395
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2396
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2397
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonName.class));
2398

    
2399
		//we have only nomenclatural references here
2400
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2401
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2402

    
2403
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2404

    
2405
		return mapping;
2406
	}
2407

    
2408

    
2409
    @Override
2410
    protected boolean doCheck(PesiExportState state) {
2411
        return true;
2412
    }
2413

    
2414
    @Override
2415
    protected boolean isIgnore(PesiExportState state) {
2416
        return ! state.getConfig().isDoTaxa();
2417
    }
2418
}
(13-13/14)