Project

General

Profile

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

    
12
import java.sql.Connection;
13
import java.sql.PreparedStatement;
14
import java.sql.ResultSet;
15
import java.sql.SQLException;
16
import java.sql.Types;
17
import java.util.ArrayList;
18
import java.util.BitSet;
19
import java.util.HashMap;
20
import java.util.HashSet;
21
import java.util.Iterator;
22
import java.util.List;
23
import java.util.Set;
24
import java.util.UUID;
25

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

    
34
import com.yourkit.api.Controller;
35

    
36
import eu.etaxonomy.cdm.api.service.TaxonServiceImpl;
37
import eu.etaxonomy.cdm.common.CdmUtils;
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.io.profiler.ProfilerController;
50
import eu.etaxonomy.cdm.model.common.Annotation;
51
import eu.etaxonomy.cdm.model.common.AnnotationType;
52
import eu.etaxonomy.cdm.model.common.CdmBase;
53
import eu.etaxonomy.cdm.model.common.Extension;
54
import eu.etaxonomy.cdm.model.common.ExtensionType;
55
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
56
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
57
import eu.etaxonomy.cdm.model.common.Language;
58
import eu.etaxonomy.cdm.model.common.Marker;
59
import eu.etaxonomy.cdm.model.common.MarkerType;
60
import eu.etaxonomy.cdm.model.common.RelationshipBase;
61
import eu.etaxonomy.cdm.model.name.BacterialName;
62
import eu.etaxonomy.cdm.model.name.BotanicalName;
63
import eu.etaxonomy.cdm.model.name.HybridRelationship;
64
import eu.etaxonomy.cdm.model.name.NameRelationship;
65
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
66
import eu.etaxonomy.cdm.model.name.NameTypeDesignationStatus;
67
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
68
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
69
import eu.etaxonomy.cdm.model.name.NonViralName;
70
import eu.etaxonomy.cdm.model.name.Rank;
71
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
72
import eu.etaxonomy.cdm.model.name.ZoologicalName;
73
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
74
import eu.etaxonomy.cdm.model.reference.Reference;
75
import eu.etaxonomy.cdm.model.taxon.Classification;
76
import eu.etaxonomy.cdm.model.taxon.Synonym;
77
import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
78
import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
79
import eu.etaxonomy.cdm.model.taxon.Taxon;
80
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
81
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
82
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
83
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
84
import eu.etaxonomy.cdm.strategy.cache.HTMLTagRules;
85
import eu.etaxonomy.cdm.strategy.cache.TagEnum;
86
import eu.etaxonomy.cdm.strategy.cache.name.BacterialNameDefaultCacheStrategy;
87
import eu.etaxonomy.cdm.strategy.cache.name.BotanicNameDefaultCacheStrategy;
88
import eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy;
89
import eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy;
90
import eu.etaxonomy.cdm.strategy.cache.name.ZooNameNoMarkerCacheStrategy;
91

    
92
/**
93
 * The export class for {@link eu.etaxonomy.cdm.model.name.TaxonNameBase TaxonNames}.<p>
94
 * Inserts into DataWarehouse database table <code>Taxon</code>.
95
 * It is divided into four phases:<p><ul>
96
 * <li>Phase 1:	Export of all {@link eu.etaxonomy.cdm.model.name.TaxonNameBase TaxonNames} except some data exported in the following phases.
97
 * <li>Phase 2:	Export of additional data: ParenTaxonFk and TreeIndex.
98
 * <li>Phase 3:	Export of additional data: Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk.
99
 * <li>Phase 4:	Export of Inferred Synonyms.</ul>
100
 * @author e.-m.lee
101
 * @date 23.02.2010
102
 *
103
 */
104
@Component
105
public class PesiTaxonExport extends PesiExportBase {
106
	private static final Logger logger = Logger.getLogger(PesiTaxonExport.class);
107
	private static final Class<? extends CdmBase> standardMethodParameter = TaxonBase.class;
108

    
109
	private static int modCount = 1000;
110
	private static final String dbTableName = "Taxon";
111
	private static final String dbTableNameSynRel = "RelTaxon";
112
	private static final String pluralString = "Taxa";
113
	private static final String parentPluralString = "Taxa";
114
	private PreparedStatement parentTaxonFk_TreeIndex_KingdomFkStmt;
115
	private PreparedStatement rankTypeExpertsUpdateStmt;
116
	private PreparedStatement rankUpdateStmt;
117
	private NomenclaturalCode nomenclaturalCode;
118
	private Integer kingdomFk;
119
	private HashMap<Rank, Rank> rank2endRankMap = new HashMap<Rank, Rank>();
120
	private List<Rank> rankList = new ArrayList<Rank>();
121
	private static final UUID uuidTreeIndex = UUID.fromString("28f4e205-1d02-4d3a-8288-775ea8413009");
122
	private AnnotationType treeIndexAnnotationType;
123
	private static ExtensionType lastActionExtensionType;
124
	private static ExtensionType lastActionDateExtensionType;
125
	private static ExtensionType expertNameExtensionType;
126
	private static ExtensionType speciesExpertNameExtensionType;
127
	private static ExtensionType cacheCitationExtensionType;
128
	public static NonViralNameDefaultCacheStrategy<?> zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
129
	public static NonViralNameDefaultCacheStrategy<?> botanicalNameStrategy = BotanicNameDefaultCacheStrategy.NewInstance();
130
	public static NonViralNameDefaultCacheStrategy<?> nonViralNameStrategy = NonViralNameDefaultCacheStrategy.NewInstance();
131
	public static NonViralNameDefaultCacheStrategy<?> bacterialNameStrategy = BacterialNameDefaultCacheStrategy.NewInstance();
132
	private static int currentTaxonId;
133
	
134
	
135
	/**
136
	 * @return the treeIndexAnnotationType
137
	 */
138
	protected AnnotationType getTreeIndexAnnotationType() {
139
		return treeIndexAnnotationType;
140
	}
141

    
142
	/**
143
	 * @param treeIndexAnnotationType the treeIndexAnnotationType to set
144
	 */
145
	protected void setTreeIndexAnnotationType(AnnotationType treeIndexAnnotationType) {
146
		this.treeIndexAnnotationType = treeIndexAnnotationType;
147
	}
148

    
149
	enum NamePosition {
150
		beginning,
151
		end,
152
		between,
153
		alone,
154
		nowhere
155
	}
156

    
157
	public PesiTaxonExport() {
158
		super();
159
	}
160

    
161
	/* (non-Javadoc)
162
	 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getStandardMethodParameter()
163
	 */
164
	@Override
165
	public Class<? extends CdmBase> getStandardMethodParameter() {
166
		return standardMethodParameter;
167
	}
168

    
169
	/* (non-Javadoc)
170
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
171
	 */
172
	@Override
173
	protected boolean doCheck(PesiExportState state) {
174
		boolean result = true;
175
		return result;
176
	}
177
	
178
	
179
	/* (non-Javadoc)
180
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
181
	 */
182
	@Override
183
	protected void doInvoke(PesiExportState state) {
184
		try {
185
			logger.info("*** Started Making " + pluralString + " ...");
186

    
187
			initPreparedStatements(state);
188
			
189
			// Stores whether this invoke was successful or not.
190
			boolean success = true;
191
	
192
			// PESI: Clear the database table Taxon.
193
			doDelete(state);
194
			
195
			// Get specific mappings: (CDM) Taxon -> (PESI) Taxon
196
			PesiExportMapping mapping = getMapping();
197
			PesiExportMapping synonymRelMapping = getSynRelMapping();
198
	
199
			// Initialize the db mapper
200
			mapping.initialize(state);
201
			synonymRelMapping.initialize(state);
202
			// Find extensionTypes
203
			lastActionExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionUuid);
204
			lastActionDateExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionDateUuid);
205
			expertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
206
			speciesExpertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
207
			cacheCitationExtensionType = (ExtensionType)getTermService().find(PesiTransformer.cacheCitationUuid);
208
			
209
			//Export Taxa..
210
			success &= doPhase01(state, mapping);
211

    
212
			//"PHASE 1b: Handle names without taxa ...
213
			success &= doNames(state);
214

    
215
			
216
			// 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
217
			success &= doPhase02(state);
218
			
219
			//PHASE 3: Add Rank data, KingdomFk, TypeNameFk ...
220
			success &= doPhase03(state);
221
			
222
			
223
			//"PHASE 4: Creating Inferred Synonyms...
224
			success &= doPhase04(state, mapping, synonymRelMapping);
225
			
226
			//updates to TaxonStatus and others
227
			success &= doPhaseUpdates(state);
228

    
229
			
230
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
231

    
232
			if (!success){
233
				state.setUnsuccessfull();
234
			}
235
			return;
236
		} catch (SQLException e) {
237
			e.printStackTrace();
238
			logger.error(e.getMessage());
239
			state.setUnsuccessfull();
240
			return;
241
		}
242
	}
243

    
244
	
245
	//TODO check if this can all be done by getTaxonStatus
246
	private boolean doPhaseUpdates(PesiExportState state) {
247
		
248
		
249
		String oldStatusFilter = "= 7 ";  //"= '" + PesiTransformer.T_STATUS_STR_UNACCEPTED + "' ";
250
		String emStr = PesiTransformer.SOURCE_STR_EM;
251
		String feStr = PesiTransformer.SOURCE_STR_FE;
252
		String ifStr = PesiTransformer.SOURCE_STR_IF;
253
		
254
		//NOT ACCEPTED names
255
		String updateNotAccepted = " UPDATE Taxon SET TaxonStatusFk = %d, TaxonStatusCache = '%s' " +
256
				" WHERE OriginalDB = '%s' AND taxonstatusfk = 1 AND ParentTaxonFk %s AND RankFk > 180 ";
257
		updateNotAccepted = String.format(updateNotAccepted, 8, "NOT ACCEPTED: TAXONOMICALLY VALUELESS LOCAL OR SINGULAR BIOTYPE", emStr, oldStatusFilter);
258
		int updated = state.getConfig().getDestination().update(updateNotAccepted);
259
		
260
		//alternative names
261
		String updateAlternativeName = "UPDATE Taxon SET TaxonStatusFk = 1, TaxonStatusCache = 'accepted' " + 
262
				" FROM RelTaxon RIGHT OUTER JOIN Taxon ON RelTaxon.TaxonFk1 = Taxon.TaxonId " +
263
				" WHERE (RelTaxon.RelTaxonQualifierFk = 17) AND (Taxon.TaxonStatusFk %s) ";
264
		updateAlternativeName = String.format(updateAlternativeName, oldStatusFilter);
265
		updated = state.getConfig().getDestination().update(updateAlternativeName);
266
		
267
		String updateSynonyms = " UPDATE Taxon SET TaxonStatusFk = 2, TaxonStatusCache = 'synonym' " + 
268
					" FROM RelTaxon RIGHT OUTER JOIN Taxon ON RelTaxon.TaxonFk1 = Taxon.TaxonId " + 
269
					" WHERE (RelTaxon.RelTaxonQualifierFk in (1, 3)) AND (Taxon.TaxonStatusFk %S)";
270
		updateSynonyms = String.format(updateSynonyms, oldStatusFilter);
271
		updated = state.getConfig().getDestination().update(updateSynonyms);
272
		
273
		// cache citation  - check if this can't be done in getCacheCitation
274
		// cache citation - FE
275
//		String updateCacheCitationFE = " UPDATE Taxon " +
276
//				" SET CacheCitation = IsNull(SpeciesExpertName + '. ', '') + WebShowName + '. Accessed through: Fauna Europaea at http://www.faunaeur.org/full_results.php?id=' + cast(TempFE_Id as varchar) " +
277
//				" WHERE OriginalDb = '%s'";
278
//		updateCacheCitationFE = String.format(updateCacheCitationFE, feStr);
279
//		updated = state.getConfig().getDestination().update(updateCacheCitationFE);
280
		
281
		// cache citation - EM
282
		String updateCacheCitationEM = " UPDATE Taxon " +
283
				" SET CacheCitation = SpeciesExpertName + ' ' + WebShowName + '. Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=' + GUID " +
284
				" WHERE OriginalDb = '%s'";
285
		updateCacheCitationEM = String.format(updateCacheCitationEM, emStr);
286
		updated = state.getConfig().getDestination().update(updateCacheCitationEM);
287
		
288
		// cache citation - IF
289
//		String updateCacheCitationIF = " UPDATE Taxon " +
290
//				" SET CacheCitation = IsNull(SpeciesExpertName + ' ', '') + WebShowName + '. Accessed through: Index Fungorum at http://www.indexfungorum.org/names/NamesRecord.asp?RecordID=' + cast(TempIF_Id as varchar) " +
291
//				" WHERE OriginalDb = '%s'";
292
//		updateCacheCitationIF = String.format(updateCacheCitationIF, ifStr);
293
//		updated = state.getConfig().getDestination().update(updateCacheCitationIF);
294
		
295
		return true;
296
	}
