Project

General

Profile

Download (88.1 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.BitSet;
18
import java.util.HashMap;
19
import java.util.Iterator;
20
import java.util.List;
21
import java.util.Map;
22
import java.util.Set;
23
import java.util.UUID;
24
import java.util.regex.Matcher;
25
import java.util.regex.Pattern;
26

    
27
import org.apache.commons.lang.StringUtils;
28
import org.apache.log4j.Logger;
29
import org.joda.time.DateTime;
30
import org.joda.time.format.DateTimeFormat;
31
import org.joda.time.format.DateTimeFormatter;
32
import org.springframework.stereotype.Component;
33
import org.springframework.transaction.TransactionStatus;
34

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

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

    
97
    private static final long serialVersionUID = -3412722058790200078L;
98
    private static final Logger logger = Logger.getLogger(PesiTaxonExport.class);
99

    
100
	private static final Class<? extends CdmBase> standardMethodParameter = TaxonBase.class;
101

    
102
	private static int modCount = 1000;
103
	private static final String dbTableName = "Taxon";
104
	private static final String dbTableNameSynRel = "RelTaxon";
105
	private static final String dbTableAdditionalSourceRel = "AdditionalTaxonSource";
106

    
107
	private static final String pluralString = "Taxa";
108
	private static final String parentPluralString = "Taxa";
109

    
110
	private PreparedStatement parentTaxonFk_TreeIndex_KingdomFkStmt;
111
	private PreparedStatement parentTaxonFkStmt;
112
	private PreparedStatement rankTypeExpertsUpdateStmt;
113
	private PreparedStatement rankUpdateStmt;
114
	private Integer kingdomFk;
115
	private AnnotationType treeIndexAnnotationType;
116
	private static ExtensionType lastActionExtensionType;
117
	private static ExtensionType lastActionDateExtensionType;
118
	private static ExtensionType expertNameExtensionType;
119
	private static ExtensionType speciesExpertNameExtensionType;
120
	private static ExtensionType cacheCitationExtensionType;
121
	public static TaxonNameDefaultCacheStrategy zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
122
	public static TaxonNameDefaultCacheStrategy nonViralNameStrategy = TaxonNameDefaultCacheStrategy.NewInstance();
123
	private static int currentTaxonId;
124

    
125
	protected AnnotationType getTreeIndexAnnotationType() {
126
		return treeIndexAnnotationType;
127
	}
128

    
129
	protected void setTreeIndexAnnotationType(AnnotationType treeIndexAnnotationType) {
130
		this.treeIndexAnnotationType = treeIndexAnnotationType;
131
	}
132

    
133
	enum NamePosition {
134
		beginning,
135
		end,
136
		between,
137
		alone,
138
		nowhere
139
	}
140

    
141
	public PesiTaxonExport() {
142
		super();
143
	}
144

    
145
	@Override
146
	public Class<? extends CdmBase> getStandardMethodParameter() {
147
		return standardMethodParameter;
148
	}
149

    
150
	@Override
151
	protected void doInvoke(PesiExportState state) {
152
		try {
153
			logger.info("*** Started Making " + pluralString + " ...");
154

    
155
			initPreparedStatements(state);
156

    
157
			// Stores whether this invoke was successful or not.
158
			boolean success = true;
159

    
160
			// PESI: Clear the database table Taxon.
161
			doDelete(state);
162

    
163
			// Get specific mappings: (CDM) Taxon -> (PESI) Taxon
164
			PesiExportMapping mapping = getMapping();
165
			PesiExportMapping synonymRelMapping = getSynRelMapping();
166
			PesiExportMapping additionalSourceMapping = getAdditionalSourceMapping(state);
167

    
168
			// Initialize the db mapper
169
			mapping.initialize(state);
170
			synonymRelMapping.initialize(state);
171
			additionalSourceMapping.initialize(state);
172

    
173
			// Find extensionTypes
174
			lastActionExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtLastAction);
175
			lastActionDateExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtLastActionDate);
176
			expertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
177
			speciesExpertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtSpeciesExpertName);
178
			cacheCitationExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtCacheCitation);
179

    
180
			//Export Taxa..
181
			success &= doPhase01(state, mapping, additionalSourceMapping);
182

    
183
			//"PHASE 1b: Handle names without taxa ...
184
			success &= doNames(state, additionalSourceMapping);
185

    
186
			// 2nd Round: Add ParentTaxonFk to each taxon
187
			success &= doPhase02(state);
188

    
189
			//PHASE 3: Add Rank data, KingdomFk, TypeNameFk ...
190
			success &= doPhase03(state);
191

    
192
			// 4nd Round: Add TreeIndex to each taxon
193
			success &= doPhase04(state);
194

    
195
			//"PHASE 5: Creating Inferred Synonyms...
196
			success &= doPhase05(state, mapping, synonymRelMapping);
197

    
198
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
199

    
200
			if (!success){
201
				state.getResult().addError("An error occurred in PesiTaxonExport.doInvoke. Success = false");
202
			}
203
			return;
204
		} catch (Exception e) {
205
			e.printStackTrace();
206
			logger.error(e.getMessage());
207
			state.getResult().addException(e);
208
		}
209
	}
210

    
211

    
212
	private void initPreparedStatements(PesiExportState state) throws SQLException {
213
		initTreeIndexStatement(state);
214
		initRankExpertsUpdateStmt(state);
215
		initRankUpdateStatement(state);
216

    
217
		initParentFkStatement(state);
218
	}
219

    
220
	// Prepare TreeIndex-And-KingdomFk-Statement
221
	private void initTreeIndexStatement(PesiExportState state) throws SQLException {
222
		Connection connection = state.getConfig().getDestination().getConnection();
223
		String parentTaxonFk_TreeIndex_KingdomFkSql = "UPDATE Taxon SET ParentTaxonFk = ?, TreeIndex = ? WHERE TaxonId = ?";
224
		parentTaxonFk_TreeIndex_KingdomFkStmt = connection.prepareStatement(parentTaxonFk_TreeIndex_KingdomFkSql);
225
	}
226

    
227
	// Prepare TreeIndex-And-KingdomFk-Statement
228
	private void initParentFkStatement(PesiExportState state) throws SQLException {
229
		Connection connection = state.getConfig().getDestination().getConnection();
230
		String parentTaxonFkSql = "UPDATE Taxon SET ParentTaxonFk = ? WHERE TaxonId = ?";
231
		parentTaxonFkStmt = connection.prepareStatement(parentTaxonFkSql);
232
	}
