Project

General

Profile

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
142
			initPreparedStatements(state);
143

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

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

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

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

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

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

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

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

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

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

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

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

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

    
198

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

    
204
		initParentFkStatement(state);
205
	}
206

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
321
		return success;
322
	}
323

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

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

    
333
		//as kingdomFk can not be defined in Phase 01 the below code was switched to use the CDM rank. This may be changed if we move validation to Phase03 or later
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 (taxonName.getRank().equals(Rank.SUBSPECIES())) {
345
				// The accepted taxon two rank levels above should be of rank subgenus
346
				ancestorLevel  = 2;
347
			}
348
			if (taxonName.getRank().equals(Rank.SPECIES())) {
349
				// The accepted taxon one rank level above should be of rank subgenus
350
				ancestorLevel = 1;
351
			}
352
			if (ancestorLevel > 0) {
353
				if (validateAncestorOfSpecificRank(taxon, ancestorLevel, Rank.SUBGENUS())) {
354
					// The child (species or subspecies) of this parent (subgenus) has to have an infraGenericEpithet
355
					if (infraGenericEpithet == null) {
356
						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() + ")");
357
						// maybe the taxon could be named here
358
					}
359
				}
360
			}
361

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

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

    
397
		// Get the limit for objects to save within a single transaction.
398
		int limit = state.getConfig().getLimitSave();
399

    
400
		insertBiota(state);
401

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

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

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

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

    
438
		// Commit transaction
439
		commitTransaction(txStatus);
440

    
441
		return success;
442
	}
443

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

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

    
472
		addValuelessTaxonToKingdomMap(state);
473

    
474
		// Get the limit for objects to save within a single transaction.
475
		int limit = state.getConfig().getLimitSave();
476

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

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

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

    
501
				doCount(count++, modCount, pluralString);
502
				Integer typeNameFk = getTypeNameFk(taxonName, state);
503
				Integer kingdomFk = findKingdomIdFromTreeIndex(taxon, state);
504
				Integer rankFk = getRankFk(taxonName, kingdomFk);
505

    
506
			    invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, state.getDbId(taxon),
507
						typeNameFk, kingdomFk, rankFk, state);
508
			}
509

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

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

    
524
		// Commit transaction
525
		commitTransaction(txStatus);
526

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

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

    
546
    // 4th round: Add TreeIndex to each taxon
547
    private boolean doPhase04(PesiExportState state) {
548
        boolean success = true;
549

    
550
        logger.info("PHASE 4: Make TreeIndex ... ");
551

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

    
557
        state.getConfig().getDestination().update("EXEC dbo.recalculateallstoredpaths");
558

    
559
        logger.info("PHASE 4: Make TreeIndex DONE");
560

    
561
        return success;
562
    }
563

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

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

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

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

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

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

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

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

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

    
679
		// Determine the count of elements in data warehouse database table Taxon
680
		currentTaxonId = determineTaxonCount(state);
681
		currentTaxonId++;
682

    
683
		count = 0;
684
		pastCount = 0;
685
		int pageSize = limit/10;
686
		int pageNumber = 1;
687
		String inferredSynonymPluralString = "Inferred Synonyms";
688

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

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

    
698
		    Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
699

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

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

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

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

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

    
732
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
733
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
734
					synRelMapping, taxonList));
735

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

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

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

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

    
761
		taxonList = null;