297

    
298
	private void initPreparedStatements(PesiExportState state) throws SQLException {
299
		initTreeIndexStatement(state);
300
		initRankExpertsUpdateStmt(state);
301
		initRankUpdateStatement(state);
302
	}
303

    
304
	// Prepare TreeIndex-And-KingdomFk-Statement
305
	private void initTreeIndexStatement(PesiExportState state) throws SQLException {
306
		Connection connection = state.getConfig().getDestination().getConnection();
307
		String parentTaxonFk_TreeIndex_KingdomFkSql = "UPDATE Taxon SET ParentTaxonFk = ?, TreeIndex = ? WHERE TaxonId = ?"; 
308
		parentTaxonFk_TreeIndex_KingdomFkStmt = connection.prepareStatement(parentTaxonFk_TreeIndex_KingdomFkSql);
309
	}
310

    
311
	private void initRankUpdateStatement(PesiExportState state) throws SQLException {
312
		Connection connection = state.getConfig().getDestination().getConnection();
313
		String rankSql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, KingdomFk = ? WHERE TaxonId = ?";
314
		rankUpdateStmt = connection.prepareStatement(rankSql);
315
	}
316

    
317
	private void initRankExpertsUpdateStmt(PesiExportState state) throws SQLException {
318
//		String sql_old = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ?, " +
319
//				"ExpertFk = ?, SpeciesExpertFk = ? WHERE TaxonId = ?";
320
		//TODO handle experts GUIDs
321
		Connection connection = state.getConfig().getDestination().getConnection();
322
		
323
		String sql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ? " +
324
				" WHERE TaxonId = ?";
325
		rankTypeExpertsUpdateStmt = connection.prepareStatement(sql);
326
	}
327

    
328
	private boolean doPhase01(PesiExportState state, PesiExportMapping mapping) throws SQLException {
329
		int count = 0;
330
		int pastCount = 0;
331
		List<TaxonBase> list;
332
		boolean success = true;
333
		// Get the limit for objects to save within a single transaction.
334
		int limit = state.getConfig().getLimitSave();
335

    
336
		
337
		logger.info("PHASE 1: Export Taxa...limit is " + limit);
338
		// Start transaction
339
		TransactionStatus txStatus = startTransaction(true);
340
		logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
341
		
342
		
343
		
344
		int partitionCount = 0;
345

    
346
		logger.warn("Taking snapshot at the beginning of phase 1 of taxonExport");
347
		ProfilerController.memorySnapshot();
348
		while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)) != null   ) {
349
			
350
			logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
351
			for (TaxonBase<?> taxon : list) {
352
				doCount(count++, modCount, pluralString);
353
				TaxonNameBase<?,?> taxonName = taxon.getName();
354
				NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
355
								
356
				if (! nvn.isProtectedTitleCache()){
357
					nvn.setTitleCache(null, false);	
358
				}
359
				if (! nvn.isProtectedNameCache()){
360
					nvn.setNameCache(null, false);	
361
				}
362
				if (! nvn.isProtectedFullTitleCache()){
363
					nvn.setFullTitleCache(null, false);	
364
				}
365
				if (! nvn.isProtectedAuthorshipCache()){
366
					nvn.setAuthorshipCache(null, false);	
367
				}
368
				
369
				success &= mapping.invoke(taxon);
370
				
371
				validatePhaseOne(taxon, nvn);
372
				taxon = null;
373
				nvn = null;
374
				taxonName = null;
375

    
376
				
377
				
378
			}
379
			
380

    
381
			// Commit transaction
382
			commitTransaction(txStatus);
383
			logger.debug("Committed transaction.");
384
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
385
			pastCount = count;
386
			/*logger.warn("Taking snapshot at the end of the loop of phase 1 of taxonExport");
387
			ProfilerController.memorySnapshot();
388
			*/
389
			// Start transaction
390
			txStatus = startTransaction(true);
391
			logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
392
			
393
		}
394
		if (list == null ) {
395
			logger.info("No " + pluralString + " left to fetch.");
396
		}
397
		
398
		
399
		
400
		// Commit transaction
401
		commitTransaction(txStatus);
402
		txStatus = null;
403
		logger.debug("Committed transaction.");
404
		list = null;
405
		logger.warn("Taking snapshot at the end of phase 1 of taxonExport");
406
		ProfilerController.memorySnapshot();
407
		return success;
408
	}
409

    
410

    
411
	private void validatePhaseOne(TaxonBase<?> taxon, NonViralName taxonName) {
412
		// Check whether some rules are violated
413
		nomenclaturalCode = taxonName.getNomenclaturalCode();
414
		String genusOrUninomial = taxonName.getGenusOrUninomial();
415
		String specificEpithet = taxonName.getSpecificEpithet();
416
		String infraSpecificEpithet = taxonName.getInfraSpecificEpithet();
417
		String infraGenericEpithet = taxonName.getInfraGenericEpithet();
418
		Integer rank = getRankFk(taxonName, nomenclaturalCode);
419
		
420
		if (rank == null) {
421
			logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
422
		} else {
423
			
424
			// Check whether infraGenericEpithet is set correctly
425
			// 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
426
			// 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
427
			
428
			int ancestorLevel = 0;
429
			if (taxonName.getRank().equals(Rank.SUBSPECIES())) {
430
				// The accepted taxon two rank levels above should be of rank subgenus
431
				ancestorLevel  = 2;
432
			}
433
			if (taxonName.getRank().equals(Rank.SPECIES())) {
434
				// The accepted taxon one rank level above should be of rank subgenus
435
				ancestorLevel = 1;
436
			}
437
			if (ancestorLevel > 0) {
438
				if (validateAncestorOfSpecificRank(taxon, ancestorLevel, Rank.SUBGENUS())) {
439
					// The child (species or subspecies) of this parent (subgenus) has to have an infraGenericEpithet
440
					if (infraGenericEpithet == null) {
441
						logger.warn("InfraGenericEpithet does not exist even though it should for: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
442
						// maybe the taxon could be named here
443
					}
444
				}
445
			}
446
			
447
			if (infraGenericEpithet == null && rank.intValue() == 190) {
448
				logger.warn("InfraGenericEpithet was not determined although it should exist for rank 190: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
449
			}
450
			if (specificEpithet != null && rank.intValue() < 216) {
451
				logger.warn("SpecificEpithet was determined for rank " + rank + " although it should only exist for ranks higher or equal to 220: TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
452
			}
453
			if (infraSpecificEpithet != null && rank.intValue() < 225) {
454
				String message = "InfraSpecificEpithet '" +infraSpecificEpithet + "' was determined for rank " + rank + " although it should only exist for ranks higher or equal to 230: "  + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")"; 
455
				if (StringUtils.isNotBlank(infraSpecificEpithet)){
456
					logger.warn(message);
457
				}else{
458
					logger.warn(message);
459
				}
460
			}
461
		}
462
		if (infraSpecificEpithet != null && specificEpithet == null) {
463
			logger.warn("An infraSpecificEpithet was determined, but a specificEpithet was not determined: "  + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
464
		}
465
		if (genusOrUninomial == null) {
466
			logger.warn("GenusOrUninomial was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
467
		}
468
	}
469

    
470
	// 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
471
	private boolean doPhase02(PesiExportState state) {
472
		boolean success = true;
473
		if (! state.getConfig().isDoTreeIndex()){
474
			logger.info ("Ignore PHASE 2: ParentTaxonFk and TreeIndex");
475
			return success;
476
		}
477
		
478
		List<Classification> classificationList = null;
479
		logger.info("PHASE 2: Add ParenTaxonFk and TreeIndex...");
480
		
481
		// Specify starting ranks for tree traversing
482
		rankList.add(Rank.KINGDOM());
483
		rankList.add(Rank.GENUS());
484

    
485
		// Specify where to stop traversing (value) when starting at a specific Rank (key)
486
		rank2endRankMap.put(Rank.GENUS(), null); // Since NULL does not match an existing Rank, traverse all the way down to the leaves
487
		rank2endRankMap.put(Rank.KINGDOM(), Rank.GENUS()); // excludes rank genus
488
		
489
		StringBuffer treeIndex = new StringBuffer();
490
		
491
		// Retrieve list of classifications
492
		TransactionStatus txStatus = startTransaction(true);
493
		logger.info("Started transaction for parentFk and treeIndex. Fetching all classifications...");
494
		classificationList = getClassificationService().listClassifications(null, 0, null, null);
495
		commitTransaction(txStatus);
496
		logger.debug("Committed transaction.");
497

    
498
		logger.info("Fetched " + classificationList.size() + " classification(s).");
499

    
500
		setTreeIndexAnnotationType(getAnnotationType(uuidTreeIndex, "TreeIndex", "TreeIndex", "TI"));
501
		List<TaxonNode> rankSpecificRootNodes;
502
		for (Classification classification : classificationList) {
503
			for (Rank rank : rankList) {
504
				
505
				txStatus = startTransaction(true);
506
				logger.info("Started transaction to fetch all rootNodes specific to Rank " + rank.getLabel() + " ...");
507

    
508
				rankSpecificRootNodes = getClassificationService().loadRankSpecificRootNodes(classification, rank, null);
509
				logger.info("Fetched " + rankSpecificRootNodes.size() + " RootNodes for Rank " + rank.getLabel());
510

    
511
				commitTransaction(txStatus);
512
				logger.debug("Committed transaction.");
513

    
514
				for (TaxonNode rootNode : rankSpecificRootNodes) {
515
					txStatus = startTransaction(false);
516
					Rank endRank = rank2endRankMap.get(rank);
517
					if (endRank != null) {
518
						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till Rank " + endRank.getLabel() + " ...");
519
					} else {
520
						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till leaves are reached ...");
521
					}
522

    
523
					TaxonNode newNode = getTaxonNodeService().load(rootNode.getUuid());
524

    
525
					if (isPesiTaxon(newNode.getTaxon())){
526
						TaxonNode parentNode = newNode.getParent();
527
						if (rank.equals(Rank.KINGDOM())) {
528
							treeIndex = new StringBuffer();
529
							treeIndex.append("#");
530
						} else {
531
							// Get treeIndex from parentNode
532
							if (parentNode != null) {
533
								boolean annotationFound = false;
534
								Set<Annotation> annotations = parentNode.getAnnotations();
535
								for (Annotation annotation : annotations) {
536
									AnnotationType annotationType = annotation.getAnnotationType();
537
									if (annotationType != null && annotationType.equals(getTreeIndexAnnotationType())) {
538
										treeIndex = new StringBuffer(CdmUtils.Nz(annotation.getText()));
539
										annotationFound = true;
540
	//									logger.error("treeIndex: " + treeIndex);
541
										break;
542
									}
543
								}
544
								if (!annotationFound) {
545
									// This should not happen because it means that the treeIndex was not set correctly as an annotation to parentNode
546
									logger.error("TreeIndex could not be read from annotation of TaxonNode: " + parentNode.getUuid() + ", Taxon: " + parentNode.getTaxon().getUuid());
547
									treeIndex = new StringBuffer();
548
									treeIndex.append("#");
549
								}
550
							} else {
551
								// TreeIndex could not be determined, but it's unclear how to proceed to generate a correct treeIndex if the parentNode is NULL
552
								logger.error("ParentNode for RootNode is NULL. TreeIndex could not be determined: " + newNode.getUuid());
553
								treeIndex = new StringBuffer(); // This just prevents growing of the treeIndex in a wrong manner
554
								treeIndex.append("#");
555
							}
556
						}
557
						nomenclaturalCode = newNode.getTaxon().getName().getNomenclaturalCode();
558
						kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
559
						traverseTree(newNode, parentNode, treeIndex, endRank, state);
560
						parentNode =null;
561
					}else{
562
						logger.debug("Taxon is not a PESI taxon: " + newNode.getTaxon().getUuid());
563
					}
564
					
565
					newNode = null;
566
					
567
					try {
568
						commitTransaction(txStatus);
569
						logger.debug("Committed transaction.");
570
					} catch (Exception e) {
571
						logger.error(e.getMessage());
572
						e.printStackTrace();
573
					}
574

    
575
				}
576
				rankSpecificRootNodes = null;
577
			}
578
			
579
		}
580
		
581
		logger.warn("Taking snapshot at the end of phase 2 of taxonExport");
582
		ProfilerController.memorySnapshot();
583
		return success;
584
	}	
585

    
586
	//PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...
587
	private boolean doPhase03(PesiExportState state) {
588
		int count = 0;
589
		int pastCount = 0;
590
		boolean success = true;
591
		if (! state.getConfig().isDoTreeIndex()){
592
			logger.info ("Ignore PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
593
			return success;
594
		}
595
		// Get the limit for objects to save within a single transaction.
596
		int limit = state.getConfig().getLimitSave();
597

    
598
		List<TaxonBase> list;
599
		logger.info("PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
600
		// Be sure to add rank information, KingdomFk, TypeNameFk, expertFk and speciesExpertFk to every taxonName
601
		
602
		// Start transaction
603
		TransactionStatus txStatus = startTransaction(true);
604
		logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
605
		int partitionCount = 0;
606
		while ((list = getNextTaxonPartition(TaxonBase.class, limit, partitionCount++, null)) != null) {
607

    
608
			logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
609
			for (TaxonBase<?> taxon : list) {
610
				TaxonNameBase<?,?> taxonName = taxon.getName();
611
				// Determine expertFk
612
//				Integer expertFk = makeExpertFk(state, taxonName);
613
//
614
//				// Determine speciesExpertFk
615
//				Integer speciesExpertFk = makeSpeciesExpertFk(state, taxonName);
616

    
617
				doCount(count++, modCount, pluralString);
618
				Integer typeNameFk = getTypeNameFk(taxonName, state);
619
				
620
				//TODO why are expertFks needed? (Andreas M.)
621
//				if (expertFk != null || speciesExpertFk != null) {
622
					invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, nomenclaturalCode, state.getDbId(taxon), 
623
							typeNameFk, kingdomFk);
624
//				}
625
					
626
					taxon = null;
627
					taxonName = null;
628
			}
629

    
630
			// Commit transaction
631
			commitTransaction(txStatus);
632
			logger.debug("Committed transaction.");
633
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
634
			pastCount = count;
635

    
636
			// Start transaction
637
			txStatus = startTransaction(true);
638
			logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
639
		}
640
		if (list == null) {
641
			logger.info("No " + pluralString + " left to fetch.");
642
		}
643
		
644
		list = null;
645
		
646
		// Commit transaction
647
		commitTransaction(txStatus);
648
		
649
		logger.debug("Committed transaction.");
650
		logger.warn("Taking snapshot at the end of phase 3 of taxonExport, number of partitions: " + partitionCount);
651
		ProfilerController.memorySnapshot();
652
		return success;
653
	}
654
	
655
	//	"PHASE 4: Creating Inferred Synonyms..."
656
	private boolean doPhase04(PesiExportState state, PesiExportMapping mapping, PesiExportMapping synRelMapping) throws SQLException {
657
		int count;
658
		int pastCount;
659
		boolean success = true;
660
		// Get the limit for objects to save within a single transaction.
661
		if (! state.getConfig().isDoInferredSynonyms()){
662
			logger.info ("Ignore PHASE 4: Creating Inferred Synonyms...");
663
			return success;
664
		}
665
		
666
		int limit = state.getConfig().getLimitSave();
667
		// Create inferred synonyms for accepted taxa
668
		logger.info("PHASE 4: Creating Inferred Synonyms...");
669

    
670
		// Determine the count of elements in datawarehouse database table Taxon
671
		currentTaxonId = determineTaxonCount(state);
672
		currentTaxonId++;
673

    
674
		count = 0;
675
		pastCount = 0;
676
		int pageSize = limit;
677
		int pageNumber = 1;
678
		String inferredSynonymPluralString = "Inferred Synonyms";
679
		
680
		// Start transaction
681
		TransactionStatus txStatus = startTransaction(true);
682
		logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
683
		List<TaxonBase> taxonList = null;
684
		
685
		
686
		
687
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", Rank.SPECIES(), pageSize, pageNumber)).size() > 0) {
688
			HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
689

    
690
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
691
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
692
					synRelMapping, taxonList));
693
			
694
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
695
			// Commit transaction
696
			commitTransaction(txStatus);
697
			logger.debug("Committed transaction.");
698
			logger.info("Exported " + (taxonList.size()) + " " + inferredSynonymPluralString + ". Total: " + count);
699
			//pastCount = count;
700
			
701
			// Save Rank Data and KingdomFk for inferred synonyms
702
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
703
				invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomenclaturalCode, taxonFk, kingdomFk);
704
			}
705

    
706
			// Start transaction
707
			txStatus = startTransaction(true);
708
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
709
			
710
			// Increment pageNumber
711
			pageNumber++;
712
		}
713
		
714
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", Rank.SUBSPECIES(), pageSize, pageNumber)).size() > 0) {
715
			HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
716

    
717
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
718
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
719
					synRelMapping, taxonList));