233

    
234
	private void initRankUpdateStatement(PesiExportState state) throws SQLException {
235
		Connection connection = state.getConfig().getDestination().getConnection();
236
		String rankSql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, KingdomFk = ? WHERE TaxonId = ?";
237
		rankUpdateStmt = connection.prepareStatement(rankSql);
238
	}
239

    
240
	private void initRankExpertsUpdateStmt(PesiExportState state) throws SQLException {
241
//		String sql_old = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ?, " +
242
//				"ExpertFk = ?, SpeciesExpertFk = ? WHERE TaxonId = ?";
243
		//TODO handle experts GUIDs
244
		Connection connection = state.getConfig().getDestination().getConnection();
245

    
246
		String sql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ? " +
247
				" WHERE TaxonId = ?";
248
		rankTypeExpertsUpdateStmt = connection.prepareStatement(sql);
249
	}
250

    
251
	private boolean doPhase01(PesiExportState state, PesiExportMapping mapping, PesiExportMapping additionalSourceMapping){
252
		int count = 0;
253
		int pastCount = 0;
254
		boolean success = true;
255
		// Get the limit for objects to save within a single transaction.
256
		int limit = state.getConfig().getLimitSave();
257

    
258
		logger.info("PHASE 1: Export Taxa...limit is " + limit);
259
		// Start transaction
260
		TransactionStatus txStatus = startTransaction(true);
261
		if (logger.isDebugEnabled()) {
262
            logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
263
            logger.info("Taking snapshot at the beginning of phase 1 of taxonExport");
264
            //ProfilerController.memorySnapshot();
265
        }
266

    
267
		int partitionCount = 0;
268
		List<TaxonBase<?>> list;
269
		while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)) != null   ) {
270

    
271
			logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
272

    
273
			for (TaxonBase<?> taxon : list) {
274
				doCount(count++, modCount, pluralString);
275
				TaxonName taxonName = taxon.getName();
276

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

    
311
				//TODO switch on again, leads to some warnings in ERMS for taxa of not correctly handled kingdoms
312
//				validatePhaseOne(taxon, nvn);
313
			}
314

    
315
			// Commit transaction
316
			commitTransaction(txStatus);
317
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 01)");
318
			pastCount = count;
319

    
320
			// Start new transaction
321
			txStatus = startTransaction(true);
322
			if (logger.isDebugEnabled()) {
323
                logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
324
            }
325

    
326
		}
327
		logger.debug("No " + pluralString + " left to fetch.");
328

    
329
		// Commit transaction
330
		commitTransaction(txStatus);
331
		txStatus = null;
332

    
333
		return success;
334
	}
335

    
336
	private void validatePhaseOne(TaxonBase<?> taxon, TaxonName taxonName) {
337

    
338
	    // Check whether some rules are violated
339
		NomenclaturalCode nomenclaturalCode = taxonName.getNameType();
340
		String genusOrUninomial = taxonName.getGenusOrUninomial();
341
		String specificEpithet = taxonName.getSpecificEpithet();
342
		String infraSpecificEpithet = taxonName.getInfraSpecificEpithet();
343
		String infraGenericEpithet = taxonName.getInfraGenericEpithet();
344
		Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
345

    
346
		if (rankFk == null) {
347
			logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
348
		} else {
349

    
350
			// Check whether infraGenericEpithet is set correctly
351
			// 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
352
			// 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
353

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

    
373
			if (infraGenericEpithet == null && rankFk.intValue() == 190) {
374
				logger.warn("InfraGenericEpithet was not determined although it should exist for rank 190: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
375
			}
376
			if (specificEpithet != null && rankFk.intValue() < 216) {
377
				logger.warn("SpecificEpithet was determined for rank " + rankFk + " although it should only exist for ranks higher or equal to 220: TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
378
			}
379
			if (infraSpecificEpithet != null && rankFk.intValue() < 225) {
380
				String message = "InfraSpecificEpithet '" +infraSpecificEpithet + "' was determined for rank " + rankFk + " although it should only exist for ranks higher or equal to 230: "  + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")";
381
				if (StringUtils.isNotBlank(infraSpecificEpithet)){
382
					logger.warn(message);
383
				}else{
384
					logger.warn(message);
385
				}
386
			}
387
		}
388
		if (infraSpecificEpithet != null && specificEpithet == null) {
389
			logger.warn("An infraSpecificEpithet was determined, but a specificEpithet was not determined: "  + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
390
		}
391
		if (genusOrUninomial == null) {
392
			logger.warn("GenusOrUninomial was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
393
		}
394
	}
395

    
396
	/**
397
	 * 2nd Round: Add ParentTaxonFk to each taxon and add Biota if not exists
398
	 */
399
	private boolean doPhase02(PesiExportState state) {
400
		int count = 0;
401
		int pastCount = 0;
402
		boolean success = true;
403
		if (! state.getConfig().isDoParentAndBiota()){
404
			logger.info ("Ignore PHASE 2: Make ParentFk and Biota...");
405
			return success;
406
		}
407

    
408
		// Get the limit for objects to save within a single transaction.
409
		int limit = state.getConfig().getLimitSave();
410

    
411
		insertBiota(state);
412

    
413
		logger.info("PHASE 2: Make ParentFk and Biota ... limit is " + limit);
414
		// Start transaction
415
		TransactionStatus txStatus = startTransaction(true);
416
		int partitionCount = 0;
417

    
418
//		ProfilerController.memorySnapshot();
419
		List<Taxon> list;
420
		while ((list = getNextTaxonPartition(Taxon.class, limit, partitionCount++, null)) != null   ) {
421

    
422
			if(logger.isDebugEnabled()) {
423
                logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
424
            }
425
			for (Taxon taxon : list) {
426
				for (TaxonNode node : taxon.getTaxonNodes()){
427
					doCount(count++, modCount, pluralString);
428
					TaxonNode parentNode = node.getParent();
429
					if (parentNode != null && parentNode.getTaxon() != null){  //new root node handling requires has root taxon with taxon == null
430
						int childId = state.getDbId( taxon);
431
						int parentId = state.getDbId(parentNode.getTaxon());
432
						success &= invokeParentTaxonFk(parentId, childId);
433
					}
434
				}
435
			}
436

    
437
			// Commit transaction
438
			commitTransaction(txStatus);
439
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 2)");
440
			pastCount = count;
441
			// Start transaction
442
			txStatus = startTransaction(true);
443
			if (logger.isDebugEnabled()){
444
			    logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
445
			}
446
		}
447
		logger.debug("No " + pluralString + " left to fetch.");
448

    
449
		// Commit transaction
450
		commitTransaction(txStatus);
451

    
452
		return success;
453
	}
454

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

    
473
	//PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...
474
	private boolean doPhase03(PesiExportState state) {
475
		int count = 0;
476
		int pastCount = 0;
477
		boolean success = true;
478
		if (! state.getConfig().isDoTreeIndex()){
479
			logger.info ("Ignore PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
480
			return success;
481
		}
482
		// Get the limit for objects to save within a single transaction.
483
		int limit = state.getConfig().getLimitSave();
484

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

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

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

    
508
				doCount(count++, modCount, pluralString);
509
				Integer typeNameFk = getTypeNameFk(taxonName, state);
510
				Integer kingdomFk = findKingdomIdFromTreeIndex(taxon, state);
511
				 //       PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
512

    
513
				//TODO why are expertFks needed? (Andreas M.)
514
//				if (expertFk != null || speciesExpertFk != null) {
515
				    NomenclaturalCode nomCode = taxonName.getNameType();
516
				    //is there a reason why we do pass nomCode separately? Before nomCode was class variable, but not clear why and when it was set
517
					invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, nomCode, state.getDbId(taxon),
518
							typeNameFk, kingdomFk, state);
519
//				}
520
			}
521

    
522
			// Commit transaction
523
			commitTransaction(txStatus);
524
			if (logger.isDebugEnabled()){logger.debug("Committed transaction.");}
525
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 3)");
526
			pastCount = count;
527

    
528
			// Start transaction
529
			txStatus = startTransaction(true);
530
			if (logger.isDebugEnabled()) {
531
                logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
532
            }
533
		}
534
		logger.debug("No " + pluralString + " left to fetch.");
535

    
536
		// Commit transaction
537
		commitTransaction(txStatus);
538

    
539
		if (logger.isDebugEnabled()){
540
		    logger.debug("Committed transaction.");
541
		    logger.debug("Try to take snapshot at the end of phase 3 of taxonExport, number of partitions: " + partitionCount);
542
		    //ProfilerController.memorySnapshot();
543
		}
544
		return success;
545
	}
