Project

General

Profile

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

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

    
27
import org.apache.commons.lang.StringUtils;
28
import org.apache.log4j.Logger;
29
import org.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.
334
		//This may be changed if we move validation to Phase03 or later
335
//		Integer rankFk = getRankFk(taxonName, taxonName.getNameType());
336
//		if (rankFk == null) {
337
//			logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
338
//		} else {
339

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

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

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

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

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

    
404
		insertBiota(state);
405

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

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

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

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

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

    
445
		return success;
446
	}
447

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

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

    
476
		addValuelessTaxonToKingdomMap(state);
477

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
565
        return success;
566
    }
567

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
832

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1052

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

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

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

    
1080
			if (kingdomFk != null) {
1081

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1196
		return true;
1197
	}
1198

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1702

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

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

    
1726

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1911
		}
1912

    
1913
		return result;
1914
	}
1915

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

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

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

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

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

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

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

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

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

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

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

    
2056
	//TODO change to ExpertGUID
2057
	private static Integer getExpertFk(Reference reference, PesiExportState state) {
2058
		Integer result = state.getDbId(reference);
2059
		return result;
2060
	}
2061

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

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

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

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

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

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

    
2145
// ********************************** MAPPINGS ********************************/
2146

    
2147
	/**
2148
	 * Returns the CDM to PESI specific export mappings.
2149
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2150
	 */
2151
	private PesiExportMapping getMapping() {
2152
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2153

    
2154
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2155
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2156
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2157
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2158

    
2159
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2160

    
2161
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2162
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2163
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misapllied Names are handled differently
2164
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2165

    
2166
		// DisplayName
2167
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2168

    
2169
		// FossilStatus (Fk, Cache)
2170
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2171
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2172

    
2173
		//handled by name mapping
2174
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2175
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2176

    
2177
		//experts
2178
//		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2179
		mapping.addMapper(MethodMapper.NewInstance("SpeciesExpertName", this, TaxonBase.class));
2180
//		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
2181
//		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2182
		mapping.addMapper(MethodMapper.NewInstance("ExpertName", this, TaxonBase.class));
2183

    
2184
		//ParentTaxonFk handled in Phase02 now
2185
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2186

    
2187
		addNameMappers(mapping);
2188

    
2189
		return mapping;
2190
	}
2191

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

    
2201
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2202

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

    
2212
		// DisplayName
2213
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonName.class));
2214

    
2215
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2216
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2217

    
2218
		addNameMappers(mapping);
2219
		return mapping;
2220
	}
2221

    
2222
	private void addNameMappers(PesiExportMapping mapping) {
2223

    
2224
	    //epithets
2225
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2226
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2227
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2228
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2229

    
2230
		//full name
2231
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2232
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonName.class));
2233
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonName.class));
2234

    
2235
		//nom ref
2236
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonName.class));
2237

    
2238
		//status
2239
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonName.class));
2240
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonName.class, PesiExportState.class));
2241
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonName.class));
2242
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonName.class, PesiExportState.class));
2243

    
2244
		//types
2245
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
2246
		//TypeNameFk handled in Phase3
2247

    
2248
		//supplemental
2249
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2250
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2251

    
2252
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2253

    
2254
	}
2255

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

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

    
2267
		return mapping;
2268
	}
2269

    
2270
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state) {
2271
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2272

    
2273
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2274
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2275

    
2276
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2277
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2278
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonName.class));
2279

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

    
2284
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2285

    
2286
		return mapping;
2287
	}
2288

    
2289

    
2290
    @Override
2291
    protected boolean doCheck(PesiExportState state) {
2292
        return true;
2293
    }
2294

    
2295
    @Override
2296
    protected boolean isIgnore(PesiExportState state) {
2297
        return ! state.getConfig().isDoTaxa();
2298
    }
2299
}
(13-13/14)