720
			;
721
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
722
			// Commit transaction
723
			commitTransaction(txStatus);
724
			logger.debug("Committed transaction.");
725
			logger.info("Exported " + taxonList.size()+ " " + inferredSynonymPluralString + ". Total: " + count);
726
			//pastCount = count;
727
			
728
			// Save Rank Data and KingdomFk for inferred synonyms
729
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
730
				invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomenclaturalCode, taxonFk, kingdomFk);
731
			}
732

    
733
			// Start transaction
734
			txStatus = startTransaction(true);
735
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
736
			
737
			// Increment pageNumber
738
			pageNumber++;
739
		}
740
		if (taxonList.size() == 0) {
741
			logger.info("No " + parentPluralString + " left to fetch.");
742
		}
743
		
744
		taxonList = null;
745
		logger.warn("Taking snapshot at the end of phase 4 of taxonExport");
746
		ProfilerController.memorySnapshot();
747
		
748
		// Commit transaction
749
		commitTransaction(txStatus);
750
		logger.warn("Taking snapshot at the end of phase 4 after commit of taxonExport");
751
		ProfilerController.memorySnapshot();
752
		System.gc();
753
		logger.warn("Taking snapshot at the end of phase 4 after gc() of taxonExport");
754
		ProfilerController.memorySnapshot();
755
		logger.debug("Committed transaction.");
756
		return success;
757
	}
758

    
759
	/**
760
	 * @param state
761
	 * @param mapping
762
	 * @param synRelMapping
763
	 * @param currentTaxonId
764
	 * @param taxonList
765
	 * @param inferredSynonymsDataToBeSaved
766
	 * @return
767
	 */
768
	private HashMap<Integer, TaxonNameBase<?, ?>> createInferredSynonymsForTaxonList(PesiExportState state,
769
			PesiExportMapping mapping, PesiExportMapping synRelMapping,
770
			 List<TaxonBase> taxonList) {
771
		
772
		Taxon acceptedTaxon;
773
		Classification classification = null;
774
		List<Synonym> inferredSynonyms = null;
775
		boolean localSuccess = true;
776
		
777
		HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
778
		
779
		for (TaxonBase<?> taxonBase : taxonList) {
780
		
781
			if (taxonBase.isInstanceOf(Taxon.class)) { // this should always be the case since we should have fetched accepted taxon only, but you never know...
782
				acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
783
				TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();
784
				
785
				if (taxonName.isInstanceOf(ZoologicalName.class)) {
786
					nomenclaturalCode  = taxonName.getNomenclaturalCode();
787
					kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
788

    
789
					Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
790
					TaxonNode singleNode = null;
791
					
792
					if (taxonNodes.size() > 0) {
793
						// Determine the classification of the current TaxonNode
794
						
795
						singleNode = taxonNodes.iterator().next();
796
						if (singleNode != null) {
797
							classification = singleNode.getClassification();
798
						} else {
799
							logger.error("A TaxonNode belonging to this accepted Taxon is NULL: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() +")");
800
						}
801
					} else {
802
						// Classification could not be determined directly from this TaxonNode
803
						// The stored classification from another TaxonNode is used. It's a simple, but not a failsafe fallback solution.
804
						if (taxonNodes.size() == 0) {
805
							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");
806
						
807
						}
808
					}
809
					
810
					if (classification != null) {
811
						try{
812
							TaxonNameBase name = acceptedTaxon.getName();
813
							//if (name.isSpecies() || name.isInfraSpecific()){
814
								inferredSynonyms  = getTaxonService().createAllInferredSynonyms(acceptedTaxon, classification, true);
815
							//}
816
//								inferredSynonyms = getTaxonService().createInferredSynonyms(classification, acceptedTaxon, SynonymRelationshipType.INFERRED_GENUS_OF());
817
							if (inferredSynonyms != null) {
818
								for (Synonym synonym : inferredSynonyms) {
819
//									TaxonNameBase<?,?> synonymName = synonym.getName();
820
									MarkerType markerType =getUuidMarkerType(PesiTransformer.uuidMarkerGuidIsMissing, state);
821
									synonym.addMarker(Marker.NewInstance(markerType, true));
822
									// Both Synonym and its TaxonName have no valid Id yet
823
									synonym.setId(currentTaxonId++);
824
									
825
									
826
									localSuccess &= mapping.invoke(synonym);
827
									//get SynonymRelationship and export
828
									if (synonym.getSynonymRelations().isEmpty() ){
829
										SynonymRelationship synRel;					
830
										IdentifiableSource source = synonym.getSources().iterator().next();
831
										if (source.getIdNamespace().contains("Potential combination")){
832
											synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
833
											logger.warn(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to potential combination");
834
										} else if (source.getIdNamespace().contains("Inferred Genus")){
835
											synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.INFERRED_GENUS_OF());
836
											logger.warn(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to inferred genus");
837
										} else if (source.getIdNamespace().contains("Inferred Epithet")){
838
											synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.INFERRED_EPITHET_OF());
839
											logger.warn(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to inferred epithet");
840
										} else{
841
											synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.INFERRED_SYNONYM_OF());
842
											logger.warn(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to inferred synonym");
843
										}
844
										
845
										localSuccess &= synRelMapping.invoke(synRel);
846
										if (!localSuccess) {
847
											logger.warn("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
848
										}
849
										synRel = null;
850
									} else {
851
										for (SynonymRelationship synRel: synonym.getSynonymRelations()){
852
											localSuccess &= synRelMapping.invoke(synRel);
853
											if (!localSuccess) {
854
												logger.warn("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
855
											}
856
											synRel = null;
857
										}
858
									}
859
									
860
									inferredSynonymsDataToBeSaved.put(synonym.getId(), synonym.getName());
861
								}
862
							}
863
						}catch(Exception e){
864
							logger.error(e.getMessage());
865
							e.printStackTrace();
866
						}
867
					} else {
868
						logger.error("Classification is NULL. Inferred Synonyms could not be created for this Taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() + ")");
869
					}
870
				} else {
871
//							logger.error("TaxonName is not a ZoologicalName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
872
				}
873
			} else {
874
				logger.error("This TaxonBase is not a Taxon even though it should be: " + taxonBase.getUuid() + " (" + taxonBase.getTitleCache() + ")");
875
			}
876
		}
877
		return inferredSynonymsDataToBeSaved;
878
	}
879
	
880

    
881
	/**
882
	 * Handles names that do not appear in taxa
883
	 * @param state
884
	 * @param mapping
885
	 * @return
886
	 */
887
	private boolean doNames(PesiExportState state)  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();
897
			mapping.initialize(state);
898
			int count = 0;
899
			int pastCount = 0;
900
			List<NonViralName<?>> list;
901
			success = true;
902
			// Get the limit for objects to save within a single transaction.
903
			int limit = state.getConfig().getLimitSave();
904

    
905
			
906
			logger.info("PHASE 1b: Export Pure Names ...");
907
			// Start transaction
908
			TransactionStatus txStatus = startTransaction(true);
909
			logger.info("Started new transaction for Pure Names. Fetching some " + pluralString + " (max: " + limit + ") ...");
910
			
911
			
912
			int partitionCount = 0;
913
			while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null   ) {
914

    
915
				logger.info("Fetched " + list.size() + " names without taxa. Exporting...");
916
				for (TaxonNameBase taxonName : list) {
917
					doCount(count++, modCount, pluralString);
918
					success &= mapping.invoke(taxonName);
919
				}
920

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

    
927
				// Start transaction
928
				txStatus = startTransaction(true);
929
				logger.info("Started new transaction for PureNames. Fetching some " + pluralString + " (max: " + limit + ") ...");
930
			}
931
			if (list == null) {
932
				logger.info("No " + pluralString + " left to fetch.");
933
			}
934
			// Commit transaction
935
			commitTransaction(txStatus);
936
			logger.debug("Committed transaction.");
937
		} catch (Exception e) {
938
			logger.error("Error occurred in pure name export");
939
			e.printStackTrace();
940
			success = false;
941
		}
942
		return success;
943
	}