546

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

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

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

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

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

    
562
        return success;
563
    }
564

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
712
			// Save Rank Data and KingdomFk for inferred synonyms
713
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
714
			    TaxonName taxonName = inferredSynonymsDataToBeSaved.get(taxonFk);
715
                NomenclaturalCode nomCode = taxonName.getNameType(); //nomCode was class variable before, not sure if this was important
716
                invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomCode, taxonFk, kingdomFk, state);
717
			}
718

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

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

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

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

    
743
			// Save Rank Data and KingdomFk for inferred synonyms
744
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
745
			    TaxonName taxonName = inferredSynonymsDataToBeSaved.get(taxonFk);
746
			    NomenclaturalCode nomCode = taxonName.getNameType(); //nomCode was class variable before, not sure if this was important
747
				invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomCode, taxonFk, kingdomFk, state);
748
			}
749

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
829

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

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

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

    
881

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1050

    
1051
	/**
1052
	 * Inserts Rank data and KingdomFk into the Taxon database table.
1053
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1054
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1055
	 * @param taxonFk The TaxonFk to store the values for.
1056
	 * @param state
1057
	 * @param kindomFk The KingdomFk.
1058
	 * @return Whether save was successful or not.
1059
	 */
1060
	private boolean invokeRankDataAndKingdomFk(TaxonName taxonName, NomenclaturalCode nomenclaturalCode, Integer taxonFk, Integer kingdomFk, PesiExportState state) {
1061
		try {
1062
			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1063
			if (rankFk != null) {
1064
				rankUpdateStmt.setInt(1, rankFk);
1065
			} else {
1066
				rankUpdateStmt.setObject(1, null);
1067
			}
1068

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

    
1076
			if (kingdomFk != null) {
1077

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

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

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

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

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

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

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

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

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

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

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

    
1182
		String sql;
1183
		Source destination =  pesiConfig.getDestination();
1184

    
1185
		// Clear Taxon
1186
		sql = "DELETE FROM " + dbTableName;
1187
		destination.update(sql);
1188

    
1189
		//TODO due to foreign keys we should also delete all tables linking to Taxon table
1190

    
1191
		return true;
1192
	}
1193

    
1194
	/**
1195
	 * Returns the rankFk for the taxon name based on the names nomenclatural code.
1196
	 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1197
	 * @param taxonName
1198
	 * @return
1199
	 */
1200
	@SuppressWarnings("unused")  //used by mapper
1201
	private static Integer getRankFk(TaxonName taxonName) {
1202
		return getRankFk(taxonName, taxonName.getNameType());
1203
	}
1204

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

    
1233
	private static String getRankCache(TaxonName taxonName, PesiExportState state) {
1234
		return getRankCache(taxonName, taxonName.getNameType(), state);
1235
	}
1236

    
1237

    
1238
	/**
1239
	 * Returns the <code>RankCache</code> attribute.
1240
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1241
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1242
	 * @param state
1243
	 * @return The <code>RankCache</code> attribute.
1244
	 * @see MethodMapper
1245
	 */
1246
	private static String getRankCache(TaxonName taxonName, NomenclaturalCode nomenclaturalCode, PesiExportState state) {
1247
	    List<TaxonNode> nodes = getTaxonNodes(taxonName);
1248
	    if (Rank.DOMAIN().equals(taxonName.getRank())){
1249
            return state.getTransformer().getCacheByRankAndKingdom(Rank.DOMAIN(), null);
1250
        }else if (!nodes.isEmpty()) {
1251
            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), findKingdomIdFromTreeIndex(nodes.iterator().next().getTaxon(), state)); //PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
1252
        }else if (nomenclaturalCode != null){
1253
            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
1254
        }else{
1255
			logger.warn("No nomenclatural code defined for name " + taxonName.getUuid());
1256
			return null;
1257
		}
1258
	}
1259

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

    
1279
    /**
1280
	 * Returns the <code>DisplayName</code> attribute.
1281
	 * @param taxon The {@link TaxonBase Taxon}.
1282
	 * @return The <code>DisplayName</code> attribute.
1283
	 * @see MethodMapper
1284
	 */
1285
	@SuppressWarnings("unused")  //used by Mapper
1286
	private static String getDisplayName(TaxonBase<?> taxon) {
1287
		TaxonName taxonName = taxon.getName();
1288
		String result = getDisplayName(taxonName);
1289
		if (isMisappliedName(taxon)){
1290
			result = result + " " + getAuthorString(taxon);
1291
		}
1292
		return result;
1293
	}
1294

    
1295
	/**
1296
	 * Returns the <code>AuthorString</code> attribute.
1297
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1298
	 * @return The <code>AuthorString</code> attribute.
1299
	 * @see MethodMapper
1300
	 */
1301
	//used by mapper