762
//		logger.warn("Taking snapshot at the end of phase 5 of taxonExport");
763
//		ProfilerController.memorySnapshot();
764

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

    
774
	private Map<Integer, TaxonName> createInferredSynonymsForTaxonList(PesiExportState state,
775
			PesiExportMapping mapping, PesiExportMapping synRelMapping,	 List<TaxonBase> taxonList) {
776

    
777
		Taxon acceptedTaxon;
778
		Classification classification = null;
779
		List<Synonym> inferredSynonyms = null;
780
		boolean localSuccess = true;
781

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

    
784
		for (TaxonBase<?> taxonBase : taxonList) {
785

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

    
790
				if (taxonName.isZoological()) {
791
					kingdomFk = findKingdomIdFromTreeIndex(taxonBase, state);
792

    
793
					Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
794
					TaxonNode singleNode = null;
795

    
796
					if (taxonNodes.size() > 0) {
797
						// Determine the classification of the current TaxonNode
798

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

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

    
828

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

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

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

    
880
	/**
881
	 * Handles names that do not appear in taxa
882
	 * @param state
883
	 * @param mapping
884
	 */
885
	private boolean doPhase01b_Names(PesiExportState state, PesiExportMapping additionalSourceMapping) {
886

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1048

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

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

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

    
1076
			if (kingdomFk != null) {
1077

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1192
		return true;
1193
	}
1194

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

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

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

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

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

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

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

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

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

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

    
1353
			if (taxonName == null){
1354
				logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1355
			}else if (! isNonViralName){
1356
				logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1357
			}
1358

    
1359
			if (StringUtils.isBlank(result)) {
1360
				return null;
1361
			} else {
1362
				return result;
1363
			}
1364
		} catch (Exception e) {
1365
			e.printStackTrace();
1366
			return null;
1367
		}
1368
	}
1369

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

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

    
1401
	@SuppressWarnings("unused")
1402
	private static String getGUID(TaxonName taxonName) {
1403
		UUID uuid = taxonName.getUuid();
1404
		String result = "NameUUID:" + uuid.toString();
1405
		return result;
1406
	}
1407

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

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

    
1437
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1438
			String result = cacheStrategy.getTitleCache(taxonName, tagRules);
1439
			return result;
1440
		}
1441
	}
1442

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

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

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

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

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

    
1546
		NomenclaturalStatus status = getNameStatus(taxonName);
1547
		if (status != null) {
1548
			result = PesiTransformer.nomStatus2nomStatusFk(status.getType());
1549
		}
1550
		return result;
1551
	}
1552

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

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

    
1596
		try {
1597
//			if (isMisappliedName(taxon)) {
1598
//				Synonym synonym = Synonym.NewInstance(null, null);
1599
//
1600
//				// This works as long as only the instance is important to differentiate between TaxonStatus.
1601
//				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in datawarehouse now.
1602
//			} else {
1603
		    //this should work now, the method itself distinguishes MAN etc.
1604
				result = PesiTransformer.taxonBase2statusFk(taxon);
1605
//			}
1606
		} catch (Exception e) {
1607
			e.printStackTrace();
1608
		}
1609
		return result;
1610
	}
1611

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

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

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

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

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

    
1698

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

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

    
1722

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

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

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

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

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

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

    
1795
		int statusFk = state.getTransformer().fossilStatusCache2FossilStatusFk(fossilStatus);
1796
		return statusFk;
1797
	}
1798

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

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

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

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

    
1873
		if (result == null) {
1874
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1875
		}
1876
		return result;
1877
	}
1878

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

    
1887
		// Get the sources first
1888
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
1889

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

    
1907
		}
1908

    
1909
		return result;
1910
	}
1911

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

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

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

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

    
1974
				// idInSource only
1975
				String idInSource = getIdInSourceOnly(taxonName);
1976

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

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

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

    
2005
		if (StringUtils.isBlank(result)) {
2006
			return null;
2007
		} else {
2008
			return result;
2009
		}
2010
	}
2011

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

    
2024
	/**
2025
	 * Returns the <code>ExpertName</code> attribute.
2026
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2027
	 * @return The <code>ExpertName</code> attribute.
2028
	 * @see MethodMapper
2029
	 */
2030
	@SuppressWarnings("unused")  //for some reason it is also called by getCacheCitation