944

    
945
	/**
946
	 * Determines the current number of entries in the DataWarehouse database table <code>Taxon</code>.
947
	 * @param state The {@link PesiExportState PesiExportState}.
948
	 * @return The count.
949
	 */
950
	private Integer determineTaxonCount(PesiExportState state) {
951
		Integer result = null;
952
		PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
953
		
954
		String sql;
955
		Source destination =  pesiConfig.getDestination();
956
		sql = "SELECT max(taxonId) FROM Taxon";
957
		destination.setQuery(sql);
958
		ResultSet resultSet = destination.getResultSet();
959
		try {
960
			resultSet.next();
961
			result = resultSet.getInt(1);
962
		} catch (SQLException e) {
963
			logger.error("TaxonCount could not be determined: " + e.getMessage());
964
			e.printStackTrace();
965
		}
966
		return result;
967
	}
968
	
969
	/**
970
	 * Checks whether a parent at specific level has a specific Rank.
971
	 * @param taxonName A {@link TaxonNameBase TaxonName}.
972
	 * @param level The ancestor level.
973
	 * @param ancestorRank The ancestor rank.
974
	 * @return Whether a parent at a specific level has a specific Rank.
975
	 */
976
	private boolean validateAncestorOfSpecificRank(TaxonBase<?> taxonBase, int level, Rank ancestorRank) {
977
		boolean result = false;
978
		TaxonNode parentNode = null;
979
		if (taxonBase.isInstanceOf(Taxon.class)){
980
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
981
			// Get ancestor Taxon via TaxonNode
982
			Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();
983
			if (taxonNodes.size() == 1) {
984
				TaxonNode taxonNode = taxonNodes.iterator().next();
985
				if (taxonNode != null) {
986
					for (int i = 0; i < level; i++) {
987
						if (taxonNode != null) {
988
							taxonNode  = taxonNode.getParent();
989
						}
990
					}
991
					parentNode = taxonNode;
992
				}
993
			} else if (taxonNodes.size() > 1) {
994
				logger.error("This taxon has " + taxonNodes.size() + " taxonNodes: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
995
			}
996
		}
997
		//compare
998
		if (parentNode != null) {
999
			TaxonNode node = CdmBase.deproxy(parentNode, TaxonNode.class);
1000
			Taxon parentTaxon = node.getTaxon();
1001
			if (parentTaxon != null) {
1002
				TaxonNameBase<?,?> parentTaxonName = parentTaxon.getName();
1003
				if (parentTaxonName != null && parentTaxonName.getRank().equals(ancestorRank)) {
1004
					result = true;
1005
				}
1006
			} else {
1007
				logger.error("This TaxonNode has no Taxon: " + node.getUuid());
1008
			}
1009
		}
1010
		return result;
1011
	}
1012

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

    
1032
	/**
1033
	 * Traverses the classification recursively and stores determined values for every Taxon.
1034
	 * @param childNode The {@link TaxonNode TaxonNode} to process.
1035
	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1036
	 * @param treeIndex The TreeIndex at the current level.
1037
	 * @param fetchLevel Rank to stop fetching at.
1038
	 * @param state The {@link PesiExportState PesiExportState}.
1039
	 */
1040
	private void traverseTree(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, Rank fetchLevel, PesiExportState state) {
1041
		// Traverse all branches from this childNode until specified fetchLevel is reached.
1042
		StringBuffer localTreeIndex = new StringBuffer(treeIndex);
1043
		Taxon childTaxon = childNode.getTaxon();
1044
		if (childTaxon != null) {
1045
			if (isPesiTaxon(childTaxon)){
1046
				Integer taxonId = state.getDbId(childTaxon);
1047
				TaxonNameBase<?,?> childName = childTaxon.getName();
1048
				if (taxonId != null) {
1049
					Rank childRank = childName.getRank();
1050
					if (childRank != null) {
1051
						if (! childRank.equals(fetchLevel)) {
1052
	
1053
							localTreeIndex.append(taxonId + "#");
1054
							
1055
							saveData(childNode, parentNode, localTreeIndex, state, taxonId);
1056
	
1057
							// Store treeIndex as annotation for further use
1058
							Annotation annotation = Annotation.NewInstance(localTreeIndex.toString(), getTreeIndexAnnotationType(), Language.DEFAULT());
1059
							childNode.addAnnotation(annotation);
1060
	
1061
							for (TaxonNode newNode : childNode.getChildNodes()) {
1062
								if (newNode.getTaxon() != null && isPesiTaxon(newNode.getTaxon())){
1063
									traverseTree(newNode, childNode, localTreeIndex, fetchLevel, state);
1064
								}
1065
							}
1066
							
1067
						} else {
1068
	//						logger.debug("Target Rank " + fetchLevel.getLabel() + " reached");
1069
							return;
1070
						}
1071
					} else {
1072
						logger.error("Rank is NULL. FetchLevel can not be checked: " + childName.getUuid() + " (" + childName.getTitleCache() + ")");
1073
					}
1074
				} else {
1075
					logger.error("Taxon can not be found in state: " + childTaxon.getUuid() + " (" + childTaxon.getTitleCache() + ")");
1076
				}
1077
			}else{
1078
				if (logger.isDebugEnabled()){ 
1079
					logger.debug("Taxon is not a PESI taxon: " + childTaxon.getUuid());
1080
				}
1081
			}
1082

    
1083
		} else {
1084
			logger.error("Taxon is NULL for TaxonNode: " + childNode.getUuid());
1085
		}
1086
	}
1087

    
1088
	/**
1089
	 * Stores values in database for every recursive round.
1090
	 * @param childNode The {@link TaxonNode TaxonNode} to process.
1091
	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1092
	 * @param treeIndex The TreeIndex at the current level.
1093
	 * @param state The {@link PesiExportState PesiExportState}.
1094
	 * @param currentTaxonFk The TaxonFk to store the values for.
1095
	 */
1096
	private void saveData(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, PesiExportState state, Integer currentTaxonFk) {
1097
		// We are differentiating kingdoms by the nomenclatural code for now.
1098
		// This needs to be handled in a better way as soon as we know how to differentiate between more kingdoms.
1099
		Taxon childTaxon = childNode.getTaxon();
1100
		if (isPesiTaxon(childTaxon)) {
1101
			TaxonBase<?> parentTaxon = null;
1102
			if (parentNode != null) {
1103
				parentTaxon = parentNode.getTaxon();
1104
				
1105
			}
1106

    
1107
			invokeParentTaxonFkAndTreeIndex(state.getDbId(parentTaxon), currentTaxonFk,	treeIndex);
1108
		}
1109
		
1110
	}
1111

    
1112
	/**
1113
	 * Inserts values into the Taxon database table.
1114
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1115
	 * @param state The {@link PesiExportState PesiExportState}.
1116
	 * @param stmt The prepared statement.
1117
	 * @return Whether save was successful or not.
1118
	 */
1119
	protected boolean invokeParentTaxonFkAndTreeIndex(Integer parentTaxonFk, Integer currentTaxonFk, StringBuffer treeIndex) {
1120
		try {
1121
			if (parentTaxonFk != null) {
1122
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(1, parentTaxonFk);
1123
			} else {
1124
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(1, null);
1125
			}
1126

    
1127
			if (treeIndex != null) {
1128
				parentTaxonFk_TreeIndex_KingdomFkStmt.setString(2, treeIndex.toString());
1129
			} else {
1130
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(2, null);
1131
			}
1132

    
1133
			if (currentTaxonFk != null) {
1134
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(3, currentTaxonFk);
1135
			} else {
1136
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(3, null);
1137
			}
1138
			
1139
			parentTaxonFk_TreeIndex_KingdomFkStmt.executeUpdate();
1140
			return true;
1141
		} catch (SQLException e) {
1142
			logger.error("ParentTaxonFk (" + parentTaxonFk ==null? "-":parentTaxonFk + ") and TreeIndex could not be inserted into database for taxon "+ (currentTaxonFk == null? "-" :currentTaxonFk) + ": " + e.getMessage());
1143
			e.printStackTrace();
1144
			return false;
1145
		}
1146
	}
1147

    
1148
	/**
1149
	 * Inserts Rank data and KingdomFk into the Taxon database table.
1150
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1151
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1152
	 * @param taxonFk The TaxonFk to store the values for.
1153
	 * @param kindomFk The KingdomFk.
1154
	 * @return Whether save was successful or not.
1155
	 */
1156
	private boolean invokeRankDataAndKingdomFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, Integer taxonFk, Integer kingdomFk) {
1157
		try {
1158
			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1159
			if (rankFk != null) {
1160
				rankUpdateStmt.setInt(1, rankFk);
1161
			} else {
1162
				rankUpdateStmt.setObject(1, null);
1163
			}
1164
	
1165
			String rankCache = getRankCache(taxonName, nomenclaturalCode);
1166
			if (rankCache != null) {
1167
				rankUpdateStmt.setString(2, rankCache);
1168
			} else {
1169
				rankUpdateStmt.setObject(2, null);
1170
			}
1171
			
1172
			if (kingdomFk != null) {
1173
				rankUpdateStmt.setInt(3, kingdomFk);
1174
			} else {
1175
				rankUpdateStmt.setObject(3, null);
1176
			}
1177
			
1178
			if (taxonFk != null) {
1179
				rankUpdateStmt.setInt(4, taxonFk);
1180
			} else {
1181
				rankUpdateStmt.setObject(4, null);
1182
			}
1183
			
1184
			rankUpdateStmt.executeUpdate();
1185
			return true;
1186
		} catch (SQLException e) {
1187
			logger.error("Data (RankFk, RankCache, KingdomFk) could not be inserted into database: " + e.getMessage());
1188
			e.printStackTrace();
1189
			return false;
1190
		}
1191
	}
1192

    
1193
	/**
1194
	 * Inserts Rank data, TypeNameFk, KingdomFk, expertFk and speciesExpertFk into the Taxon database table.
1195
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1196
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1197
	 * @param taxonFk The TaxonFk to store the values for.
1198
	 * @param typeNameFk The TypeNameFk.
1199
	 * @param kindomFk The KingdomFk.
1200
	 * @param expertFk The ExpertFk.
1201
	 * @param speciesExpertFk The SpeciesExpertFk.
1202
	 * @return Whether save was successful or not.
1203
	 */
1204
	private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, 