1302
	protected static String getAuthorString(TaxonBase<?> taxon) {
1303
		try {
1304
			String result = null;
1305
			boolean isNonViralName = false;
1306
			String authorshipCache = null;
1307
			TaxonName taxonName = taxon.getName();
1308
			if (taxonName != null && taxonName.isNonViral()){
1309
				authorshipCache = taxonName.getAuthorshipCache();
1310
				isNonViralName = true;
1311
			}
1312
			result = authorshipCache;
1313

    
1314
			// For a misapplied names there are special rules
1315
			if (isMisappliedName(taxon)){
1316
				if (taxon.getSec() != null){
1317
					String secTitle = taxon.getSec().getTitleCache();
1318
					if (! secTitle.startsWith("auct")){
1319
						secTitle = "sensu " + secTitle;
1320
					}else if (secTitle.equals("auct")){  //may be removed once the title cache is generated correctly for references with title auct. #
1321
						secTitle = "auct.";
1322
					}
1323
					return secTitle;
1324
				}else if (StringUtils.isBlank(authorshipCache)) {
1325
					// Set authorshipCache to "auct."
1326
					result = PesiTransformer.AUCT_STRING;
1327
				}else{
1328
					result = PesiTransformer.AUCT_STRING;
1329
//					result = authorshipCache;
1330
				}
1331
			}
1332

    
1333
			if (taxonName == null){
1334
				logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1335
			}else if (! isNonViralName){
1336
				logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1337
			}
1338

    
1339
			if (StringUtils.isBlank(result)) {
1340
				return null;
1341
			} else {
1342
				return result;
1343
			}
1344
		} catch (Exception e) {
1345
			e.printStackTrace();
1346
			return null;
1347
		}
1348
	}
1349

    
1350
	/**
1351
	 * Returns the <code>DisplayName</code> attribute.
1352
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1353
	 * @return The <code>DisplayName</code> attribute.
1354
	 * @see MethodMapper
1355
	 */
1356
	 //used by Mapper
1357
	private static String getDisplayName(TaxonName taxonName) {
1358
		// TODO: extension?
1359
		if (taxonName == null) {
1360
			return null;
1361
		}else{
1362
		    taxonName = CdmBase.deproxy(taxonName);
1363
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1364
			HTMLTagRules tagRules = new HTMLTagRules().
1365
					addRule(TagEnum.name, "i").
1366
					addRule(TagEnum.nomStatus, "@status@");
1367

    
1368
			String result;
1369
			if (getSources(taxonName).get(PesiTransformer.SOURCE_ERMS)){
1370
			    result = cacheStrategy.getTitleCache(taxonName, tagRules);  //according to SQL script (also in ERMS sources are not abbreviated)
1371
			}else if (getSources(taxonName).get(PesiTransformer.SOURCE_EM)){
1372
			    result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
1373
			}else{
1374
			    //TODO define for FE + IF and for multiple sources
1375
			    result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
1376
			}
1377
			return result.replaceAll(",?\\<@status@\\>.*\\</@status@\\>", "");
1378
		}
1379
	}
1380

    
1381
	@SuppressWarnings("unused")
1382
	private static String getGUID(TaxonName taxonName) {
1383
		UUID uuid = taxonName.getUuid();
1384
		String result = "NameUUID:" + uuid.toString();
1385
		return result;
1386
	}
1387

    
1388
	/**
1389
	 * Returns the <code>WebShowName</code> attribute for a taxon.
1390
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1391
	 * @return The <code>WebShowName</code> attribute.
1392
	 * @see MethodMapper
1393
	*/
1394
	@SuppressWarnings("unused")
1395
	private static String getWebShowName(TaxonBase<?> taxon) {
1396
		TaxonName taxonName = taxon.getName();
1397
		String result = getWebShowName(taxonName);
1398
		if (isMisappliedName(taxon)){
1399
			result = result + " " + getAuthorString(taxon);
1400
		}
1401
		return result;
1402
	}
1403

    
1404
	/**
1405
	 * Returns the <code>WebShowName</code> attribute.
1406
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1407
	 * @return The <code>WebShowName</code> attribute.
1408
	 * @see MethodMapper
1409
	 */
1410
	private static String getWebShowName(TaxonName taxonName) {
1411
		//TODO extensions?
1412
		if (taxonName == null) {
1413
			return null;
1414
		}else{
1415
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1416

    
1417
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1418
			String result = cacheStrategy.getTitleCache(taxonName, tagRules);
1419
			return result;
1420
		}
1421
	}
1422

    
1423
	/**
1424
	 * Returns the <code>WebSearchName</code> attribute.
1425
	 * @param taxonName The {@link NonViralName NonViralName}.
1426
	 * @return The <code>WebSearchName</code> attribute.
1427
	 * @see MethodMapper
1428
	 */
1429
	@SuppressWarnings("unused")
1430
	private static String getWebSearchName(TaxonName taxonName) {
1431
		//TODO extensions?
1432
	    TaxonNameDefaultCacheStrategy strategy = getCacheStrategy(taxonName);
1433
		String result = strategy.getNameCache(taxonName);
1434
		return result;
1435
	}
1436

    
1437
	/**
1438
	 * Returns the <code>FullName</code> attribute.
1439
	 * @param taxonName The {@link NonViralName NonViralName}.
1440
	 * @return The <code>FullName</code> attribute.
1441
	 * @see MethodMapper
1442
	 */
1443
	@SuppressWarnings("unused")
1444
	private static String getFullName(TaxonName taxonName) {
1445
		//TODO extensions?
1446
		String result = getCacheStrategy(taxonName).getTitleCache(taxonName);
1447
		Iterator<Taxon> taxa = taxonName.getTaxa().iterator();
1448
		if (taxonName.getTaxa().size() >0){
1449
			if (taxonName.getTaxa().size() == 1){
1450
				Taxon taxon = taxa.next();
1451
				if (isMisappliedName(taxon)){
1452
					result = result + " " + getAuthorString(taxon);
1453
				}
1454
				taxon = null;
1455
			}
1456
		}
1457
		return result;
1458
	}
1459

    
1460
	/**
1461
	 * Returns the SourceNameCache for the AdditionalSource table
1462
	 * @param taxonName
1463
	 * @return
1464
	 */
1465
	static boolean isFirstAbbrevTitle = true;
1466
	@SuppressWarnings("unused")