2031
	private static String getExpertName(TaxonBase<?> taxon) {
2032
		try {
2033
		    String result = null;
2034
    		if(expertNameExtensionType!=null){  //some databases do not have this extension type
2035
    		    Set<Extension> extensions = taxon.getExtensions();
2036
    		    for (Extension extension : extensions) {
2037
    		        if (extension.getType().equals(expertNameExtensionType)) {
2038
    		            result = extension.getValue();
2039
    		        }
2040
    		    }
2041
    		}
2042
    		if (getPesiSources(taxon).contains(PesiSource.EM)){
2043
                return taxon.getSec().getTitleCache();
2044
            }
2045
            return null;
2046
		} catch (Exception e) {
2047
			e.printStackTrace();
2048
			return null;
2049
		}
2050
	}
2051

    
2052
	//TODO change to ExpertGUID
2053
	private static Integer getExpertFk(Reference reference, PesiExportState state) {
2054
		Integer result = state.getDbId(reference);
2055
		return result;
2056
	}
2057

    
2058
	/**
2059
	 * Returns the <code>SpeciesExpertName</code> attribute.
2060
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2061
	 * @return The <code>SpeciesExpertName</code> attribute.
2062
	 * @see MethodMapper
2063
	 */
2064
	@SuppressWarnings("unused")
2065
	private static String getSpeciesExpertName(TaxonBase<?> taxon) {
2066
		try {
2067
    		Set<Extension> extensions = taxon.getExtensions();
2068
    		if(speciesExpertNameExtensionType!=null){  //some databases do not have this extension type
2069
                for (Extension extension : extensions) {
2070
        			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2071
        				return extension.getValue();
2072
        			}
2073
        		}
2074
    		}
2075
    		if (getPesiSources(taxon).contains(PesiSource.EM)){
2076
    		    return taxon.getSec().getTitleCache();
2077
            }
2078
    		return null;
2079
		} catch (Exception e) {
2080
			e.printStackTrace();
2081
			return null;
2082
		}
2083
	}
2084

    
2085
	/**
2086
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2087
	 * @param reference The {@link Reference Reference}.
2088
	 * @param state The {@link PesiExportState PesiExportState}.
2089
	 * @return The <code>SpeciesExpertFk</code> attribute.
2090
	 * @see MethodMapper
2091
	 */
2092
	//TODO should be changed to SpeciesExpertGUID
2093
	private static Integer getSpeciesExpertFk(Reference reference, PesiExportState state) {
2094
		Integer result = state.getDbId(reference);
2095
		return result;
2096
	}
2097

    
2098
	protected static TaxonNameDefaultCacheStrategy getCacheStrategy(TaxonName taxonName) {
2099
		taxonName = CdmBase.deproxy(taxonName);
2100
		TaxonNameDefaultCacheStrategy cacheStrategy;
2101
		if (taxonName.isZoological()){
2102
			cacheStrategy = zooNameStrategy;
2103
		}else if (taxonName.isBotanical()) {
2104
			cacheStrategy = nonViralNameStrategy;
2105
		}else if (taxonName.isNonViral()) {
2106
			cacheStrategy = nonViralNameStrategy;
2107
		}else if (taxonName.isBacterial()) {
2108
			cacheStrategy = nonViralNameStrategy;
2109
		}else{
2110
			logger.error("Unhandled taxon name type. Can't define strategy class");
2111
			cacheStrategy = nonViralNameStrategy;
2112
		}
2113
		return cacheStrategy;
2114
	}
2115

    
2116
	/**
2117
	 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2118
	 * @param relationship The {@link RelationshipBase Relationship}.
2119
	 * @return The <code>RelTaxonQualifierFk</code> attribute.
2120
	 * @see MethodMapper
2121
	 */
2122
	@SuppressWarnings("unused")
2123
	private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2124
		return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2125
	}
2126

    
2127
    //TODO still in use?
2128
    private static String getSynonymTypeCache(Synonym synonym, PesiExportState state) {
2129
        String result = null;
2130
        NomenclaturalCode code = null;
2131
        code = CdmBase.deproxy(synonym, Synonym.class).getAcceptedTaxon().getName().getNameType();
2132

    
2133
        if (code != null) {
2134
            result = state.getConfig().getTransformer().getCacheBySynonymType(synonym, code);
2135
        } else {
2136
            logger.error("NomenclaturalCode is NULL while creating the following synonym: " + synonym.getUuid());
2137
        }
2138
        return result;
2139
    }