1205
			Integer taxonFk, Integer typeNameFk, Integer kingdomFkk) {
1206
		try {
1207
			int index = 1;
1208
			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1209
			if (rankFk != null) {
1210
				rankTypeExpertsUpdateStmt.setInt(index++, rankFk);
1211
			} else {
1212
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1213
			}
1214
	
1215
			String rankCache = getRankCache(taxonName, nomenclaturalCode);
1216
			if (rankCache != null) {
1217
				rankTypeExpertsUpdateStmt.setString(index++, rankCache);
1218
			} else {
1219
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1220
			}
1221
			
1222
			if (typeNameFk != null) {
1223
				rankTypeExpertsUpdateStmt.setInt(index++, typeNameFk);
1224
			} else {
1225
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1226
			}
1227
			
1228
			if (kingdomFk != null) {
1229
				rankTypeExpertsUpdateStmt.setInt(index++, kingdomFk);
1230
			} else {
1231
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1232
			}
1233
			
1234
//			if (expertFk != null) {
1235
//				rankTypeExpertsUpdateStmt.setInt(5, expertFk);
1236
//			} else {
1237
//				rankTypeExpertsUpdateStmt.setObject(5, null);
1238
//			}
1239
//
1240
//			//TODO handle experts GUIDS
1241
//			if (speciesExpertFk != null) {
1242
//				rankTypeExpertsUpdateStmt.setInt(6, speciesExpertFk);
1243
//			} else {
1244
//				rankTypeExpertsUpdateStmt.setObject(6, null);
1245
//			}
1246
//			
1247
			if (taxonFk != null) {
1248
				rankTypeExpertsUpdateStmt.setInt(index++, taxonFk);
1249
			} else {
1250
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1251
			}
1252

    
1253
			rankTypeExpertsUpdateStmt.executeUpdate();
1254
			return true;
1255
		} catch (SQLException e) {
1256
			logger.error("Data could not be inserted into database: " + e.getMessage());
1257
			e.printStackTrace();
1258
			return false;
1259
		} catch (Exception e) {
1260
			logger.error("Some exception occurred: " + e.getMessage());
1261
			e.printStackTrace();
1262
			return false;
1263
		}
1264
	}
1265

    
1266
	/**
1267
	 * Deletes all entries of database tables related to <code>Taxon</code>.
1268
	 * @param state The {@link PesiExportState PesiExportState}.
1269
	 * @return Whether the delete operation was successful or not.
1270
	 */
1271
	protected boolean doDelete(PesiExportState state) {
1272
		PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
1273
		
1274
		String sql;
1275
		Source destination =  pesiConfig.getDestination();
1276

    
1277
		// Clear Taxon
1278
		sql = "DELETE FROM " + dbTableName;
1279
		destination.setQuery(sql);
1280
		destination.update(sql);
1281
		return true;
1282
	}
1283

    
1284
	/* (non-Javadoc)
1285
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
1286
	 */
1287
	@Override
1288
	protected boolean isIgnore(PesiExportState state) {
1289
		return ! state.getConfig().isDoTaxa();
1290
	}
1291

    
1292
	
1293
	/**
1294
	 * Creates the kingdom fk.
1295
	 * @param taxonName
1296
	 * @return
1297
	 */
1298
	@SuppressWarnings("unused")  //used by mapper
1299
	private static Integer getKingdomFk(TaxonNameBase taxonName){
1300
		return PesiTransformer.nomenClaturalCode2Kingdom(taxonName.getNomenclaturalCode());
1301
	}
1302
	
1303
	/**
1304
	 * Creates the parent fk.
1305
	 * @param taxonName
1306
	 * @return
1307
	 */
1308
	@SuppressWarnings("unused")  //used by mapper
1309
	private static Integer getParentTaxonFk(TaxonBase<?> taxonBase, PesiExportState state){
1310
		if (taxonBase.isInstanceOf(Taxon.class)){
1311
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1312
			if (! isMisappliedName(taxon)){
1313
				Set<TaxonNode> nodes = taxon.getTaxonNodes();
1314
				if (nodes.size() == 0){
1315
					if (taxon.getName().getRank().isLower(Rank.KINGDOM())){
1316
						logger.warn("Accepted taxon has no parent. " + taxon.getTitleCache() + ", " +  taxon.getUuid());
1317
					}
1318
				}else if (nodes.size() > 1){
1319
					logger.warn("Taxon has more than 1 node attached. This is not supported by PESI export." +  taxon.getTitleCache() + ", " +  taxon.getUuid());
1320
				}else{
1321
					Taxon parent =nodes.iterator().next().getParent().getTaxon();
1322
					return state.getDbId(parent);
1323
				}
1324
			}
1325
		}
1326
		return null;
1327
	}
1328

    
1329
	/**
1330
	 * Returns the rankFk for the taxon name based on the names nomenclatural code.
1331
	 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1332
	 * @param taxonName
1333
	 * @return
1334
	 */
1335
	@SuppressWarnings("unused")  //used by mapper
1336
	private static Integer getRankFk(TaxonNameBase<?,?> taxonName) {
1337
		return getRankFk(taxonName, taxonName.getNomenclaturalCode());
1338
	}
1339
		
1340
	
1341
	/**
1342
	 * Returns the <code>RankFk</code> attribute.
1343
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1344
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1345
	 * @return The <code>RankFk</code> attribute.
1346
	 * @see MethodMapper
1347
	 */
1348
	private static Integer getRankFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode) {
1349
		Integer result = null;
1350
		try {
1351
			if (nomenclaturalCode != null) {
1352
				if (taxonName != null) {
1353
					if (taxonName.getRank() == null) {
1354
						logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1355
					} else {
1356
						result = PesiTransformer.rank2RankId(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1357
					}
1358
					if (result == null) {
1359
						logger.warn("Rank could not be determined for PESI-Kingdom-Id " + PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode) + " and TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1360
					}
1361
				}
1362
			}
1363
		} catch (Exception e) {
1364
			e.printStackTrace();
1365
		}
1366
		return result;
1367
	}
1368

    
1369
	/**
1370
	 * Returns the rank cache for the taxon name based on the names nomenclatural code.
1371
	 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1372
	 * @param taxonName
1373
	 * @return
1374
	 */
1375
	@SuppressWarnings("unused")  //used by mapper
1376
	private static String getRankCache(TaxonNameBase<?,?> taxonName) {
1377
		return getRankCache(taxonName, taxonName.getNomenclaturalCode());
1378
	}
1379

    
1380
	
1381
	/**
1382
	 * Returns the <code>RankCache</code> attribute.
1383
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1384
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1385
	 * @return The <code>RankCache</code> attribute.
1386
	 * @see MethodMapper
1387
	 */
1388
	private static String getRankCache(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode) {
1389
		String result = null;
1390
		try {
1391
			if (nomenclaturalCode != null) {
1392
				result = PesiTransformer.rank2RankCache(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1393
			}
1394
		} catch (Exception e) {
1395
			e.printStackTrace();
1396
		}
1397
		return result;
1398
	}
1399

    
1400
	
1401
	/**
1402
	 * Returns the <code>DisplayName</code> attribute.
1403
	 * @param taxon The {@link TaxonBase Taxon}.
1404
	 * @return The <code>DisplayName</code> attribute.
1405
	 * @see MethodMapper
1406
	 */
1407
	@SuppressWarnings("unused")  //used by Mapper
1408
	private static String getDisplayName(TaxonBase<?> taxon) {
1409
		TaxonNameBase<?,?> taxonName = taxon.getName();
1410
		String result = getDisplayName(taxonName);
1411
		if (isMisappliedName(taxon)){
1412
			result = result + " " + getAuthorString(taxon);
1413
		}
1414
		return result;
1415
	}
1416
	
1417
	/**
1418
	 * Returns the <code>AuthorString</code> attribute.
1419
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1420
	 * @return The <code>AuthorString</code> attribute.
1421
	 * @see MethodMapper
1422
	 */
1423
	@SuppressWarnings("unused")
1424
	protected static String getAuthorString(TaxonBase<?> taxon) {
1425
		try {
1426
			String result = null;
1427
			boolean isNonViralName = false;
1428
			String authorshipCache = null;
1429
			TaxonNameBase<?,?> taxonName = taxon.getName();
1430
			if (taxonName != null && taxonName.isInstanceOf(NonViralName.class)){
1431
				authorshipCache = CdmBase.deproxy(taxonName, NonViralName.class).getAuthorshipCache();
1432
				isNonViralName = true;
1433
			}
1434
			result = authorshipCache;
1435
			
1436
			// For a misapplied names there are special rules
1437
			if (isMisappliedName(taxon)){
1438
				if (taxon.getSec() != null){
1439
					String secTitle = taxon.getSec().getTitleCache();
1440
					if (! secTitle.startsWith("auct")){
1441
						secTitle = "sensu " + secTitle;
1442
					}else if (secTitle.equals("auct")){  //may be removed once the title cache is generated correctly for references with title auct. #
1443
						secTitle = "auct.";
1444
					}
1445
					return secTitle;
1446
				}else if (StringUtils.isBlank(authorshipCache)) {
1447
					// Set authorshipCache to "auct."
1448
					result = PesiTransformer.AUCT_STRING;
1449
				}else{
1450
					result = PesiTransformer.AUCT_STRING;
1451
//					result = authorshipCache;
1452
				}
1453
			}
1454
			
1455
			if (taxonName == null){
1456
				logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1457
			}else if (! isNonViralName){
1458
				logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1459
			}
1460
			
1461
			if (StringUtils.isBlank(result)) {
1462
				return null;
1463
			} else {
1464
				return result;
1465
			}
1466
		} catch (Exception e) {
1467
			e.printStackTrace();
1468
			return null;
1469
		}
1470
		
1471
	}
1472
		
1473
	
1474
	/**
1475
	 * Returns the <code>DisplayName</code> attribute.
1476
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1477
	 * @return The <code>DisplayName</code> attribute.
1478
	 * @see MethodMapper
1479
	 */
1480
	@SuppressWarnings("unused")  //used by Mapper
1481
	private static String getDisplayName(TaxonNameBase<?,?> taxonName) {
1482
		// TODO: extension?
1483
		if (taxonName == null) {
1484
			return null;
1485
		}else{
1486
			INonViralNameCacheStrategy<NonViralName<?>> cacheStrategy = getCacheStrategy(taxonName);
1487
			HTMLTagRules tagRules = new HTMLTagRules().
1488
					addRule(TagEnum.name, "i").
1489
					addRule(TagEnum.nomStatus, "@status@");
1490
			
1491
			NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1492
			String result = cacheStrategy.getFullTitleCache(nvn, tagRules);
1493
			cacheStrategy = null;
1494
			nvn = null;
1495
			return result.replaceAll("\\<@status@\\>.*\\</@status@\\>", "");
1496
		}
1497
	}
1498
	
1499

    
1500
	/**
1501
	 * Returns the <code>WebShowName</code> attribute for a taxon.
1502
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1503
	 * @return The <code>WebShowName</code> attribute.
1504
	 * @see MethodMapper
1505
	*/
1506
	@SuppressWarnings("unused")
1507
	private static String getWebShowName(TaxonBase<?> taxon) {
1508
		TaxonNameBase<?,?> taxonName = taxon.getName();
1509
		String result = getWebShowName(taxonName);
1510
		if (isMisappliedName(taxon)){
1511
			result = result + " " + getAuthorString(taxon);
1512
		}
1513
		return result;
1514
	}
1515
	
1516
	/**
1517
	 * Returns the <code>WebShowName</code> attribute.
1518
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1519
	 * @return The <code>WebShowName</code> attribute.
1520
	 * @see MethodMapper
1521
	 */
1522
	private static String getWebShowName(TaxonNameBase<?,?> taxonName) {
1523
		//TODO extensions?
1524
		if (taxonName == null) {
1525
			return null;
1526
		}else{
1527
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1528
		
1529
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1530
			NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1531
			String result = cacheStrategy.getTitleCache(nvn, tagRules);
1532
			cacheStrategy = null;
1533
			nvn = null;
1534
			return result;
1535
		}
1536
	}
1537

    
1538
 	
1539
	/**
1540
	 * Returns the <code>WebSearchName</code> attribute.
1541
	 * @param taxonName The {@link NonViralName NonViralName}.
1542
	 * @return The <code>WebSearchName</code> attribute.
1543
	 * @see MethodMapper
1544
	 */
1545
	@SuppressWarnings("unused")
1546
	private static String getWebSearchName(TaxonNameBase taxonName) {
1547
		//TODO extensions?
1548
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1549
		NonViralNameDefaultCacheStrategy strategy = getCacheStrategy(nvn);
1550
		String result = strategy.getNameCache(nvn);
1551
		strategy = null;
1552
		nvn = null;
1553
		return result;
1554
	}
1555

    
1556

    
1557
	/**
1558
	 * Returns the <code>FullName</code> attribute.
1559
	 * @param taxonName The {@link NonViralName NonViralName}.
1560
	 * @return The <code>FullName</code> attribute.
1561
	 * @see MethodMapper
1562
	 */
1563
	@SuppressWarnings("unused")
1564
	private static String getFullName(TaxonNameBase taxonName) {
1565
		//TODO extensions?
1566
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1567
		String result = getCacheStrategy(nvn).getTitleCache(nvn);
1568
		Iterator<TaxonBase> taxa = taxonName.getTaxa().iterator();
1569
		if (taxonName.getTaxa().size() >0){
1570
			if (taxonName.getTaxa().size() == 1){
1571
				TaxonBase taxon = taxa.next();
1572
				if (isMisappliedName(taxon)){
1573
					result = result + " " + getAuthorString(taxon);
1574
				}
1575
				taxon = null;
1576
			}
1577
		}
1578
		taxa = null;
1579
		nvn = null;
1580
		return result;
1581
	}
1582
	
1583
	/**
1584
	 * Returns the <code>FullName</code> attribute.
1585
	 * @param taxon The {@link TaxonBase taxon}.
1586
	 * @return The <code>FullName</code> attribute.
1587
	 * @see MethodMapper
1588
	 */
1589
	/*@SuppressWarnings("unused")
1590
	private static String getFullName(TaxonBase taxon) {
1591
		//TODO extensions?
1592
		TaxonNameBase name = taxon.getName();
1593
		String result = getFullName(name);
1594
		if (isMisappliedName(taxon)){
1595
			result = result + " " + getAuthorString(taxon);
1596
		}
1597
		
1598
		return result;
1599
	}
1600
*/
1601
	
1602
	/**
1603
	 * Returns the nomenclatural reference which is the reference
1604
	 * including the detail (microreference).
1605
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1606
	 * @return The <code>AuthorString</code> attribute.
1607
	 * @see MethodMapper
1608
	 */
1609
	@SuppressWarnings("unused")
1610
	private static String getNomRefString(TaxonNameBase<?,?> taxonName) {
1611
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1612
		return ref == null ? null : ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1613
	}
1614
	
1615
	
1616
	
1617

    
1618
		
1619
	
1620

    
1621
	
1622
	/**
1623
	 * Returns the <code>NameStatusFk</code> attribute.
1624
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1625
	 * @return The <code>NameStatusFk</code> attribute.
1626
	 * @see MethodMapper
1627
	 */
1628
	@SuppressWarnings("unused")
1629
	private static Integer getNameStatusFk(TaxonNameBase<?,?> taxonName) {
1630
		Integer result = null;
1631

    
1632
		NomenclaturalStatus state = getNameStatus(taxonName);
1633
		if (state != null) {
1634
			result = PesiTransformer.nomStatus2nomStatusFk(state.getType());
1635
		}
1636
		return result;
1637
	}
1638
	
1639
	/**
1640
	 * Returns the <code>NameStatusCache</code> attribute.
1641
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1642
	 * @return The <code>NameStatusCache</code> attribute.
1643
	 * @throws UndefinedTransformerMethodException 
1644
	 * @see MethodMapper
1645
	 */
1646
	@SuppressWarnings("unused")
1647
	private static String getNameStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1648
		String result = null;
1649
		NomenclaturalStatus status = getNameStatus(taxonName);
1650
		if (status != null) {
1651
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1652
		}
1653
		return result;
1654
	}
1655
	
1656
	
1657
	private static NomenclaturalStatus getNameStatus(TaxonNameBase<?,?> taxonName) {
1658
		
1659
		try {
1660
			if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1661
				NonViralName<?> nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1662
				Set<NomenclaturalStatus> states = nonViralName.getStatus();
1663
				if (states.size() == 1) {
1664
					NomenclaturalStatus status = states.iterator().next();
1665
					return status;
1666
				} else if (states.size() > 1) {
1667
					logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1668
				}
1669
			}
1670
		
1671
		} catch (Exception e) {
1672
			e.printStackTrace();
1673
		}
1674
		return null;
1675
	}