1467
	private static String getSourceNameCache(TaxonName taxonName) {
1468
		if (taxonName != null){
1469
			Reference nomRef = taxonName.getNomenclaturalReference();
1470
			if (nomRef != null ){
1471
			    if (isFirstAbbrevTitle){
1472
			        //#5388 is definetely not the correct ticket number
1473
			        logger.warn("Semantics of getAbbrevTitleCache has changed. Please check if output is still correct. See #5388");
1474
			        isFirstAbbrevTitle = false;
1475
			    }
1476
			    return nomRef.getAbbrevTitleCache();
1477
			}
1478
		}
1479
		return null;
1480
	}
1481

    
1482
	/**
1483
	 * Returns the nomenclatural reference which is the reference
1484
	 * including the detail (microreference).
1485
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1486
	 * @return The <code>AuthorString</code> attribute.
1487
	 * @see MethodMapper
1488
	 */
1489
	@SuppressWarnings("unused")
1490
	private static String getNomRefString(TaxonName taxonName) {
1491
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1492
		if (ref == null){
1493
			return null;
1494
		}
1495
		String result = null;
1496
		BitSet sources = getSources(taxonName);
1497
		int len = sources.length();
1498
		if(sources.get(PesiTransformer.SOURCE_EM)){
1499
		    if (! ref.isProtectedAbbrevTitleCache()){
1500
		        ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1501
		    }
1502
		    result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1503
		}else if(sources.get(PesiTransformer.SOURCE_FE)||sources.get(PesiTransformer.SOURCE_IF) ){
1504
            //TODO still need to check if correct for FE + IF
1505
		    if (! ref.isProtectedAbbrevTitleCache()){
1506
                ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1507
            }
1508
            result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1509
            return result;   // according to SQL script
1510
		}else if(sources.get(PesiTransformer.SOURCE_ERMS)) {
1511
            //result = null; //according to SQL script
1512
		}else{
1513
		    logger.warn("Source not yet supported");
1514
		}
1515
		return result;
1516
	}
1517

    
1518
	/**
1519
	 * Returns the <code>NameStatusFk</code> attribute.
1520
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1521
	 * @return The <code>NameStatusFk</code> attribute.
1522
	 * @see MethodMapper
1523
	 */
1524
	@SuppressWarnings("unused")
1525
	private static Integer getNameStatusFk(TaxonName taxonName) {
1526
		Integer result = null;
1527

    
1528
		NomenclaturalStatus status = getNameStatus(taxonName);
1529
		if (status != null) {
1530
			result = PesiTransformer.nomStatus2nomStatusFk(status.getType());
1531
		}
1532
		return result;
1533
	}
1534

    
1535
	/**
1536
	 * Returns the <code>NameStatusCache</code> attribute.
1537
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1538
	 * @return The <code>NameStatusCache</code> attribute.
1539
	 * @throws UndefinedTransformerMethodException
1540
	 * @see MethodMapper
1541
	 */
1542
	@SuppressWarnings("unused")
1543
	private static String getNameStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1544
		String result = null;
1545
		NomenclaturalStatus status = getNameStatus(taxonName);
1546
		if (status != null) {
1547
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1548
		}
1549
		return result;
1550
	}
1551

    
1552
	private static NomenclaturalStatus getNameStatus(TaxonName taxonName) {
1553
		try {
1554
			if (taxonName != null) {
1555
			    Set<NomenclaturalStatus> states = taxonName.getStatus();
1556
				if (states.size() == 1) {
1557
					NomenclaturalStatus status = states.iterator().next();
1558
					return status;
1559
				} else if (states.size() > 1) {
1560
					logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1561
				}
1562
			}
1563
		} catch (Exception e) {
1564
			e.printStackTrace();
1565
		}
1566
		return null;
1567
	}
1568
	/**
1569
	 * Returns the <code>TaxonStatusFk</code> attribute.
1570
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1571
	 * @param state The {@link PesiExportState PesiExportState}.
1572
	 * @return The <code>TaxonStatusFk</code> attribute.
1573
	 * @see MethodMapper
1574
	 */
1575
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1576
		Integer result = null;
1577

    
1578
		try {
1579
			if (isMisappliedName(taxon)) {
1580
				Synonym synonym = Synonym.NewInstance(null, null);
1581

    
1582
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1583
				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1584
			} else {
1585
				result = PesiTransformer.taxonBase2statusFk(taxon);
1586
			}
1587
		} catch (Exception e) {
1588
			e.printStackTrace();
1589
		}
1590
		return result;
1591
	}
1592

    
1593
	/**
1594
	 * Returns the <code>TaxonStatusCache</code> attribute.
1595
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1596
	 * @param state The {@link PesiExportState PesiExportState}.
1597
	 * @return The <code>TaxonStatusCache</code> attribute.
1598
	 * @throws UndefinedTransformerMethodException
1599
	 * @see MethodMapper
1600
	 */
1601
	@SuppressWarnings("unused")
1602
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1603
		return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1604
	}
1605

    
1606
    /**
1607
     * Returns the <code>TaxonFk1</code> attribute. It corresponds to a CDM <code>TaxonRelationship</code>.
1608
     * @param relationship The {@link RelationshipBase Relationship}.
1609
     * @param state The {@link PesiExportState PesiExportState}.
1610
     * @return The <code>TaxonFk1</code> attribute.
1611
     * @see MethodMapper
1612
     */
1613
    @SuppressWarnings("unused")
1614
    private static Integer getSynonym(Synonym synonym, PesiExportState state) {
1615
         return state.getDbId(synonym);
1616
    }
1617

    
1618
	/**
1619
	 * Returns the <code>TypeNameFk</code> attribute.
1620
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1621
	 * @param state The {@link PesiExportState PesiExportState}.
1622
	 * @return The <code>TypeNameFk</code> attribute.
1623
	 * @see MethodMapper
1624
	 */
1625
	private static Integer getTypeNameFk(TaxonName taxonName, PesiExportState state) {
1626
		Integer result = null;
1627
		if (taxonName != null) {
1628
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1629
			if (nameTypeDesignations.size() == 1) {
1630
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1631
				if (nameTypeDesignation != null) {
1632
					TaxonName typeName = nameTypeDesignation.getTypeName();
1633
					if (typeName != null) {
1634
					    result = state.getDbId(typeName);
1635
					}
1636
				}
1637
			} else if (nameTypeDesignations.size() > 1) {
1638
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1639
			}
1640
		}
1641
		return result;
1642
	}
1643

    
1644
	/**
1645
	 * Returns the <code>TypeFullnameCache</code> attribute.
1646
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1647
	 * @return The <code>TypeFullnameCache</code> attribute.
1648
	 * @see MethodMapper
1649
	 */
1650
	@SuppressWarnings("unused")