2140

    
2141
// ********************************** MAPPINGS ********************************/
2142

    
2143
	/**
2144
	 * Returns the CDM to PESI specific export mappings.
2145
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2146
	 */
2147
	private PesiExportMapping getMapping() {
2148
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2149

    
2150
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2151
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2152
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2153
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2154

    
2155
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2156

    
2157
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2158
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2159
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misapllied Names are handled differently
2160
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2161

    
2162
		// DisplayName
2163
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2164

    
2165
		// FossilStatus (Fk, Cache)
2166
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2167
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2168

    
2169
		//handled by name mapping
2170
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2171
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2172

    
2173
		//experts
2174
//		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2175
		mapping.addMapper(MethodMapper.NewInstance("SpeciesExpertName", this, TaxonBase.class));
2176
//		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
2177
//		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2178
		mapping.addMapper(MethodMapper.NewInstance("ExpertName", this, TaxonBase.class));
2179

    
2180
		//ParentTaxonFk handled in Phase02 now
2181
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2182

    
2183
		addNameMappers(mapping);
2184

    
2185
		return mapping;
2186
	}
2187

    
2188
	/**
2189
	 * Returns the CDM to PESI specific export mappings.
2190
	 * @param state
2191
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2192
	 * @throws UndefinedTransformerMethodException
2193
	 */
2194
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2195
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2196

    
2197
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2198

    
2199
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonName.class));
2200
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonName.class));
2201
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonName.class, PesiExportState.class));
2202
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER, PesiTransformer.T_STATUS_UNACCEPTED));
2203
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR, state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2204
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
2205
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonName.class));
2206
		mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonName.class));
2207

    
2208
		// DisplayName
2209
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonName.class));
2210

    
2211
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2212
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2213

    
2214
		addNameMappers(mapping);
2215
		return mapping;
2216
	}
2217

    
2218
	private void addNameMappers(PesiExportMapping mapping) {
2219

    
2220
	    //epithets
2221
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2222
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2223
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2224
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2225

    
2226
		//full name
2227
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2228
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonName.class));
2229
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonName.class));
2230

    
2231
		//nom ref
2232
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonName.class));
2233

    
2234
		//status
2235
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonName.class));
2236
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonName.class, PesiExportState.class));
2237
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonName.class));
2238
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonName.class, PesiExportState.class));
2239

    
2240
		//types
2241
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
2242
		//TypeNameFk handled in Phase3
2243

    
2244
		//supplemental
2245
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2246
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2247

    
2248
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2249

    
2250
	}
2251

    
2252
	private PesiExportMapping getSynRelMapping() {
2253
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2254
		logger.warn("SynRelMapping currently not implemented. Needs to be checked");
2255

    
2256
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getSynonym", Synonym.class, PesiExportState.class));
2257
		mapping.addMapper(DbObjectMapper.NewInstance("acceptedTaxon", "TaxonFk2"));
2258
		mapping.addMapper(DbObjectMapper.NewInstance("type", "RelTaxonQualifierFk"));
2259
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this.getClass(), "getSynonymTypeCache", Synonym.class, PesiExportState.class));
2260
		// TODO
2261
//		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2262

    
2263
		return mapping;
2264
	}
2265

    
2266
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state) {
2267
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2268

    
2269
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2270
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2271

    
2272
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2273
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2274
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonName.class));
2275

    
2276
		//we have only nomenclatural references here
2277
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2278
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2279

    
2280
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2281

    
2282
		return mapping;
2283
	}
2284

    
2285

    
2286
    @Override
2287
    protected boolean doCheck(PesiExportState state) {
2288
        return true;
2289
    }
2290

    
2291
    @Override
2292
    protected boolean isIgnore(PesiExportState state) {
2293
        return ! state.getConfig().isDoTaxa();
2294
    }
2295
}
(13-13/14)