1676
	/**
1677
	 * Returns the <code>TaxonStatusFk</code> attribute.
1678
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1679
	 * @param state The {@link PesiExportState PesiExportState}.
1680
	 * @return The <code>TaxonStatusFk</code> attribute.
1681
	 * @see MethodMapper
1682
	 */
1683
	@SuppressWarnings("unused")
1684
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1685
		Integer result = null;
1686
		
1687
		try {
1688
			if (isMisappliedName(taxon)) {
1689
				Synonym synonym = Synonym.NewInstance(null, null);
1690
				
1691
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1692
				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1693
			} else {
1694
				result = PesiTransformer.taxonBase2statusFk(taxon);
1695
			}
1696
		
1697
		} catch (Exception e) {
1698
			e.printStackTrace();
1699
		}
1700
		return result;
1701
	}
1702
	
1703
	/**
1704
	 * Returns the <code>TaxonStatusCache</code> attribute.
1705
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1706
	 * @param state The {@link PesiExportState PesiExportState}.
1707
	 * @return The <code>TaxonStatusCache</code> attribute.
1708
	 * @see MethodMapper
1709
	 */
1710
	@SuppressWarnings("unused")
1711
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) {
1712
		String result = null;
1713
		
1714
		try {
1715
			if (isMisappliedName(taxon)) {
1716
				Synonym synonym = Synonym.NewInstance(null, null);
1717
				
1718
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1719
				result = PesiTransformer.taxonBase2statusCache(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1720
			} else {
1721
				result = PesiTransformer.taxonBase2statusCache(taxon);
1722
			}
1723
		
1724
		} catch (Exception e) {
1725
			e.printStackTrace();
1726
		}
1727
		return result;
1728
	}
1729
	
1730
	/**
1731
	 * Returns the <code>TypeNameFk</code> attribute.
1732
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1733
	 * @param state The {@link PesiExportState PesiExportState}.
1734
	 * @return The <code>TypeNameFk</code> attribute.
1735
	 * @see MethodMapper
1736
	 */
1737
	private static Integer getTypeNameFk(TaxonNameBase<?,?> taxonNameBase, PesiExportState state) {
1738
		Integer result = null;
1739
		if (taxonNameBase != null) {
1740
			Set<NameTypeDesignation> nameTypeDesignations = taxonNameBase.getNameTypeDesignations();
1741
			if (nameTypeDesignations.size() == 1) {
1742
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1743
				if (nameTypeDesignation != null) {
1744
					TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1745
					if (typeName != null) {
1746
						result = state.getDbId(typeName);
1747
					}
1748
				}
1749
			} else if (nameTypeDesignations.size() > 1) {
1750
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonNameBase.getUuid() + " (" + taxonNameBase.getTitleCache() + ")");
1751
			}
1752
		}
1753
		return result;
1754
	}
1755
	
1756
	/**
1757
	 * Returns the <code>TypeFullnameCache</code> attribute.
1758
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1759
	 * @return The <code>TypeFullnameCache</code> attribute.
1760
	 * @see MethodMapper
1761
	 */
1762
	@SuppressWarnings("unused")
1763
	private static String getTypeFullnameCache(TaxonNameBase<?,?> taxonName) {
1764
		String result = null;
1765
		
1766
		try {
1767
		if (taxonName != null) {
1768
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1769
			if (nameTypeDesignations.size() == 1) {
1770
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1771
				if (nameTypeDesignation != null) {
1772
					TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1773
					if (typeName != null) {
1774
						result = typeName.getTitleCache();
1775
					}
1776
				}
1777
			} else if (nameTypeDesignations.size() > 1) {
1778
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1779
			}
1780
		}
1781
		
1782
		} catch (Exception e) {
1783
			e.printStackTrace();
1784
		}
1785
		return result;
1786
	}
1787

    
1788
	
1789
	/**
1790
	 * Returns the <code>QualityStatusFk</code> attribute.
1791
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1792
	 * @return The <code>QualityStatusFk</code> attribute.
1793
	 * @see MethodMapper
1794
	 */
1795
	private static Integer getQualityStatusFk(TaxonNameBase taxonName) {
1796
		BitSet sources = getSources(taxonName);
1797
		return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1798
	}
1799

    
1800
	
1801
	/**
1802
	 * Returns the <code>QualityStatusCache</code> attribute.
1803
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1804
	 * @return The <code>QualityStatusCache</code> attribute.
1805
	 * @throws UndefinedTransformerMethodException 
1806
	 * @see MethodMapper
1807
	 */
1808
	@SuppressWarnings("unused")
1809
	private static String getQualityStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1810
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1811
	}
1812

    
1813
	
1814
	/**
1815
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1816
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1817
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1818
	 * @see MethodMapper
1819
	 */
1820
	@SuppressWarnings("unused")
1821
	private static Integer getTypeDesignationStatusFk(TaxonNameBase<?,?> taxonName) {
1822
		Integer result = null;
1823
		
1824
		try {
1825
		if (taxonName != null) {
1826
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1827
			if (typeDesignations.size() == 1) {
1828
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1829
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1830
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1831
			} else if (typeDesignations.size() > 1) {
1832
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1833
			}
1834
		}
1835
		
1836
		} catch (Exception e) {
1837
			e.printStackTrace();
1838
		}
1839
		return result;
1840
	}
1841

    
1842
	/**
1843
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1844
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1845
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1846
	 * @see MethodMapper
1847
	 */
1848
	@SuppressWarnings("unused")
1849
	private static String getTypeDesignationStatusCache(TaxonNameBase<?,?> taxonName) {
1850
		String result = null;
1851
		
1852
		try {
1853
		if (taxonName != null) {
1854
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1855
			if (typeDesignations.size() == 1) {
1856
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1857
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1858
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
1859
			} else if (typeDesignations.size() > 1) {
1860
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1861
			}
1862
		}
1863
		
1864
		} catch (Exception e) {
1865
			e.printStackTrace();
1866
		}
1867
		return result;
1868
	}
1869
	
1870
	/**
1871
	 * Returns the <code>FossilStatusFk</code> attribute.
1872
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1873
	 * @return The <code>FossilStatusFk</code> attribute.
1874
	 * @see MethodMapper
1875
	 */
1876
	@SuppressWarnings("unused")
1877
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
1878
		Integer result = null;
1879
		
1880
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
1881
		if (fossilStatuus.size() == 0){
1882
			return null;
1883
		}else if (fossilStatuus.size() > 1){
1884
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
1885
		}
1886
		String fossilStatus = fossilStatuus.iterator().next();
1887
		
1888
		int statusFk = state.getTransformer().FossilStatusCache2FossilStatusFk(fossilStatus);
1889
		return statusFk;
1890
	}
1891
	
1892
	/**
1893
	 * Returns the <code>FossilStatusCache</code> attribute.
1894
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1895
	 * @return The <code>FossilStatusCache</code> attribute.
1896
	 * @see MethodMapper
1897
	 */
1898
	@SuppressWarnings("unused")
1899
	private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
1900
		String result = null;
1901
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
1902
		if (fossilStatuus.size() == 0){
1903
			return null;
1904
		}
1905
		for (String strFossilStatus : fossilStatuus){
1906
			result = CdmUtils.concat(";", result, strFossilStatus);
1907
		}
1908
		return result;
1909
	}
1910
	
1911
	/**
1912
	 * Returns the <code>IdInSource</code> attribute.
1913
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1914
	 * @return The <code>IdInSource</code> attribute.
1915
	 * @see MethodMapper
1916
	 */
1917
	@SuppressWarnings("unused")
1918
	private static String getIdInSource(IdentifiableEntity taxonName) {
1919
		String result = null;
1920
		
1921
		try {
1922
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
1923
			if (sources.size() > 1){
1924
				logger.warn("There is > 1 Pesi source. This is not yet handled.");
1925
			}
1926
			if (sources.size() == 0){
1927
				logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1928
			}
1929
			for (IdentifiableSource source : sources) {
1930
				Reference<?> ref = source.getCitation();
1931
				UUID refUuid = ref.getUuid();
1932
				String idInSource = source.getIdInSource();
1933
				if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed)){
1934
					result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
1935
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
1936
					result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
1937
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
1938
					result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
1939
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
1940
					result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
1941
				}else{
1942
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
1943
				}
1944
				
1945
				String sourceIdNameSpace = source.getIdNamespace();
1946
				if (sourceIdNameSpace != null) {
1947
					if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
1948
						result =  idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
1949
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
1950
						result =  idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
1951
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
1952
						result =  idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
1953
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
1954
						result =  idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
1955
					} 
1956
				}