1651
	private static String getTypeFullnameCache(TaxonName taxonName) {
1652
		String result = null;
1653

    
1654
		try {
1655
    		if (taxonName != null) {
1656
    			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1657
    			if (nameTypeDesignations.size() == 1) {
1658
    				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1659
    				if (nameTypeDesignation != null) {
1660
    					TaxonName typeName = nameTypeDesignation.getTypeName();
1661
    					if (typeName != null) {
1662
    						result = typeName.getTitleCache();
1663
    					}
1664
    				}
1665
    			} else if (nameTypeDesignations.size() > 1) {
1666
    				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1667
    			}
1668
    		}
1669
		} catch (Exception e) {
1670
			e.printStackTrace();
1671
		}
1672
		return result;
1673
	}
1674

    
1675

    
1676
	/**
1677
	 * Returns the <code>QualityStatusFk</code> attribute.
1678
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1679
	 * @return The <code>QualityStatusFk</code> attribute.
1680
	 * @see MethodMapper
1681
	 */
1682
	private static Integer getQualityStatusFk(TaxonName taxonName) {
1683
		BitSet sources = getSources(taxonName);
1684
		return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1685
	}
1686

    
1687

    
1688
	/**
1689
	 * Returns the <code>QualityStatusCache</code> attribute.
1690
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1691
	 * @return The <code>QualityStatusCache</code> attribute.
1692
	 * @throws UndefinedTransformerMethodException
1693
	 * @see MethodMapper
1694
	 */
1695
	@SuppressWarnings("unused")
1696
	private static String getQualityStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1697
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1698
	}
1699

    
1700

    
1701
	/**
1702
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1703
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1704
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1705
	 * @see MethodMapper
1706
	 */
1707
	@SuppressWarnings("unused")
1708
	private static Integer getTypeDesignationStatusFk(TaxonName taxonName) {
1709
		Integer result = null;
1710

    
1711
		try {
1712
		if (taxonName != null) {
1713
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1714
			if (typeDesignations.size() == 1) {
1715
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1716
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1717
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1718
			} else if (typeDesignations.size() > 1) {
1719
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1720
			}
1721
		}
1722

    
1723
		} catch (Exception e) {
1724
			e.printStackTrace();
1725
		}
1726
		return result;
1727
	}
1728

    
1729
	/**
1730
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1731
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1732
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1733
	 * @see MethodMapper
1734
	 */
1735
	@SuppressWarnings("unused")
1736
	private static String getTypeDesignationStatusCache(TaxonName taxonName) {
1737
		String result = null;
1738

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

    
1751
		} catch (Exception e) {
1752
			e.printStackTrace();
1753
		}
1754
		return result;
1755
	}
1756

    
1757
	/**
1758
	 * Returns the <code>FossilStatusFk</code> attribute.
1759
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1760
	 * @return The <code>FossilStatusFk</code> attribute.
1761
	 * @see MethodMapper
1762
	 */
1763
	@SuppressWarnings("unused")
1764
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
1765
		Integer result = null;
1766

    
1767
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
1768
		if (fossilStatuus.size() == 0){
1769
			return null;
1770
		}else if (fossilStatuus.size() > 1){
1771
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
1772
		}
1773
		String fossilStatus = fossilStatuus.iterator().next();
1774

    
1775
		int statusFk = state.getTransformer().fossilStatusCache2FossilStatusFk(fossilStatus);
1776
		return statusFk;
1777
	}
1778

    
1779
	/**
1780
	 * Returns the <code>FossilStatusCache</code> attribute.
1781
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1782
	 * @return The <code>FossilStatusCache</code> attribute.
1783
	 * @see MethodMapper
1784
	 */
1785
	@SuppressWarnings("unused")
1786
	private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
1787
		String result = null;
1788
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
1789
		if (fossilStatuus.size() == 0){
1790
			return null;
1791
		}
1792
		for (String strFossilStatus : fossilStatuus){
1793
			result = CdmUtils.concat(";", result, strFossilStatus);
1794
		}
1795
		return result;
1796
	}
1797

    
1798
	/**
1799
	 * Returns the <code>IdInSource</code> attribute.
1800
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1801
	 * @return The <code>IdInSource</code> attribute.
1802
	 * @see MethodMapper
1803
	 */
1804
	@SuppressWarnings("unused")
1805
	private static String getIdInSource(IdentifiableEntity<?> taxonName) {
1806
		String result = null;
1807

    
1808
		try {
1809
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
1810
			if (sources.size() > 1){
1811
				logger.warn("There is > 1 Pesi source. This is not yet handled.");
1812
			}
1813
			if (sources.size() == 0){
1814
				logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1815
			}
1816
			for (IdentifiableSource source : sources) {
1817
				Reference ref = source.getCitation();
1818
				UUID refUuid = ref.getUuid();
1819
				String idInSource = source.getIdInSource();
1820
				if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
1821
					result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
1822
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
1823
					result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
1824
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
1825
					result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
1826
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
1827
					result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
1828
				}else{
1829
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");}
1830
				}
1831

    
1832
				String sourceIdNameSpace = source.getIdNamespace();
1833
				if (sourceIdNameSpace != null) {
1834
					if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
1835
						result =  idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
1836
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
1837
						result =  idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
1838
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
1839
						result =  idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
1840
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
1841
						result =  idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
1842
					}
1843
				}
1844
				if (result == null) {
1845
					logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
1846
				}
1847
			}
1848
		} catch (Exception e) {
1849
			e.printStackTrace();
1850
			logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
1851
		}
1852

    
1853
		if (result == null) {
1854
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1855
		}
1856
		return result;
1857
	}
1858

    
1859
	/**
1860
	 * Returns the idInSource for a given TaxonName only.
1861
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1862
	 * @return The idInSource.
1863
	 */
1864
	private static String getIdInSourceOnly(IdentifiableEntity<?> identEntity) {
1865
		String result = null;
1866

    
1867
		// Get the sources first
1868
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
1869

    
1870
		// Determine the idInSource
1871
		if (sources.size() == 1) {
1872
			IdentifiableSource source = sources.iterator().next();
1873
			if (source != null) {
1874
				result = source.getIdInSource();
1875
			}
1876
		} else if (sources.size() > 1) {
1877
			int count = 1;
1878
			result = "";
1879
			for (IdentifiableSource source : sources) {
1880
				result += source.getIdInSource();
1881
				if (count < sources.size()) {
1882
					result += "; ";
1883
				}
1884
				count++;
1885
			}
1886

    
1887
		}
1888

    
1889
		return result;
1890
	}
1891

    
1892
	/**
1893
	 * Returns the <code>GUID</code> attribute.
1894
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1895
	 * @return The <code>GUID</code> attribute.
1896
	 * @see MethodMapper
1897
	 */
1898
	private static String getGUID(TaxonBase<?> taxon) {
1899
		if (taxon.getLsid() != null ){
1900
			return taxon.getLsid().getLsid();
1901
		}else if (taxon.hasMarker(PesiTransformer.uuidMarkerGuidIsMissing, true)){
1902
			return null;
1903
		}else{
1904
			return taxon.getUuid().toString();
1905
		}
1906
	}
1907

    
1908
	/**
1909
	 * Returns the <code>DerivedFromGuid</code> attribute.
1910
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1911
	 * @return The <code>DerivedFromGuid</code> attribute.
1912
	 * @see MethodMapper
1913
	 */
1914
	@SuppressWarnings("unused")
1915
	private static String getDerivedFromGuid(TaxonBase<?> taxon) {
1916
		String result = null;
1917
		try {
1918
		// The same as GUID for now
1919
		result = getGUID(taxon);
1920
		} catch (Exception e) {
1921
			e.printStackTrace();
1922
		}
1923
		return result;
1924
	}
1925

    
1926
	/**
1927
	 * Returns the <code>CacheCitation</code> attribute.
1928
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1929
	 * @return The CacheCitation.
1930
	 * @see MethodMapper
1931
	 */
1932
	@SuppressWarnings("unused")
1933
	private static String getCacheCitation(TaxonBase<?> taxon) {
1934
		// !!! See also doPhaseUpdates
1935

    
1936
		TaxonName taxonName = taxon.getName();
1937
		String result = "";
1938
		//TODO implement anew for taxa
1939
		try {
1940
			BitSet sources = getSources(taxon);
1941
			if (sources.isEmpty()) {
1942
//				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1943
			} else if (sources.get(PesiTransformer.SOURCE_ERMS)) {
1944
				Set<Extension> extensions = taxon.getExtensions();
1945
				for (Extension extension : extensions) {
1946
					if (extension.getType().equals(cacheCitationExtensionType)) {
1947
						result = extension.getValue();
1948
					}
1949
				}
1950
			} else {
1951
				String expertName = getExpertName(taxon);
1952
				String webShowName = getWebShowName(taxonName);
1953

    
1954
				// idInSource only
1955
				String idInSource = getIdInSourceOnly(taxonName);
1956

    
1957
				// build the cacheCitation
1958
				if (expertName != null) {
1959
					result += expertName + ". ";
1960
				} else {
1961
					if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
1962
				}
1963
				if (webShowName != null) {
1964
					result += webShowName + ". ";
1965
				} else {
1966
					logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1967
				}
1968

    
1969
				if (getOriginalDB(taxonName).equals("FaEu")) {
1970
					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
1971
				} else if (getOriginalDB(taxonName).equals("EM")) {
1972
					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
1973
				}
1974

    
1975
				if (idInSource != null) {
1976
					result += idInSource;
1977
				} else {
1978
					logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1979
				}
1980
			}
1981
		} catch (Exception e) {
1982
			e.printStackTrace();
1983
		}
1984

    
1985
		if (StringUtils.isBlank(result)) {
1986
			return null;
1987
		} else {
1988
			return result;
1989
		}
1990
	}
1991

    
1992
	/**
1993
	 * Returns the <code>OriginalDB</code> attribute.
1994
	 * @param identifiableEntity
1995
	 * @return The <code>OriginalDB</code> attribute.
1996
	 * @see MethodMapper
1997
	 */
1998
	@SuppressWarnings("unused")
1999
	private static String getOriginalDB(IdentifiableEntity identifiableEntity) {
2000
		BitSet sources  = getSources(identifiableEntity);
2001
		return PesiTransformer.getOriginalDbBySources(sources);
2002
	}
2003

    
2004
	/**
2005
	 * Returns the <code>LastAction</code> attribute.
2006
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2007
	 * @return The <code>LastAction</code> attribute.
2008
	 * @see MethodMapper
2009
	 */
2010
	@SuppressWarnings("unused")
2011
	private static String getLastAction(IdentifiableEntity<?> identEntity) {
2012
		String result = null;
2013
		try {
2014
		Set<Extension> extensions = identEntity.getExtensions();
2015
		for (Extension extension : extensions) {
2016
			if (extension.getType().equals(lastActionExtensionType)) {
2017
				result = extension.getValue();
2018
			}
2019
		}
2020
		} catch (Exception e) {
2021
			e.printStackTrace();
2022
		}
2023
		return result;
2024
	}
2025

    
2026
	/**
2027
	 * Returns the <code>LastActionDate</code> attribute.
2028
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2029
	 * @return The <code>LastActionDate</code> attribute.
2030
	 * @see MethodMapper
2031
	 */
2032
	@SuppressWarnings({ "unused" })
2033
	private static DateTime getLastActionDate(IdentifiableEntity<?> identEntity) {
2034
		DateTime result = null;
2035
		try {
2036
			Set<Extension> extensions = identEntity.getExtensions();
2037
			for (Extension extension : extensions) {
2038
				if (extension.getType().equals(lastActionDateExtensionType)) {
2039
					String dateTime = extension.getValue();
2040
					if (dateTime != null) {
2041
						DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
2042
						result = formatter.parseDateTime(dateTime);
2043
					}
2044
				}
2045
			}
2046
		} catch (Exception e) {
2047
			e.printStackTrace();
2048
		}
2049
		return result;
2050
	}
2051

    
2052
	/**
2053
	 * Returns the <code>ExpertName</code> attribute.
2054
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2055
	 * @return The <code>ExpertName</code> attribute.
2056
	 * @see MethodMapper
2057
	 */
2058
	@SuppressWarnings("unused")
2059
	private static String getExpertName(TaxonBase<?> taxonName) {
2060
		String result = null;
2061
		try {
2062
		Set<Extension> extensions = taxonName.getExtensions();
2063
		for (Extension extension : extensions) {
2064
			if (extension.getType().equals(expertNameExtensionType)) {
2065
				result = extension.getValue();
2066
			}
2067
		}
2068
		} catch (Exception e) {
2069
			e.printStackTrace();
2070
		}
2071
		return result;
2072
	}
2073

    
2074
	/**
2075
	 * Returns the <code>ExpertFk</code> attribute.
2076
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2077
	 * @param state The {@link PesiExportState PesiExportState}.
2078
	 * @return The <code>ExpertFk</code> attribute.
2079
	 * @see MethodMapper
2080
	 */
2081
	private static Integer getExpertFk(Reference reference, PesiExportState state) {
2082
		Integer result = state.getDbId(reference);
2083
		return result;
2084
	}