1957
				if (result == null) {
1958
					logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
1959
				}
1960
			}
1961
		} catch (Exception e) {
1962
			e.printStackTrace();
1963
			logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
1964
		}
1965

    
1966
		if (result == null) {
1967
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1968
		}
1969
		return result;
1970
	}
1971
	
1972
	/**
1973
	 * Returns the idInSource for a given TaxonName only.
1974
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1975
	 * @return The idInSource.
1976
	 */
1977
	private static String getIdInSourceOnly(IdentifiableEntity identEntity) {
1978
		String result = null;
1979
		
1980
		// Get the sources first
1981
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
1982

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

    
2000
		}
2001
		
2002
		return result;
2003
	}
2004
	
2005
	/**
2006
	 * Returns the Sources for a given TaxonName only.
2007
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2008
	 * @return The Sources.
2009
	 */
2010
	private static Set<IdentifiableSource> getPesiSources(IdentifiableEntity identEntity) {
2011
		Set<IdentifiableSource> sources = new java.util.HashSet<IdentifiableSource>();
2012

    
2013
		//Taxon Names
2014
		if (identEntity.isInstanceOf(TaxonNameBase.class)){
2015
			// Sources from TaxonName
2016
			TaxonNameBase taxonName = CdmBase.deproxy(identEntity, TaxonNameBase.class);
2017
			Set<IdentifiableSource> testSources = identEntity.getSources();
2018
			sources = filterPesiSources(identEntity.getSources());
2019
			
2020
			if (sources.size() == 0 && testSources.size()>0){
2021
				IdentifiableSource source = testSources.iterator().next();
2022
				logger.warn("There are sources, but they are no pesi sources!!!" + source.getIdInSource() + " - " + source.getIdNamespace() + " - "+source.getCitation().generateTitle());
2023
			}
2024
			if (sources.size() > 1) {
2025
				logger.warn("This TaxonName has more than one Source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() + ")");
2026
			}
2027
			
2028
			// name has no PESI source, take sources from TaxonBase
2029
			if (sources == null || sources.isEmpty()) {
2030
				Set<TaxonBase> taxa = taxonName.getTaxonBases();
2031
				for (TaxonBase taxonBase: taxa){
2032
					sources.addAll(filterPesiSources(taxonBase.getSources()));
2033
				}
2034
			}
2035

    
2036
		//for TaxonBases
2037
		}else if (identEntity.isInstanceOf(TaxonBase.class)){
2038
			sources = filterPesiSources(identEntity.getSources());	
2039
		}
2040

    
2041
		/*TODO: deleted only for testing the inferred synonyms 
2042
		if (sources == null || sources.isEmpty()) {
2043
			logger.warn("This TaxonName has no PESI Sources: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2044
		}else if (sources.size() > 1){
2045
			logger.warn("This Taxon(Name) has more than 1 PESI source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2046
		}
2047
		*/
2048
		return sources;
2049
	}
2050
	
2051
	// return all sources with a PESI reference	
2052
	private static Set<IdentifiableSource> filterPesiSources(Set<? extends IdentifiableSource> sources) {
2053
		Set<IdentifiableSource> result = new HashSet<IdentifiableSource>();
2054
		for (IdentifiableSource source : sources){
2055
			Reference ref = source.getCitation();
2056
			UUID refUuid = ref.getUuid();
2057
			if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed) || 
2058
				refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)||
2059
				refUuid.equals(PesiTransformer.uuidSourceRefErms)||
2060
				refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum) ||
2061
				refUuid.equals(PesiTransformer.uuidSourceRefAuct)){
2062
				result.add(source);
2063
			}
2064
		}
2065
		return result;
2066
	}
2067

    
2068
	/**
2069
	 * Returns the <code>GUID</code> attribute.
2070
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2071
	 * @return The <code>GUID</code> attribute.
2072
	 * @see MethodMapper
2073
	 */
2074
	private static String getGUID(TaxonBase<?> taxon) {
2075
		if (taxon.getLsid() != null ){
2076
			return taxon.getLsid().getLsid();
2077
		}else if (taxon.hasMarker(PesiTransformer.uuidMarkerGuidIsMissing, true)){
2078
			return null;
2079
		}else{
2080
			return taxon.getUuid().toString();
2081
		}
2082
	}
2083
	
2084
	
2085
	
2086
	
2087
	/**
2088
	 * Returns the <code>DerivedFromGuid</code> attribute.
2089
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2090
	 * @return The <code>DerivedFromGuid</code> attribute.
2091
	 * @see MethodMapper
2092
	 */
2093
	@SuppressWarnings("unused")
2094
	private static String getDerivedFromGuid(TaxonBase<?> taxon) {
2095
		String result = null;
2096
		try {
2097
		// The same as GUID for now
2098
		result = getGUID(taxon);
2099
		} catch (Exception e) {
2100
			e.printStackTrace();
2101
		}
2102
		return result;
2103
	}
2104
	
2105
	/**
2106
	 * Returns the <code>CacheCitation</code> attribute.
2107
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2108
	 * @return The CacheCitation.
2109
	 * @see MethodMapper
2110
	 */
2111
	@SuppressWarnings("unused")
2112
	private static String getCacheCitation(TaxonBase taxon) {
2113
		// !!! See also doPhaseUpdates
2114
		
2115
		TaxonNameBase<?,?> taxonName = taxon.getName();
2116
		String result = "";
2117
		//TODO implement anew for taxa
2118
		try {
2119
			BitSet sources = getSources(taxonName);
2120
			if (sources.isEmpty()) {
2121
//				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2122
			} else if (sources.get(PesiTransformer.SOURCE_ERMS)) {
2123
				// TODO: 19.08.2010: An import of CacheCitation does not exist in the ERMS import yet or it will be imported in a different way...
2124
				// 		 So the following code is some kind of harmless assumption.
2125
				Set<Extension> extensions = taxonName.getExtensions();
2126
				for (Extension extension : extensions) {
2127
					if (extension.getType().equals(cacheCitationExtensionType)) {
2128
						result = extension.getValue();
2129
					}
2130
				}
2131
			} else {
2132
				String expertName = getExpertName(taxon);
2133
				String webShowName = getWebShowName(taxonName);
2134
				
2135
				// idInSource only
2136
				String idInSource = getIdInSourceOnly(taxonName);
2137
				
2138
				// build the cacheCitation
2139
				if (expertName != null) {
2140
					result += expertName + ". ";
2141
				} else {
2142
					if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
2143
				}
2144
				if (webShowName != null) {
2145
					result += webShowName + ". ";
2146
				} else {
2147
					logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2148
				}
2149
				
2150
				if (getOriginalDB(taxonName).equals("FaEu")) {
2151
					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
2152
				} else if (getOriginalDB(taxonName).equals("EM")) {
2153
					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
2154
				}
2155
				
2156
				if (idInSource != null) {
2157
					result += idInSource;
2158
				} else {
2159
					logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2160
				}
2161
			}
2162
		} catch (Exception e) {
2163
			e.printStackTrace();
2164
		}
2165
		
2166
		if (StringUtils.isBlank(result)) {
2167
			return null;
2168
		} else {
2169
			return result;
2170
		}
2171
	}
2172
	
2173
	/**
2174
	 * Returns the <code>OriginalDB</code> attribute.
2175
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2176
	 * @return The <code>OriginalDB</code> attribute.
2177
	 * @see MethodMapper
2178
	 */
2179
	private static String getOriginalDB(IdentifiableEntity identEntity) {
2180
		// Sources from TaxonName
2181
		BitSet sources  = getSources(identEntity);
2182
		return PesiTransformer.getOriginalDbBySources(sources);
2183
	}
2184
	
2185
	/**
2186
	 * Returns the <code>LastAction</code> attribute.
2187
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2188
	 * @return The <code>LastAction</code> attribute.
2189
	 * @see MethodMapper
2190
	 */
2191
	@SuppressWarnings("unused")
2192
	private static String getLastAction(IdentifiableEntity<?> identEntity) {
2193
		String result = null;
2194
		try {
2195
		Set<Extension> extensions = identEntity.getExtensions();
2196
		for (Extension extension : extensions) {
2197
			if (extension.getType().equals(lastActionExtensionType)) {
2198
				result = extension.getValue();
2199
			}
2200
		}
2201
		} catch (Exception e) {
2202
			e.printStackTrace();
2203
		}
2204
		return result;
2205
	}
2206
	
2207
	/**
2208
	 * Returns the <code>LastActionDate</code> attribute.
2209
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2210
	 * @return The <code>LastActionDate</code> attribute.
2211
	 * @see MethodMapper
2212
	 */
2213
	@SuppressWarnings({ "unused" })
2214
	private static DateTime getLastActionDate(IdentifiableEntity identEntity) {
2215
		DateTime result = null;
2216
		try {
2217
			Set<Extension> extensions = identEntity.getExtensions();
2218
			for (Extension extension : extensions) {
2219
				if (extension.getType().equals(lastActionDateExtensionType)) {
2220
					String dateTime = extension.getValue();
2221
					if (dateTime != null) {
2222
						DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
2223
						result = formatter.parseDateTime(dateTime);
2224
					}
2225
				}
2226
			}
2227
		} catch (Exception e) {
2228
			e.printStackTrace();
2229
		}
2230
		return result;
2231
	}
2232
	
2233
	/**
2234
	 * Returns the <code>ExpertName</code> attribute.
2235
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2236
	 * @return The <code>ExpertName</code> attribute.
2237
	 * @see MethodMapper
2238
	 */
2239
	@SuppressWarnings("unused")
2240
	private static String getExpertName(TaxonBase<?> taxonName) {
2241
		String result = null;
2242
		try {
2243
		Set<Extension> extensions = taxonName.getExtensions();
2244
		for (Extension extension : extensions) {
2245
			if (extension.getType().equals(expertNameExtensionType)) {
2246
				result = extension.getValue();
2247
			}
2248
		}
2249
		} catch (Exception e) {
2250
			e.printStackTrace();
2251
		}
2252
		return result;
2253
	}
2254
	
2255
	/**
2256
	 * Returns the <code>ExpertFk</code> attribute.
2257
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2258
	 * @param state The {@link PesiExportState PesiExportState}.
2259
	 * @return The <code>ExpertFk</code> attribute.
2260
	 * @see MethodMapper
2261
	 */
2262
	private static Integer getExpertFk(Reference<?> reference, PesiExportState state) {
2263
		Integer result = state.getDbId(reference);
2264
		return result;
2265
	}
2266
	
2267
	/**
2268
	 * Returns the <code>SpeciesExpertName</code> attribute.
2269
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2270
	 * @return The <code>SpeciesExpertName</code> attribute.
2271
	 * @see MethodMapper
2272
	 */
2273
	@SuppressWarnings("unused")
2274
	private static String getSpeciesExpertName(TaxonBase<?> taxonName) {
2275
		String result = null;
2276
		try {
2277
		Set<Extension> extensions = taxonName.getExtensions();
2278
		for (Extension extension : extensions) {
2279
			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2280
				result = extension.getValue();
2281
			}
2282
		}
2283
		} catch (Exception e) {
2284
			e.printStackTrace();
2285
		}
2286
		return result;
2287
	}
2288
	
2289
	/**
2290
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2291
	 * @param reference The {@link Reference Reference}.
2292
	 * @param state The {@link PesiExportState PesiExportState}.
2293
	 * @return The <code>SpeciesExpertFk</code> attribute.
2294
	 * @see MethodMapper
2295
	 */
2296
	private static Integer getSpeciesExpertFk(Reference<?> reference, PesiExportState state) {
2297
		Integer result = state.getDbId(reference);
2298
		return result;
2299
	}
2300
	
2301
	
2302
	/**
2303
	 * Returns the source (E+M, Fauna Europaea, Index Fungorum, ERMS) of a given
2304
	 * Identifiable Entity as a BitSet
2305
	 * @param identEntity
2306
	 * @return
2307
	 */
2308
	private static BitSet getSources(IdentifiableEntity<?> identEntity){
2309
		BitSet bitSet = new BitSet();
2310
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2311
		for (IdentifiableSource source : sources) {
2312
			Reference<?> ref = source.getCitation();
2313
			UUID refUuid = ref.getUuid();
2314
			if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed)){
2315
				bitSet.set(PesiTransformer.SOURCE_EM);
2316
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2317
				bitSet.set(PesiTransformer.SOURCE_FE);
2318
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2319
				bitSet.set(PesiTransformer.SOURCE_ERMS);
2320
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){
2321
				bitSet.set(PesiTransformer.SOURCE_IF);
2322
			}else{
2323
				if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2324
			}
2325
		}
2326
		return bitSet;
2327
		
2328
	}
2329
	
2330
	protected static NonViralNameDefaultCacheStrategy getCacheStrategy(TaxonNameBase<?, ?> taxonName) {
2331
		taxonName = CdmBase.deproxy(taxonName, TaxonNameBase.class);
2332
		NonViralNameDefaultCacheStrategy<?> cacheStrategy;
2333
		if (taxonName.isInstanceOf(ZoologicalName.class)){
2334
			cacheStrategy = zooNameStrategy;
2335
		}else if (taxonName.isInstanceOf(BotanicalName.class)) {
2336
			cacheStrategy = botanicalNameStrategy;
2337
		}else if (taxonName.getClass().equals(NonViralName.class)) {
2338
			cacheStrategy = nonViralNameStrategy;
2339
		}else if (taxonName.getClass().equals(BacterialName.class)) {
2340
			cacheStrategy = bacterialNameStrategy;
2341
		}else{
2342
			logger.error("Unhandled taxon name type. Can't define strategy class");
2343
			cacheStrategy = botanicalNameStrategy;
2344
		}
2345
		return cacheStrategy;
2346
	}
2347
	
2348
	/**
2349
	 * Returns the <code>TaxonFk1</code> attribute. It corresponds to a CDM <code>TaxonRelationship</code>.
2350
	 * @param relationship The {@link RelationshipBase Relationship}.
2351
	 * @param state The {@link PesiExportState PesiExportState}.
2352
	 * @return The <code>TaxonFk1</code> attribute.
2353
	 * @see MethodMapper
2354
	 */
2355
	private static Integer getTaxonFk1(RelationshipBase<?, ?, ?> relationship, PesiExportState state) {
2356
		
2357
		return getObjectFk(relationship, state, true);
2358
	}
2359
	
2360
	/**
2361
	 * Returns the <code>TaxonFk2</code> attribute. It corresponds to a CDM <code>SynonymRelationship</code>.
2362
	 * @param relationship The {@link RelationshipBase Relationship}.
2363
	 * @param state The {@link PesiExportState PesiExportState}.
2364
	 * @return The <code>TaxonFk2</code> attribute.
2365
	 * @see MethodMapper
2366
	 */
2367
	private static Integer getTaxonFk2(RelationshipBase<?, ?, ?> relationship, PesiExportState state) {
2368
		return getObjectFk(relationship, state, false);
2369
	}
2370
	
2371
	/**
2372
	 * Returns the database key of an object in the given relationship.
2373
	 * @param relationship {@link RelationshipBase RelationshipBase}.
2374
	 * @param state {@link PesiExportState PesiExportState}.
2375
	 * @param isFrom A boolean value indicating whether the database key of the parent or child in this relationship is searched. <code>true</code> means the child is searched. <code>false</code> means the parent is searched.
2376
	 * @return The database key of an object in the given relationship.
2377
	 */
2378
	private static Integer getObjectFk(RelationshipBase<?, ?, ?> relationship, PesiExportState state, boolean isFrom) {
2379
		TaxonBase<?> taxonBase = null;
2380
		if (relationship.isInstanceOf(TaxonRelationship.class)) {
2381
			TaxonRelationship tr = (TaxonRelationship)relationship;
2382
			taxonBase = (isFrom) ? tr.getFromTaxon():  tr.getToTaxon();
2383
		} else if (relationship.isInstanceOf(SynonymRelationship.class)) {
2384
			SynonymRelationship sr = (SynonymRelationship)relationship;
2385
			taxonBase = (isFrom) ? sr.getSynonym() : sr.getAcceptedTaxon();
2386
		} else if (relationship.isInstanceOf(NameRelationship.class) ||  relationship.isInstanceOf(HybridRelationship.class)) {
2387
			if (isFrom){
2388
				return state.getDbId(state.getCurrentFromObject());
2389
			}else{
2390
				return state.getDbId(state.getCurrentToObject());
2391
			}
2392
		}
2393
		if (taxonBase != null) {
2394
			if (! isPesiTaxon(taxonBase)){
2395
				logger.warn("Related taxonBase is not a PESI taxon. Taxon: " + taxonBase.getId() + "/" + taxonBase.getUuid() + "; TaxonRel: " +  relationship.getId() + "(" + relationship.getType().getTitleCache() + ")");
2396
				return null;
2397
			}else{
2398
				return state.getDbId(taxonBase);	
2399
			}
2400
			
2401
		}
2402
		logger.warn("No taxon found in state for relationship: " + relationship.toString());
2403
		return null;
2404
	}
2405
	
2406
	/**
2407
	 * Returns the <code>RelQualifierCache</code> attribute.
2408
	 * @param relationship The {@link RelationshipBase Relationship}.
2409
	 * @return The <code>RelQualifierCache</code> attribute.
2410
	 * @see MethodMapper
2411
	 */
2412
	@SuppressWarnings("unused")
2413
	private static String getRelQualifierCache(RelationshipBase<?, ?, ?> relationship, PesiExportState state) {
2414
		String result = null;
2415
		NomenclaturalCode code = null;
2416
		if (relationship.isInstanceOf(TaxonRelationship.class)){
2417
			code = CdmBase.deproxy(relationship, TaxonRelationship.class).getToTaxon().getName().getNomenclaturalCode();
2418
		}else if (relationship.isInstanceOf(SynonymRelationship.class)){
2419
			code = CdmBase.deproxy(relationship, SynonymRelationship.class).getAcceptedTaxon().getName().getNomenclaturalCode();
2420
		}else if (relationship.isInstanceOf(NameRelationship.class)){
2421
			code = CdmBase.deproxy(relationship,  NameRelationship.class).getFromName().getNomenclaturalCode();
2422
		}else if (relationship.isInstanceOf(HybridRelationship.class)){
2423
			code = CdmBase.deproxy(relationship,  HybridRelationship.class).getParentName().getNomenclaturalCode();
2424
		}
2425
		if (code != null) {
2426
			result = state.getConfig().getTransformer().getCacheByRelationshipType(relationship, code);
2427
		} else {
2428
			logger.error("NomenclaturalCode is NULL while creating the following relationship: " + relationship.getUuid());
2429
		}
2430
		return result;
2431
	}
2432
	
2433
	/**
2434
	 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2435
	 * @param relationship The {@link RelationshipBase Relationship}.
2436
	 * @return The <code>RelTaxonQualifierFk</code> attribute.
2437
	 * @see MethodMapper
2438
	 */
2439
	@SuppressWarnings("unused")
2440
	private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2441
		return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2442
	}
2443
	/**
2444
	 * Returns the <code>Notes</code> attribute.
2445
	 * @param relationship The {@link RelationshipBase Relationship}.
2446
	 * @return The <code>Notes</code> attribute.
2447
	 * @see MethodMapper
2448
	 */
2449
	@SuppressWarnings("unused")
2450
	private static String getNotes(RelationshipBase<?, ?, ?> relationship) {
2451
		// TODO
2452
		return null;
2453
	}
2454

    
2455
//	/**
2456
//	 * Returns the <code>SourceFk</code> attribute.
2457
//	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2458
//	 * @param state The {@link PesiExportState PesiExportState}.
2459
//	 * @return The <code>SourceFk</code> attribute.
2460
//	 */
2461
//	@SuppressWarnings("unused")
2462
//	private static Integer getSourceFk(TaxonNameBase<?,?> taxonName, PesiExportState state) {
2463
//		Integer result = null;
2464
//		
2465
//		try {
2466
//			TaxonBase<?> taxonBase = getSourceTaxonBase(taxonName);
2467
//	
2468
//			if (taxonBase != null) {
2469
//				result = state.getDbId(taxonBase.getSec());
2470
//			}
2471
//		} catch (Exception e) {
2472
//			e.printStackTrace();
2473
//		}
2474
//		return result;
2475
//	}
2476

    
2477

    
2478
//	/**
2479
//	 * Determines the TaxonBase of a TaxonName.
2480
//	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2481
//	 * @return The TaxonBase.
2482
//	 */
2483
//	private static TaxonBase<?> getSourceTaxonBase(TaxonNameBase<?,?> taxonName) {
2484
//		TaxonBase<?> taxonBase = null;
2485
//		Set<Taxon> taxa = taxonName.getTaxa();
2486
//		if (taxa.size() == 1) {
2487
//			taxonBase =taxa.iterator().next();
2488
//		} else if (taxa.size() > 1) {
2489
//			logger.warn("This TaxonName has " + taxa.size() + " Taxa: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2490
//		}
2491
//		
2492
//		Set<Synonym> synonyms  = taxonName.getSynonyms();
2493
//		if (synonyms.size() == 1) {
2494
//			taxonBase = synonyms.iterator().next();
2495
//		} else if (synonyms.size() > 1) {
2496
//			logger.warn("This TaxonName has " + synonyms.size() + " Synonyms: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2497
//		}
2498
//		return taxonBase;
2499
//	}
2500
	
2501
	/**
2502
	 * Returns the CDM to PESI specific export mappings.
2503
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2504
	 */
2505
	private PesiExportMapping getMapping() {
2506
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2507
		
2508
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2509
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2510
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2511
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2512
		
2513
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2514
		
2515
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2516
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2517
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misallied Names are handled differently
2518
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2519
		
2520
		// DisplayName
2521
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2522

    
2523
		// FossilStatus (Fk, Cache)
2524
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2525
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2526
		
2527
		//handled by name mapping
2528
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2529
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2530
		
2531
		//experts
2532
		ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
2533
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2534
		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
2535
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2536
		
2537
//		mapping.addMapper(MethodMapper.NewInstance("ParentTaxonFk", this, TaxonBase.class, PesiExportState.class));  //by AM, doesn't work, FK exception
2538
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonNameBase.class, "Name"));
2539
		
2540
		addNameMappers(mapping);
2541

    
2542
		return mapping;
2543
	}
2544
	
2545
	/**
2546
	 * Returns the CDM to PESI specific export mappings.
2547
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2548
	 */
2549
	private PesiExportMapping getPureNameMapping() {
2550
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2551
		
2552
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2553

    
2554
		//		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2555

    
2556
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonNameBase.class));
2557
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonNameBase.class));
2558
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonNameBase.class));
2559
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
2560
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , PesiTransformer.T_STATUS_STR_UNACCEPTED));
2561
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));  
2562
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonNameBase.class));
2563
		
2564
		// DisplayName
2565
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonNameBase.class));
2566
		
2567
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2568
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2569
		
2570
		addNameMappers(mapping);
2571
		//TODO add author mapper, TypeNameFk
2572

    
2573
		return mapping;
2574
	}
2575

    
2576
	private void addNameMappers(PesiExportMapping mapping) {
2577
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2578
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2579
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2580
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2581
		
2582
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2583
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonNameBase.class));
2584
		
2585
//		mapping.addMapper(DbStringMapper.NewInstance("TitleCache", "FullName"));    //does not work as we need other cache strategy
2586
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonNameBase.class));
2587
		
2588
		
2589
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonNameBase.class));
2590
		
2591
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonNameBase.class));
2592
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2593
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonNameBase.class));
2594
		//TODO TypeNameFk
2595
		
2596
		//quality status
2597
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonNameBase.class));
2598
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2599
		
2600
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2601
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2602

    
2603
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2604

    
2605
	}
2606
	
2607
	private PesiExportMapping getSynRelMapping() {
2608
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2609
		
2610
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getTaxonFk1", RelationshipBase.class, PesiExportState.class));
2611
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk2", this.getClass(), "getTaxonFk2", RelationshipBase.class, PesiExportState.class));
2612
		mapping.addMapper(MethodMapper.NewInstance("RelTaxonQualifierFk", this,  RelationshipBase.class));
2613
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this, RelationshipBase.class, PesiExportState.class));
2614
		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2615

    
2616
		return mapping;
2617
	}
2618

    
2619

    
2620
}
(16-16/17)