2085

    
2086
	/**
2087
	 * Returns the <code>SpeciesExpertName</code> attribute.
2088
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2089
	 * @return The <code>SpeciesExpertName</code> attribute.
2090
	 * @see MethodMapper
2091
	 */
2092
	@SuppressWarnings("unused")
2093
	private static String getSpeciesExpertName(TaxonBase<?> taxonName) {
2094
		String result = null;
2095
		try {
2096
		Set<Extension> extensions = taxonName.getExtensions();
2097
		for (Extension extension : extensions) {
2098
			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2099
				result = extension.getValue();
2100
			}
2101
		}
2102
		} catch (Exception e) {
2103
			e.printStackTrace();
2104
		}
2105
		return result;
2106
	}
2107

    
2108
	/**
2109
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2110
	 * @param reference The {@link Reference Reference}.
2111
	 * @param state The {@link PesiExportState PesiExportState}.
2112
	 * @return The <code>SpeciesExpertFk</code> attribute.
2113
	 * @see MethodMapper
2114
	 */
2115
	private static Integer getSpeciesExpertFk(Reference reference, PesiExportState state) {
2116
		Integer result = state.getDbId(reference);
2117
		return result;
2118
	}
2119

    
2120
	protected static TaxonNameDefaultCacheStrategy getCacheStrategy(TaxonName taxonName) {
2121
		taxonName = CdmBase.deproxy(taxonName);
2122
		TaxonNameDefaultCacheStrategy cacheStrategy;
2123
		if (taxonName.isZoological()){
2124
			cacheStrategy = zooNameStrategy;
2125
		}else if (taxonName.isBotanical()) {
2126
			cacheStrategy = nonViralNameStrategy;
2127
		}else if (taxonName.isNonViral()) {
2128
			cacheStrategy = nonViralNameStrategy;
2129
		}else if (taxonName.isBacterial()) {
2130
			cacheStrategy = nonViralNameStrategy;
2131
		}else{
2132
			logger.error("Unhandled taxon name type. Can't define strategy class");
2133
			cacheStrategy = nonViralNameStrategy;
2134
		}
2135
		return cacheStrategy;
2136
	}
2137

    
2138
	/**
2139
	 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2140
	 * @param relationship The {@link RelationshipBase Relationship}.
2141
	 * @return The <code>RelTaxonQualifierFk</code> attribute.
2142
	 * @see MethodMapper
2143
	 */
2144
	@SuppressWarnings("unused")
2145
	private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2146
		return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2147
	}
2148

    
2149
    @SuppressWarnings("unused")
2150
    private static String getSynonymTypeCache(Synonym synonym, PesiExportState state) {
2151
        String result = null;
2152
        NomenclaturalCode code = null;
2153
        code = CdmBase.deproxy(synonym, Synonym.class).getAcceptedTaxon().getName().getNameType();
2154

    
2155
        if (code != null) {
2156
            result = state.getConfig().getTransformer().getCacheBySynonymType(synonym, code);
2157
        } else {
2158
            logger.error("NomenclaturalCode is NULL while creating the following synonym: " + synonym.getUuid());
2159
        }
2160
        return result;
2161
    }
2162

    
2163
// ********************************** MAPPINGS ********************************/
2164

    
2165
	/**
2166
	 * Returns the CDM to PESI specific export mappings.
2167
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2168
	 */
2169
	private PesiExportMapping getMapping() {
2170
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2171

    
2172
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2173
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2174
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2175
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2176

    
2177
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2178

    
2179
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2180
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2181
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misapllied Names are handled differently
2182
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2183

    
2184
		// DisplayName
2185
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2186

    
2187
		// FossilStatus (Fk, Cache)
2188
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2189
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2190

    
2191
		//handled by name mapping
2192
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2193
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2194

    
2195
		//experts
2196
		ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtSpeciesExpertName);
2197
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2198

    
2199
		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
2200
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2201

    
2202
		//handled in Phase02 now
2203
//		mapping.addMapper(MethodMapper.NewInstance("ParentTaxonFk", this, TaxonBase.class, PesiExportState.class));  //by AM, doesn't work, FK exception
2204
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2205

    
2206
		addNameMappers(mapping);
2207

    
2208
		return mapping;
2209
	}
2210

    
2211
	/**
2212
	 * Returns the CDM to PESI specific export mappings.
2213
	 * @param state
2214
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2215
	 * @throws UndefinedTransformerMethodException
2216
	 */
2217
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2218
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2219

    
2220
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2221

    
2222
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonName.class));
2223
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonName.class));
2224
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonName.class, PesiExportState.class));
2225
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
2226
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2227
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
2228
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonName.class));
2229
		mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonName.class));
2230

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

    
2234
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2235
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2236

    
2237
		addNameMappers(mapping);
2238
		//TODO add author mapper, TypeNameFk
2239

    
2240
		return mapping;
2241
	}
2242

    
2243
	private void addNameMappers(PesiExportMapping mapping) {
2244
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2245
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2246
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2247
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2248

    
2249
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2250
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonName.class));
2251

    
2252
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonName.class));
2253

    
2254

    
2255
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonName.class));
2256

    
2257
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonName.class));
2258
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonName.class, PesiExportState.class));
2259
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
2260
		//TODO TypeNameFk
2261

    
2262
		//quality status
2263
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonName.class));
2264
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonName.class, PesiExportState.class));
2265

    
2266
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2267
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2268

    
2269
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2270

    
2271
	}
2272

    
2273
	private PesiExportMapping getSynRelMapping() {
2274
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2275
		logger.warn("SynRelMapping currently not implemented. Needs to be checked");
2276

    
2277
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getSynonym", Synonym.class, PesiExportState.class));
2278
		mapping.addMapper(DbObjectMapper.NewInstance("acceptedTaxon", "TaxonFk2"));
2279
		mapping.addMapper(DbObjectMapper.NewInstance("type", "RelTaxonQualifierFk"));
2280
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this.getClass(), "getSynonymTypeCache", Synonym.class, PesiExportState.class));
2281
		// TODO
2282
//		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2283

    
2284
		return mapping;
2285
	}
2286

    
2287
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state) {
2288
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2289

    
2290
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2291
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2292

    
2293
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2294
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2295
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonName.class));
2296

    
2297
		//we have only nomenclatural references here
2298
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2299
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2300

    
2301
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2302

    
2303
		return mapping;
2304
	}
2305

    
2306

    
2307
    @Override
2308
    protected boolean doCheck(PesiExportState state) {
2309
        return true;
2310
    }
2311

    
2312
    @Override
2313
    protected boolean isIgnore(PesiExportState state) {
2314
        return ! state.getConfig().isDoTaxa();
2315
    }
2316
}
(13-13/14)