Project

General

Profile

Download (88 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.List;
22
import java.util.Set;
23
import java.util.UUID;
24

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

    
33
import eu.etaxonomy.cdm.api.service.TaxonServiceImpl;
34
import eu.etaxonomy.cdm.app.pesi.ErmsActivator;
35
import eu.etaxonomy.cdm.app.pesi.EuroMedActivator;
36
import eu.etaxonomy.cdm.app.pesi.FaunaEuropaeaActivator;
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.DbObjectMapper;
43
import eu.etaxonomy.cdm.io.common.mapping.out.DbStringMapper;
44
import eu.etaxonomy.cdm.io.common.mapping.out.IdMapper;
45
import eu.etaxonomy.cdm.io.common.mapping.out.MethodMapper;
46
import eu.etaxonomy.cdm.io.common.mapping.out.ObjectChangeMapper;
47
import eu.etaxonomy.cdm.io.pesi.erms.ErmsTransformer;
48
import eu.etaxonomy.cdm.model.common.Annotation;
49
import eu.etaxonomy.cdm.model.common.AnnotationType;
50
import eu.etaxonomy.cdm.model.common.CdmBase;
51
import eu.etaxonomy.cdm.model.common.Extension;
52
import eu.etaxonomy.cdm.model.common.ExtensionType;
53
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
54
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
55
import eu.etaxonomy.cdm.model.common.Language;
56
import eu.etaxonomy.cdm.model.name.BotanicalName;
57
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
58
import eu.etaxonomy.cdm.model.name.NameTypeDesignationStatus;
59
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
60
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
61
import eu.etaxonomy.cdm.model.name.NonViralName;
62
import eu.etaxonomy.cdm.model.name.Rank;
63
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
64
import eu.etaxonomy.cdm.model.name.ZoologicalName;
65
import eu.etaxonomy.cdm.model.reference.Reference;
66
import eu.etaxonomy.cdm.model.taxon.Classification;
67
import eu.etaxonomy.cdm.model.taxon.Synonym;
68
import eu.etaxonomy.cdm.model.taxon.Taxon;
69
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
70
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
71
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
72
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
73
import eu.etaxonomy.cdm.persistence.query.MatchMode;
74
import eu.etaxonomy.cdm.strategy.cache.HTMLTagRules;
75
import eu.etaxonomy.cdm.strategy.cache.TagEnum;
76
import eu.etaxonomy.cdm.strategy.cache.TaggedText;
77
import eu.etaxonomy.cdm.strategy.cache.name.BotanicNameDefaultCacheStrategy;
78
import eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy;
79
import eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy;
80
import eu.etaxonomy.cdm.strategy.cache.name.ZooNameDefaultCacheStrategy;
81
import eu.etaxonomy.cdm.strategy.cache.name.ZooNameNoMarkerCacheStrategy;
82

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

    
100
	private static int modCount = 1000;
101
	private static final String dbTableName = "Taxon";
102
	private static final String pluralString = "Taxa";
103
	private static final String parentPluralString = "Taxa";
104
	private PreparedStatement parentTaxonFk_TreeIndex_KingdomFkStmt;
105
	private PreparedStatement rankTypeExpertsUpdateStmt;
106
	private PreparedStatement rankUpdateStmt;
107
	private NomenclaturalCode nomenclaturalCode;
108
	private Integer kingdomFk;
109
	private HashMap<Rank, Rank> rank2endRankMap = new HashMap<Rank, Rank>();
110
	private List<Rank> rankList = new ArrayList<Rank>();
111
	private static final UUID uuidTreeIndex = UUID.fromString("28f4e205-1d02-4d3a-8288-775ea8413009");
112
	private AnnotationType treeIndexAnnotationType;
113
	private static ExtensionType lastActionExtensionType;
114
	private static ExtensionType lastActionDateExtensionType;
115
	private static ExtensionType expertNameExtensionType;
116
	private static ExtensionType speciesExpertNameExtensionType;
117
	private static ExtensionType cacheCitationExtensionType;
118
	private static ExtensionType expertUserIdExtensionType;
119
	private static ExtensionType speciesExpertUserIdExtensionType;
120
	private static NonViralNameDefaultCacheStrategy zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
121
	private static NonViralNameDefaultCacheStrategy botanicalNameStrategy = BotanicNameDefaultCacheStrategy.NewInstance();
122
	
123
	
124
	/**
125
	 * @return the treeIndexAnnotationType
126
	 */
127
	protected AnnotationType getTreeIndexAnnotationType() {
128
		return treeIndexAnnotationType;
129
	}
130

    
131
	/**
132
	 * @param treeIndexAnnotationType the treeIndexAnnotationType to set
133
	 */
134
	protected void setTreeIndexAnnotationType(AnnotationType treeIndexAnnotationType) {
135
		this.treeIndexAnnotationType = treeIndexAnnotationType;
136
	}
137

    
138
	enum NamePosition {
139
		beginning,
140
		end,
141
		between,
142
		alone,
143
		nowhere
144
	}
145

    
146
	public PesiTaxonExport() {
147
		super();
148
	}
149

    
150
	/* (non-Javadoc)
151
	 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getStandardMethodParameter()
152
	 */
153
	@Override
154
	public Class<? extends CdmBase> getStandardMethodParameter() {
155
		return standardMethodParameter;
156
	}
157

    
158
	/* (non-Javadoc)
159
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
160
	 */
161
	@Override
162
	protected boolean doCheck(PesiExportState state) {
163
		boolean result = true;
164
		return result;
165
	}
166
	
167
	
168
	/* (non-Javadoc)
169
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
170
	 */
171
	@Override
172
	protected void doInvoke(PesiExportState state) {
173
		try {
174
			logger.info("*** Started Making " + pluralString + " ...");
175

    
176
			initPreparedStatements(state);
177
			
178
			// Stores whether this invoke was successful or not.
179
			boolean success = true;
180
	
181
			// PESI: Clear the database table Taxon.
182
			doDelete(state);
183
			
184
			// Get specific mappings: (CDM) Taxon -> (PESI) Taxon
185
			PesiExportMapping mapping = getMapping();
186
	
187
			// Initialize the db mapper
188
			mapping.initialize(state);
189

    
190
			// Find extensionTypes
191
			lastActionExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionUuid);
192
			lastActionDateExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionDateUuid);
193
			expertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
194
			speciesExpertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
195
			cacheCitationExtensionType = (ExtensionType)getTermService().find(PesiTransformer.cacheCitationUuid);
196
			expertUserIdExtensionType = (ExtensionType)getTermService().find(PesiTransformer.expertUserIdUuid);
197
			speciesExpertUserIdExtensionType = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertUserIdUuid);
198

    
199

    
200
			//"PHASE 1b: Handle names without taxa ...
201
			success &= doNames(state);
202

    
203
			//Export Taxa..
204
			success &= doPhase01(state, mapping);
205

    
206
			
207
			
208
			// 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
209
			success &= doPhase02(state);
210
			
211
			//PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...
212
			success &= doPhase03(state);
213
			
214
			
215
			//"PHASE 4: Creating Inferred Synonyms...
216
			success &= doPhase04(state, mapping);
217
			
218
			//updates to TaxonStatus
219
			success &= doPhaseUpdates(state);
220

    
221
			
222
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
223

    
224
			if (!success){
225
				state.setUnsuccessfull();
226
			}
227
			return;
228
		} catch (SQLException e) {
229
			e.printStackTrace();
230
			logger.error(e.getMessage());
231
			state.setUnsuccessfull();
232
			return;
233
		}
234
	}
235

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

    
289
	private void initPreparedStatements(PesiExportState state) throws SQLException {
290
		initTreeIndexStatement(state);
291
		initRankExpertsUpdateStmt(state);
292
		initRankUpdateStatement(state);
293
	}
294

    
295
	// Prepare TreeIndex-And-KingdomFk-Statement
296
	private void initTreeIndexStatement(PesiExportState state) throws SQLException {
297
		Connection connection = state.getConfig().getDestination().getConnection();
298
		String parentTaxonFk_TreeIndex_KingdomFkSql = "UPDATE Taxon SET ParentTaxonFk = ?, TreeIndex = ? WHERE TaxonId = ?"; 
299
		parentTaxonFk_TreeIndex_KingdomFkStmt = connection.prepareStatement(parentTaxonFk_TreeIndex_KingdomFkSql);
300
	}
301

    
302
	private void initRankUpdateStatement(PesiExportState state) throws SQLException {
303
		Connection connection = state.getConfig().getDestination().getConnection();
304
		String rankSql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, KingdomFk = ? WHERE TaxonId = ?";
305
		rankUpdateStmt = connection.prepareStatement(rankSql);
306
	}
307

    
308
	private void initRankExpertsUpdateStmt(PesiExportState state) throws SQLException {
309
//		String sql_old = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ?, " +
310
//				"ExpertFk = ?, SpeciesExpertFk = ? WHERE TaxonId = ?";
311
		//TODO handle experts GUIDs
312
		Connection connection = state.getConfig().getDestination().getConnection();
313
		
314
		String sql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ? " +
315
				" WHERE TaxonId = ?";
316
		rankTypeExpertsUpdateStmt = connection.prepareStatement(sql);
317
	}
318

    
319
	private boolean doPhase01(PesiExportState state, PesiExportMapping mapping) throws SQLException {
320
		int count = 0;
321
		int pastCount = 0;
322
		List<TaxonBase> list;
323
		boolean success = true;
324
		// Get the limit for objects to save within a single transaction.
325
		int limit = state.getConfig().getLimitSave();
326

    
327
		
328
		logger.info("PHASE 1: Export Taxa...");
329
		// Start transaction
330
		TransactionStatus txStatus = startTransaction(true);
331
		logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
332
		
333
		
334
		int partitionCount = 0;
335
		while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)).size() > 0   ) {
336

    
337
			logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
338
			for (TaxonBase<?> taxon : list) {
339
				doCount(count++, modCount, pluralString);
340
				TaxonNameBase<?,?> taxonName = taxon.getName();
341
				NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
342
				
343
				if (! nvn.isProtectedTitleCache()){
344
					nvn.setTitleCache(null, false);	
345
				}
346
				if (! nvn.isProtectedNameCache()){
347
					nvn.setNameCache(null, false);	
348
				}
349
				if (! nvn.isProtectedFullTitleCache()){
350
					nvn.setFullTitleCache(null, false);	
351
				}
352
				if (! nvn.isProtectedAuthorshipCache()){
353
					nvn.setAuthorshipCache(null, false);	
354
				}
355
				
356
				success &= mapping.invoke(taxon);
357
				
358
				validatePhaseOne(taxon, nvn);
359
				
360
			}
361

    
362
			// Commit transaction
363
			commitTransaction(txStatus);
364
			logger.debug("Committed transaction.");
365
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
366
			pastCount = count;
367

    
368
			// Start transaction
369
			txStatus = startTransaction(true);
370
			logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
371
		}
372
		if (list.size() == 0) {
373
			logger.info("No " + pluralString + " left to fetch.");
374
		}
375
		// Commit transaction
376
		commitTransaction(txStatus);
377
		logger.debug("Committed transaction.");
378
		return success;
379
	}
380

    
381

    
382
	private void validatePhaseOne(TaxonBase<?> taxon, NonViralName taxonName) {
383
		// Check whether some rules are violated
384
		nomenclaturalCode = taxonName.getNomenclaturalCode();
385
		String genusOrUninomial = taxonName.getGenusOrUninomial();
386
		String specificEpithet = taxonName.getSpecificEpithet();
387
		String infraSpecificEpithet = taxonName.getInfraSpecificEpithet();
388
		String infraGenericEpithet = taxonName.getInfraGenericEpithet();
389
		Integer rank = getRankFk(taxonName, nomenclaturalCode);
390
		
391
		if (rank == null) {
392
			logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
393
		} else {
394
			
395
			// Check whether infraGenericEpithet is set correctly
396
			// 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
397
			// 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
398
			
399
			int ancestorLevel = 0;
400
			if (taxonName.getRank().equals(Rank.SUBSPECIES())) {
401
				// The accepted taxon two rank levels above should be of rank subgenus
402
				ancestorLevel  = 2;
403
			}
404
			if (taxonName.getRank().equals(Rank.SPECIES())) {
405
				// The accepted taxon one rank level above should be of rank subgenus
406
				ancestorLevel = 1;
407
			}
408
			if (ancestorLevel > 0) {
409
				if (ancestorOfSpecificRank(taxon, ancestorLevel, Rank.SUBGENUS())) {
410
					// The child (species or subspecies) of this parent (subgenus) has to have an infraGenericEpithet
411
					if (infraGenericEpithet == null) {
412
						logger.warn("InfraGenericEpithet does not exist even though it should for: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
413
						// maybe the taxon could be named here
414
					}
415
				}
416
			}
417
			
418
			if (infraGenericEpithet == null && rank.intValue() == 190) {
419
				logger.warn("InfraGenericEpithet was not determined although it should exist for rank 190: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
420
			}
421
			if (specificEpithet != null && rank.intValue() < 216) {
422
				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() + ")");
423
			}
424
			if (infraSpecificEpithet != null && rank.intValue() < 225) {
425
				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() + ")"; 
426
				if (StringUtils.isNotBlank(infraSpecificEpithet)){
427
					logger.warn(message);
428
				}else{
429
					logger.warn(message);
430
				}
431
			}
432
		}
433
		if (infraSpecificEpithet != null && specificEpithet == null) {
434
			logger.warn("An infraSpecificEpithet was determined, but a specificEpithet was not determined: "  + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
435
		}
436
		if (genusOrUninomial == null) {
437
			logger.warn("GenusOrUninomial was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
438
		}
439
	}
440

    
441
	// 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
442
	private boolean doPhase02(PesiExportState state) {
443
		boolean success = true;
444
		if (! state.getConfig().isDoTreeIndex()){
445
			logger.info ("Ignore PHASE 2: ParentTaxonFk and TreeIndex");
446
			return success;
447
		}
448
		
449
		List<Classification> classificationList = null;
450
		logger.info("PHASE 2: Add ParenTaxonFk and TreeIndex...");
451
		
452
		// Specify starting ranks for tree traversing
453
		rankList.add(Rank.KINGDOM());
454
		rankList.add(Rank.GENUS());
455

    
456
		// Specify where to stop traversing (value) when starting at a specific Rank (key)
457
		rank2endRankMap.put(Rank.GENUS(), null); // Since NULL does not match an existing Rank, traverse all the way down to the leaves
458
		rank2endRankMap.put(Rank.KINGDOM(), Rank.GENUS()); // excludes rank genus
459
		
460
		StringBuffer treeIndex = new StringBuffer();
461
		
462
		// Retrieve list of classifications
463
		TransactionStatus txStatus = startTransaction(true);
464
		logger.info("Started transaction for parentFk and treeIndex. Fetching all classifications...");
465
		classificationList = getClassificationService().listClassifications(null, 0, null, null);
466
		commitTransaction(txStatus);
467
		logger.debug("Committed transaction.");
468

    
469
		logger.info("Fetched " + classificationList.size() + " classification(s).");
470

    
471
		setTreeIndexAnnotationType(getAnnotationType(uuidTreeIndex, "TreeIndex", "TreeIndex", "TI"));
472
		
473
		for (Classification classification : classificationList) {
474
			for (Rank rank : rankList) {
475
				
476
				txStatus = startTransaction(true);
477
				logger.info("Started transaction to fetch all rootNodes specific to Rank " + rank.getLabel() + " ...");
478

    
479
				List<TaxonNode> rankSpecificRootNodes = getClassificationService().loadRankSpecificRootNodes(classification, rank, null);
480
				logger.info("Fetched " + rankSpecificRootNodes.size() + " RootNodes for Rank " + rank.getLabel());
481

    
482
				commitTransaction(txStatus);
483
				logger.debug("Committed transaction.");
484

    
485
				for (TaxonNode rootNode : rankSpecificRootNodes) {
486
					txStatus = startTransaction(false);
487
					Rank endRank = rank2endRankMap.get(rank);
488
					if (endRank != null) {
489
						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till Rank " + endRank.getLabel() + " ...");
490
					} else {
491
						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till leaves are reached ...");
492
					}
493

    
494
					TaxonNode newNode = getTaxonNodeService().load(rootNode.getUuid());
495

    
496
					if (isPesiTaxon(newNode.getTaxon())){
497
						TaxonNode parentNode = newNode.getParent();
498
						if (rank.equals(Rank.KINGDOM())) {
499
							treeIndex = new StringBuffer();
500
							treeIndex.append("#");
501
						} else {
502
							// Get treeIndex from parentNode
503
							if (parentNode != null) {
504
								boolean annotationFound = false;
505
								Set<Annotation> annotations = parentNode.getAnnotations();
506
								for (Annotation annotation : annotations) {
507
									AnnotationType annotationType = annotation.getAnnotationType();
508
									if (annotationType != null && annotationType.equals(getTreeIndexAnnotationType())) {
509
										treeIndex = new StringBuffer(CdmUtils.Nz(annotation.getText()));
510
										annotationFound = true;
511
	//									logger.error("treeIndex: " + treeIndex);
512
										break;
513
									}
514
								}
515
								if (!annotationFound) {
516
									// This should not happen because it means that the treeIndex was not set correctly as an annotation to parentNode
517
									logger.error("TreeIndex could not be read from annotation of TaxonNode: " + parentNode.getUuid() + ", Taxon: " + parentNode.getTaxon().getUuid());
518
									treeIndex = new StringBuffer();
519
									treeIndex.append("#");
520
								}
521
							} else {
522
								// TreeIndex could not be determined, but it's unclear how to proceed to generate a correct treeIndex if the parentNode is NULL
523
								logger.error("ParentNode for RootNode is NULL. TreeIndex could not be determined: " + newNode.getUuid());
524
								treeIndex = new StringBuffer(); // This just prevents growing of the treeIndex in a wrong manner
525
								treeIndex.append("#");
526
							}
527
						}
528
						nomenclaturalCode = newNode.getTaxon().getName().getNomenclaturalCode();
529
						kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
530
						traverseTree(newNode, parentNode, treeIndex, endRank, state);
531
					}else{
532
						logger.debug("Taxon is not a PESI taxon: " + newNode.getTaxon().getUuid());
533
					}
534
					
535
					
536
					
537
					commitTransaction(txStatus);
538
					logger.debug("Committed transaction.");
539

    
540
				}
541
			}
542
		}
543
		return success;
544
	}	
545

    
546
	//PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...
547
	private boolean doPhase03(PesiExportState state) {
548
		int count = 0;
549
		int pastCount = 0;
550
		boolean success = true;
551
		if (! state.getConfig().isDoTreeIndex()){
552
			logger.info ("Ignore PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
553
			return success;
554
		}
555
		// Get the limit for objects to save within a single transaction.
556
		int limit = state.getConfig().getLimitSave();
557

    
558
		List<TaxonBase> list;
559
		logger.info("PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
560
		// Be sure to add rank information, KingdomFk, TypeNameFk, expertFk and speciesExpertFk to every taxonName
561
		
562
		// Start transaction
563
		TransactionStatus txStatus = startTransaction(true);
564
		logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
565
		int partitionCount = 0;
566
		while ((list = getNextTaxonPartition(TaxonBase.class, limit, partitionCount++, null)).size() > 0) {
567

    
568
			logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
569
			for (TaxonBase<?> taxon : list) {
570
				TaxonNameBase<?,?> taxonName = taxon.getName();
571
				// Determine expertFk
572
				Integer expertFk = makeExpertFk(state, taxonName);
573

    
574
				// Determine speciesExpertFk
575
				Integer speciesExpertFk = makeSpeciesExpertFk(state, taxonName);
576

    
577
				doCount(count++, modCount, pluralString);
578
				Integer typeNameFk = getTypeNameFk(taxonName, state);
579
				
580
				//TODO why are expertFks needed? (Andreas M.)
581
//				if (expertFk != null || speciesExpertFk != null) {
582
					invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, nomenclaturalCode, state.getDbId(taxon), 
583
							typeNameFk, kingdomFk, expertFk, speciesExpertFk);
584
//				}
585
			}
586

    
587
			// Commit transaction
588
			commitTransaction(txStatus);
589
			logger.debug("Committed transaction.");
590
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
591
			pastCount = count;
592

    
593
			// Start transaction
594
			txStatus = startTransaction(true);
595
			logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
596
		}
597
		if (list.size() == 0) {
598
			logger.info("No " + pluralString + " left to fetch.");
599
		}
600
		// Commit transaction
601
		commitTransaction(txStatus);
602
		logger.debug("Committed transaction.");
603
		return success;
604
	}
605

    
606
	private Integer makeSpeciesExpertFk(PesiExportState state, TaxonNameBase<?, ?> taxonName) {
607
		List<Reference> referenceList;
608
		Integer speciesExpertFk = null;
609
		String speciesExpertUserId = getSpeciesExpertUserId(taxonName);
610
		if (speciesExpertUserId != null) {
611
			
612
			// The speciesExpertUserId was stored in the field 'title' of the corresponding Reference during FaEu import
613
			referenceList = getReferenceService().listByReferenceTitle(null, speciesExpertUserId, MatchMode.EXACT, null, null, null, null, null);
614
			if (referenceList.size() == 1) {
615
				speciesExpertFk  = getSpeciesExpertFk(referenceList.iterator().next(), state);
616
			} else if (referenceList.size() > 1) {
617
				logger.error("Found more than one match using listByTitle() searching for a Reference with this speciesExpertUserId as title: " + speciesExpertUserId);
618
			} else if (referenceList.size() == 0) {
619
				logger.error("Found no match using listByReferenceTitle() searching for a Reference with this speciesExpertUserId as title: " + speciesExpertUserId);
620
			}
621
		} else {
622
			logger.debug("SpeciesExpertName is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
623
		}
624
		return speciesExpertFk;
625
	}
626

    
627
	private Integer makeExpertFk(PesiExportState state,
628
			TaxonNameBase<?, ?> taxonName) {
629
		List<Reference> referenceList;
630
		Integer expertFk = null;
631
		String expertUserId = getExpertUserId(taxonName);
632
		if (expertUserId != null) {
633

    
634
			// The expertUserId was stored in the field 'title' of the corresponding Reference during FaEu import
635
			referenceList = getReferenceService().listByReferenceTitle(null, expertUserId, MatchMode.EXACT, null, null, null, null, null);
636
			if (referenceList.size() == 1) {
637
				expertFk  = getExpertFk(referenceList.iterator().next(), state);
638
			} else if (referenceList.size() > 1) {
639
				logger.error("Found more than one match using listByReferenceTitle() searching for a Reference with this expertUserId as title: " + expertUserId);
640
			} else if (referenceList.size() == 0) {
641
				logger.error("Found no match using listByReferenceTitle() searching for a Reference with this expertUserId as title: " + expertUserId);
642
			}
643
		} else {
644
			logger.debug("ExpertName is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
645
		}
646
		return expertFk;
647
	}
648
	
649
	//	"PHASE 4: Creating Inferred Synonyms..."
650
	private boolean doPhase04(PesiExportState state, PesiExportMapping mapping) throws SQLException {
651
		int count;
652
		int pastCount;
653
		boolean success = true;
654
		// Get the limit for objects to save within a single transaction.
655
		if (! state.getConfig().isDoTreeIndex()){
656
			logger.info ("Ignore PHASE 4: Creating Inferred Synonyms...");
657
			return success;
658
		}
659
		
660
		int limit = state.getConfig().getLimitSave();
661
		// Create inferred synonyms for accepted taxa
662
		logger.info("PHASE 4: Creating Inferred Synonyms...");
663

    
664
		// Determine the count of elements in datawarehouse database table Taxon
665
		Integer currentTaxonId = determineTaxonCount(state);
666
		currentTaxonId++;
667

    
668
		count = 0;
669
		pastCount = 0;
670
		int pageSize = limit;
671
		int pageNumber = 1;
672
		String inferredSynonymPluralString = "Inferred Synonyms";
673
		
674
		// Start transaction
675
		Classification classification = null;
676
		Taxon acceptedTaxon = null;
677
		TransactionStatus txStatus = startTransaction(true);
678
		logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
679
		List<TaxonBase> taxonList = null;
680
		List<Synonym> inferredSynonyms = null;
681
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", null, pageSize, pageNumber)).size() > 0) {
682
			HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
683

    
684
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
685
			for (TaxonBase<?> taxonBase : taxonList) {
686

    
687
				if (taxonBase.isInstanceOf(Taxon.class)) { // this should always be the case since we should have fetched accepted taxon only, but you never know...
688
					acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
689
					TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();
690
					
691
					if (taxonName.isInstanceOf(ZoologicalName.class)) {
692
						nomenclaturalCode  = taxonName.getNomenclaturalCode();
693
						kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
694

    
695
						Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
696
						TaxonNode singleNode = null;
697
						if (taxonNodes.size() > 0) {
698
							// Determine the classification of the current TaxonNode
699
							singleNode = taxonNodes.iterator().next();
700
							if (singleNode != null) {
701
								classification = singleNode.getClassification();
702
							} else {
703
								logger.error("A TaxonNode belonging to this accepted Taxon is NULL: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() +")");
704
							}
705
						} else {
706
							// Classification could not be determined directly from this TaxonNode
707
							// The stored classification from another TaxonNode is used. It's a simple, but not a failsafe fallback solution.
708
							if (classification == null) {
709
								logger.error("Classification could not be determined directly from this TaxonNode: " + singleNode.getUuid() + "). " +
710
										"This classification stored from another TaxonNode is used: " + classification.getTitleCache());
711
							}
712
						}
713
						
714
						if (classification != null) {
715
							try{
716
								inferredSynonyms  = getTaxonService().createAllInferredSynonyms(acceptedTaxon, classification);
717
	
718
	//								inferredSynonyms = getTaxonService().createInferredSynonyms(classification, acceptedTaxon, SynonymRelationshipType.INFERRED_GENUS_OF());
719
								if (inferredSynonyms != null) {
720
									for (Synonym synonym : inferredSynonyms) {
721
	//									TaxonNameBase<?,?> synonymName = synonym.getName();
722
											
723
										// Both Synonym and its TaxonName have no valid Id yet
724
										synonym.setId(currentTaxonId++);
725
										
726
										doCount(count++, modCount, inferredSynonymPluralString);
727
										success &= mapping.invoke(synonym);
728
										
729
										// Add Rank Data and KingdomFk to hashmap for later saving
730
										inferredSynonymsDataToBeSaved.put(synonym.getId(), synonym.getName());
731
									}
732
								}
733
							}catch(Exception e){
734
								logger.error(e.getMessage());
735
								e.printStackTrace();
736
							}
737
						} else {
738
							logger.error("Classification is NULL. Inferred Synonyms could not be created for this Taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() + ")");
739
						}
740
					} else {
741
//							logger.error("TaxonName is not a ZoologicalName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
742
					}
743
				} else {
744
					logger.error("This TaxonBase is not a Taxon even though it should be: " + taxonBase.getUuid() + " (" + taxonBase.getTitleCache() + ")");
745
				}
746
			}
747

    
748
			// Commit transaction
749
			commitTransaction(txStatus);
750
			logger.debug("Committed transaction.");
751
			logger.info("Exported " + (count - pastCount) + " " + inferredSynonymPluralString + ". Total: " + count);
752
			pastCount = count;
753
			
754
			// Save Rank Data and KingdomFk for inferred synonyms
755
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
756
				invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomenclaturalCode, taxonFk, kingdomFk);
757
			}
758

    
759
			// Start transaction
760
			txStatus = startTransaction(true);
761
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
762
			
763
			// Increment pageNumber
764
			pageNumber++;
765
		}
766
		if (taxonList.size() == 0) {
767
			logger.info("No " + parentPluralString + " left to fetch.");
768
		}
769
		// Commit transaction
770
		commitTransaction(txStatus);
771
		logger.debug("Committed transaction.");
772
		return success;
773
	}
774
	
775

    
776
	/**
777
	 * Handles names that do not appear in taxa
778
	 * @param state
779
	 * @param mapping
780
	 * @return
781
	 */
782
	private boolean doNames(PesiExportState state)  throws SQLException {
783
		
784
		boolean success = true;
785
		if (! state.getConfig().isDoPureNames()){
786
			logger.info ("Ignore PHASE 1b: PureNames");
787
			return success;
788
		}
789
		
790
		try {
791
			PesiExportMapping mapping = getPureNameMapping();
792
			mapping.initialize(state);
793
			int count = 0;
794
			int pastCount = 0;
795
			List<NonViralName<?>> list;
796
			success = true;
797
			// Get the limit for objects to save within a single transaction.
798
			int limit = state.getConfig().getLimitSave();
799

    
800
			
801
			logger.info("PHASE 1b: Export Pure Names ...");
802
			// Start transaction
803
			TransactionStatus txStatus = startTransaction(true);
804
			logger.info("Started new transaction for Pure Names. Fetching some " + pluralString + " (max: " + limit + ") ...");
805
			
806
			
807
			int partitionCount = 0;
808
			while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null   ) {
809

    
810
				logger.info("Fetched " + list.size() + " names without taxa. Exporting...");
811
				for (TaxonNameBase taxonName : list) {
812
					doCount(count++, modCount, pluralString);
813
					success &= mapping.invoke(taxonName);
814
				}
815

    
816
				// Commit transaction
817
				commitTransaction(txStatus);
818
				logger.debug("Committed transaction.");
819
				logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
820
				pastCount = count;
821

    
822
				// Start transaction
823
				txStatus = startTransaction(true);
824
				logger.info("Started new transaction for PureNames. Fetching some " + pluralString + " (max: " + limit + ") ...");
825
			}
826
			if (list == null) {
827
				logger.info("No " + pluralString + " left to fetch.");
828
			}
829
			// Commit transaction
830
			commitTransaction(txStatus);
831
			logger.debug("Committed transaction.");
832
		} catch (Exception e) {
833
			logger.error("Error occurred in pure name export");
834
			e.printStackTrace();
835
			success = false;
836
		}
837
		return success;
838
	}
839

    
840
	/**
841
	 * Determines the current number of entries in the DataWarehouse database table <code>Taxon</code>.
842
	 * @param state The {@link PesiExportState PesiExportState}.
843
	 * @return The count.
844
	 */
845
	private Integer determineTaxonCount(PesiExportState state) {
846
		Integer result = null;
847
		PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
848
		
849
		String sql;
850
		Source destination =  pesiConfig.getDestination();
851
		sql = "SELECT max(taxonId) FROM Taxon";
852
		destination.setQuery(sql);
853
		ResultSet resultSet = destination.getResultSet();
854
		try {
855
			resultSet.next();
856
			result = resultSet.getInt(1);
857
		} catch (SQLException e) {
858
			logger.error("TaxonCount could not be determined: " + e.getMessage());
859
			e.printStackTrace();
860
		}
861
		return result;
862
	}
863

    
864
	/**
865
	 * Returns the userId of the expert associated with the given TaxonName.
866
	 * @param taxonName A {@link TaxonNameBase TaxonName}.
867
	 * @return The userId.
868
	 */
869
	private String getExpertUserId(TaxonNameBase<?,?> taxonName) {
870
		String result = null;
871
		try {
872
			Set<Extension> extensions = taxonName.getExtensions();
873
			for (Extension extension : extensions) {
874
				if (extension.getType().equals(expertUserIdExtensionType)) {
875
					result = extension.getValue();
876
				}
877
			}
878
		} catch (Exception e) {
879
			e.printStackTrace();
880
		}
881
		return result;
882
	}
883

    
884
	/**
885
	 * Returns the userId of the speciesExpert associated with the given TaxonName.
886
	 * @param taxonName A {@link TaxonNameBase TaxonName}.
887
	 * @return The userId.
888
	 */
889
	private String getSpeciesExpertUserId(TaxonNameBase<?,?> taxonName) {
890
		String result = null;
891
		try {
892
			Set<Extension> extensions = taxonName.getExtensions();
893
			for (Extension extension : extensions) {
894
				if (extension.getType().equals(speciesExpertUserIdExtensionType)) {
895
					result = extension.getValue();
896
				}
897
			}
898
		} catch (Exception e) {
899
			e.printStackTrace();
900
		}
901
		return result;
902
	}
903
	
904
	/**
905
	 * Checks whether a parent at specific level has a specific Rank.
906
	 * @param taxonName A {@link TaxonNameBase TaxonName}.
907
	 * @param level The ancestor level.
908
	 * @param ancestorRank The ancestor rank.
909
	 * @return Whether a parent at a specific level has a specific Rank.
910
	 */
911
	private boolean ancestorOfSpecificRank(TaxonBase<?> taxonBase, int level, Rank ancestorRank) {
912
		boolean result = false;
913
		TaxonNode parentNode = null;
914
		if (taxonBase.isInstanceOf(Taxon.class)){
915
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
916
			// Get ancestor Taxon via TaxonNode
917
			Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();
918
			if (taxonNodes.size() == 1) {
919
				TaxonNode taxonNode = taxonNodes.iterator().next();
920
				if (taxonNode != null) {
921
					for (int i = 0; i < level; i++) {
922
						if (taxonNode != null) {
923
							taxonNode  = taxonNode.getParent();
924
						}
925
					}
926
					parentNode = taxonNode;
927
				}
928
			} else if (taxonNodes.size() > 1) {
929
				logger.error("This taxon has " + taxonNodes.size() + " taxonNodes: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
930
			}
931
		}
932
		//compare
933
		if (parentNode != null) {
934
			TaxonNode node = CdmBase.deproxy(parentNode, TaxonNode.class);
935
			Taxon parentTaxon = node.getTaxon();
936
			if (parentTaxon != null) {
937
				TaxonNameBase<?,?> parentTaxonName = parentTaxon.getName();
938
				if (parentTaxonName != null && parentTaxonName.getRank().equals(ancestorRank)) {
939
					result = true;
940
				}
941
			} else {
942
				logger.error("This TaxonNode has no Taxon: " + node.getUuid());
943
			}
944
		}
945
		return result;
946
	}
947

    
948
	/**
949
	 * Returns the AnnotationType for a given UUID.
950
	 * @param uuid The Annotation UUID.
951
	 * @param label The Annotation label.
952
	 * @param text The Annotation text.
953
	 * @param labelAbbrev The Annotation label abbreviation.
954
	 * @return The AnnotationType.
955
	 */
956
	protected AnnotationType getAnnotationType(UUID uuid, String label, String text, String labelAbbrev){
957
		AnnotationType annotationType = (AnnotationType)getTermService().find(uuid);
958
		if (annotationType == null) {
959
			annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
960
			annotationType.setUuid(uuid);
961
//			annotationType.setVocabulary(AnnotationType.EDITORIAL().getVocabulary());
962
			getTermService().save(annotationType);
963
		}
964
		return annotationType;
965
	}
966

    
967
	/**
968
	 * Traverses the classification recursively and stores determined values for every Taxon.
969
	 * @param childNode The {@link TaxonNode TaxonNode} to process.
970
	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
971
	 * @param treeIndex The TreeIndex at the current level.
972
	 * @param fetchLevel Rank to stop fetching at.
973
	 * @param state The {@link PesiExportState PesiExportState}.
974
	 */
975
	private void traverseTree(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, Rank fetchLevel, PesiExportState state) {
976
		// Traverse all branches from this childNode until specified fetchLevel is reached.
977
		StringBuffer localTreeIndex = new StringBuffer(treeIndex);
978
		Taxon childTaxon = childNode.getTaxon();
979
		if (childTaxon != null) {
980
			if (isPesiTaxon(childTaxon)){
981
				Integer taxonId = state.getDbId(childTaxon);
982
				TaxonNameBase<?,?> childName = childTaxon.getName();
983
				if (taxonId != null) {
984
					Rank childRank = childName.getRank();
985
					if (childRank != null) {
986
						if (! childRank.equals(fetchLevel)) {
987
	
988
							localTreeIndex.append(taxonId + "#");
989
							
990
							saveData(childNode, parentNode, localTreeIndex, state, taxonId);
991
	
992
							// Store treeIndex as annotation for further use
993
							Annotation annotation = Annotation.NewInstance(localTreeIndex.toString(), getTreeIndexAnnotationType(), Language.DEFAULT());
994
							childNode.addAnnotation(annotation);
995
	
996
							for (TaxonNode newNode : childNode.getChildNodes()) {
997
								if (newNode.getTaxon() != null && isPesiTaxon(newNode.getTaxon())){
998
									traverseTree(newNode, childNode, localTreeIndex, fetchLevel, state);
999
								}
1000
							}
1001
							
1002
						} else {
1003
	//						logger.debug("Target Rank " + fetchLevel.getLabel() + " reached");
1004
							return;
1005
						}
1006
					} else {
1007
						logger.error("Rank is NULL. FetchLevel can not be checked: " + childName.getUuid() + " (" + childName.getTitleCache() + ")");
1008
					}
1009
				} else {
1010
					logger.error("Taxon can not be found in state: " + childTaxon.getUuid() + " (" + childTaxon.getTitleCache() + ")");
1011
				}
1012
			}else{
1013
				if (logger.isDebugEnabled()){ 
1014
					logger.debug("Taxon is not a PESI taxon: " + childTaxon.getUuid());
1015
				}
1016
			}
1017

    
1018
		} else {
1019
			logger.error("Taxon is NULL for TaxonNode: " + childNode.getUuid());
1020
		}
1021
	}
1022

    
1023
	/**
1024
	 * Stores values in database for every recursive round.
1025
	 * @param childNode The {@link TaxonNode TaxonNode} to process.
1026
	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1027
	 * @param treeIndex The TreeIndex at the current level.
1028
	 * @param state The {@link PesiExportState PesiExportState}.
1029
	 * @param currentTaxonFk The TaxonFk to store the values for.
1030
	 */
1031
	private void saveData(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, PesiExportState state, Integer currentTaxonFk) {
1032
		// We are differentiating kingdoms by the nomenclatural code for now.
1033
		// This needs to be handled in a better way as soon as we know how to differentiate between more kingdoms.
1034
		Taxon childTaxon = childNode.getTaxon();
1035
		if (isPesiTaxon(childTaxon)) {
1036
			TaxonBase<?> parentTaxon = null;
1037
			if (parentNode != null) {
1038
				parentTaxon = parentNode.getTaxon();
1039
				
1040
			}
1041

    
1042
			invokeParentTaxonFkAndTreeIndex(state.getDbId(parentTaxon), currentTaxonFk,	treeIndex);
1043
		}
1044
		
1045
	}
1046

    
1047
	/**
1048
	 * Inserts values into the Taxon database table.
1049
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1050
	 * @param state The {@link PesiExportState PesiExportState}.
1051
	 * @param stmt The prepared statement.
1052
	 * @return Whether save was successful or not.
1053
	 */
1054
	protected boolean invokeParentTaxonFkAndTreeIndex(Integer parentTaxonFk, Integer currentTaxonFk, StringBuffer treeIndex) {
1055
		try {
1056
			if (parentTaxonFk != null) {
1057
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(1, parentTaxonFk);
1058
			} else {
1059
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(1, null);
1060
			}
1061

    
1062
			if (treeIndex != null) {
1063
				parentTaxonFk_TreeIndex_KingdomFkStmt.setString(2, treeIndex.toString());
1064
			} else {
1065
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(2, null);
1066
			}
1067

    
1068
			if (currentTaxonFk != null) {
1069
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(3, currentTaxonFk);
1070
			} else {
1071
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(3, null);
1072
			}
1073
			
1074
			parentTaxonFk_TreeIndex_KingdomFkStmt.executeUpdate();
1075
			return true;
1076
		} catch (SQLException e) {
1077
			logger.error("ParentTaxonFk (" + parentTaxonFk ==null? "-":parentTaxonFk + ") and TreeIndex could not be inserted into database for taxon "+ (currentTaxonFk == null? "-" :currentTaxonFk) + ": " + e.getMessage());
1078
			e.printStackTrace();
1079
			return false;
1080
		}
1081
	}
1082

    
1083
	/**
1084
	 * Inserts Rank data and KingdomFk into the Taxon database table.
1085
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1086
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1087
	 * @param taxonFk The TaxonFk to store the values for.
1088
	 * @param kindomFk The KingdomFk.
1089
	 * @return Whether save was successful or not.
1090
	 */
1091
	private boolean invokeRankDataAndKingdomFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, Integer taxonFk, Integer kingdomFk) {
1092
		try {
1093
			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1094
			if (rankFk != null) {
1095
				rankUpdateStmt.setInt(1, rankFk);
1096
			} else {
1097
				rankUpdateStmt.setObject(1, null);
1098
			}
1099
	
1100
			String rankCache = getRankCache(taxonName, nomenclaturalCode);
1101
			if (rankCache != null) {
1102
				rankUpdateStmt.setString(2, rankCache);
1103
			} else {
1104
				rankUpdateStmt.setObject(2, null);
1105
			}
1106
			
1107
			if (kingdomFk != null) {
1108
				rankUpdateStmt.setInt(3, kingdomFk);
1109
			} else {
1110
				rankUpdateStmt.setObject(3, null);
1111
			}
1112
			
1113
			if (taxonFk != null) {
1114
				rankUpdateStmt.setInt(4, taxonFk);
1115
			} else {
1116
				rankUpdateStmt.setObject(4, null);
1117
			}
1118
			
1119
			rankUpdateStmt.executeUpdate();
1120
			return true;
1121
		} catch (SQLException e) {
1122
			logger.error("Data (RankFk, RankCache, KingdomFk) could not be inserted into database: " + e.getMessage());
1123
			e.printStackTrace();
1124
			return false;
1125
		}
1126
	}
1127

    
1128
	/**
1129
	 * Inserts Rank data, TypeNameFk, KingdomFk, expertFk and speciesExpertFk into the Taxon database table.
1130
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1131
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1132
	 * @param taxonFk The TaxonFk to store the values for.
1133
	 * @param typeNameFk The TypeNameFk.
1134
	 * @param kindomFk The KingdomFk.
1135
	 * @param expertFk The ExpertFk.
1136
	 * @param speciesExpertFk The SpeciesExpertFk.
1137
	 * @return Whether save was successful or not.
1138
	 */
1139
	private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, 
1140
			Integer taxonFk, Integer typeNameFk, Integer kingdomFk,
1141
			Integer expertFk, Integer speciesExpertFk) {
1142
		try {
1143
			int index = 1;
1144
			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1145
			if (rankFk != null) {
1146
				rankTypeExpertsUpdateStmt.setInt(index++, rankFk);
1147
			} else {
1148
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1149
			}
1150
	
1151
			String rankCache = getRankCache(taxonName, nomenclaturalCode);
1152
			if (rankCache != null) {
1153
				rankTypeExpertsUpdateStmt.setString(index++, rankCache);
1154
			} else {
1155
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1156
			}
1157
			
1158
			if (typeNameFk != null) {
1159
				rankTypeExpertsUpdateStmt.setInt(index++, typeNameFk);
1160
			} else {
1161
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1162
			}
1163
			
1164
			if (kingdomFk != null) {
1165
				rankTypeExpertsUpdateStmt.setInt(index++, kingdomFk);
1166
			} else {
1167
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1168
			}
1169
			
1170
//			if (expertFk != null) {
1171
//				rankTypeExpertsUpdateStmt.setInt(5, expertFk);
1172
//			} else {
1173
//				rankTypeExpertsUpdateStmt.setObject(5, null);
1174
//			}
1175
//
1176
//			//TODO handle experts GUIDS
1177
//			if (speciesExpertFk != null) {
1178
//				rankTypeExpertsUpdateStmt.setInt(6, speciesExpertFk);
1179
//			} else {
1180
//				rankTypeExpertsUpdateStmt.setObject(6, null);
1181
//			}
1182
//			
1183
			if (taxonFk != null) {
1184
				rankTypeExpertsUpdateStmt.setInt(index++, taxonFk);
1185
			} else {
1186
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1187
			}
1188

    
1189
			rankTypeExpertsUpdateStmt.executeUpdate();
1190
			return true;
1191
		} catch (SQLException e) {
1192
			logger.error("Data could not be inserted into database: " + e.getMessage());
1193
			e.printStackTrace();
1194
			return false;
1195
		}
1196
	}
1197

    
1198
	/**
1199
	 * Deletes all entries of database tables related to <code>Taxon</code>.
1200
	 * @param state The {@link PesiExportState PesiExportState}.
1201
	 * @return Whether the delete operation was successful or not.
1202
	 */
1203
	protected boolean doDelete(PesiExportState state) {
1204
		PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
1205
		
1206
		String sql;
1207
		Source destination =  pesiConfig.getDestination();
1208

    
1209
		// Clear Taxon
1210
		sql = "DELETE FROM " + dbTableName;
1211
		destination.setQuery(sql);
1212
		destination.update(sql);
1213
		return true;
1214
	}
1215

    
1216
	/* (non-Javadoc)
1217
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
1218
	 */
1219
	@Override
1220
	protected boolean isIgnore(PesiExportState state) {
1221
		return ! state.getConfig().isDoTaxa();
1222
	}
1223

    
1224
	
1225
	private static Integer getKingdomFk(TaxonNameBase taxonName){
1226
		return PesiTransformer.nomenClaturalCode2Kingdom(taxonName.getNomenclaturalCode());
1227
	}
1228

    
1229
	
1230
	/**
1231
	 * Returns the rankFk for the taxon name based on the names nomenclatural code.
1232
	 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1233
	 * @param taxonName
1234
	 * @return
1235
	 */
1236
	@SuppressWarnings("unused")  //used by mapper
1237
	private static Integer getRankFk(TaxonNameBase<?,?> taxonName) {
1238
		return getRankFk(taxonName, taxonName.getNomenclaturalCode());
1239
	}
1240
		
1241
	
1242
	/**
1243
	 * Returns the <code>RankFk</code> attribute.
1244
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1245
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1246
	 * @return The <code>RankFk</code> attribute.
1247
	 * @see MethodMapper
1248
	 */
1249
	private static Integer getRankFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode) {
1250
		Integer result = null;
1251
		try {
1252
			if (nomenclaturalCode != null) {
1253
				if (taxonName != null) {
1254
					if (taxonName.getRank() == null) {
1255
						logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1256
					} else {
1257
						result = PesiTransformer.rank2RankId(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1258
					}
1259
					if (result == null) {
1260
						logger.warn("Rank could not be determined for PESI-Kingdom-Id " + PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode) + " and TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1261
					}
1262
				}
1263
			}
1264
		} catch (Exception e) {
1265
			e.printStackTrace();
1266
		}
1267
		return result;
1268
	}
1269

    
1270
	/**
1271
	 * Returns the rank cache for the taxon name based on the names nomenclatural code.
1272
	 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1273
	 * @param taxonName
1274
	 * @return
1275
	 */
1276
	@SuppressWarnings("unused")  //used by mapper
1277
	private static String getRankCache(TaxonNameBase<?,?> taxonName) {
1278
		return getRankCache(taxonName, taxonName.getNomenclaturalCode());
1279
	}
1280

    
1281
	
1282
	/**
1283
	 * Returns the <code>RankCache</code> attribute.
1284
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1285
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1286
	 * @return The <code>RankCache</code> attribute.
1287
	 * @see MethodMapper
1288
	 */
1289
	private static String getRankCache(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode) {
1290
		String result = null;
1291
		try {
1292
			if (nomenclaturalCode != null) {
1293
				result = PesiTransformer.rank2RankCache(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1294
			}
1295
		} catch (Exception e) {
1296
			e.printStackTrace();
1297
		}
1298
		return result;
1299
	}
1300

    
1301
	
1302
	/**
1303
	 * Returns the <code>WebShowName</code> attribute.
1304
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1305
	 * @return The <code>WebShowName</code> attribute.
1306
	 * @see MethodMapper
1307
	 */
1308
	@SuppressWarnings("unused")
1309
	private static String getWebShowName(TaxonNameBase<?,?> taxonName) {
1310
		//TODO extensions?
1311
		if (taxonName == null) {
1312
			return null;
1313
		}else{
1314
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1315
		
1316
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1317
			NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1318
			return cacheStrategy.getTitleCache(nvn, tagRules);
1319
		}
1320
	}
1321

    
1322
	/**
1323
	 * Returns the <code>WebSearchName</code> attribute.
1324
	 * @param taxonName The {@link NonViralName NonViralName}.
1325
	 * @return The <code>WebSearchName</code> attribute.
1326
	 * @see MethodMapper
1327
	 */
1328
	@SuppressWarnings("unused")
1329
	private static String getWebSearchName(TaxonNameBase taxonName) {
1330
		//TODO extensions?
1331
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1332
		NonViralNameDefaultCacheStrategy strategy = getCacheStrategy(nvn);
1333
		String result = strategy.getNameCache(nvn);
1334
		return result;
1335
	}
1336

    
1337

    
1338
	/**
1339
	 * Returns the <code>FullName</code> attribute.
1340
	 * @param taxonName The {@link NonViralName NonViralName}.
1341
	 * @return The <code>FullName</code> attribute.
1342
	 * @see MethodMapper
1343
	 */
1344
	@SuppressWarnings("unused")
1345
	private static String getFullName(TaxonNameBase taxonName) {
1346
		//TODO extensions?
1347
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1348
		String result = getCacheStrategy(nvn).getTitleCache(nvn);
1349
		return result;
1350
	}
1351

    
1352
	
1353
	/**
1354
	 * Returns the <code>AuthorString</code> attribute.
1355
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1356
	 * @return The <code>AuthorString</code> attribute.
1357
	 * @see MethodMapper
1358
	 */
1359
	@SuppressWarnings("unused")
1360
	private static String getAuthorString(TaxonBase<?> taxon) {
1361
		String result = null;
1362
		try {
1363
			boolean isNonViralName = false;
1364
			String authorshipCache = null;
1365
			TaxonNameBase<?,?> taxonName = taxon.getName();
1366
			if (taxonName != null && taxonName.isInstanceOf(NonViralName.class)){
1367
				authorshipCache = CdmBase.deproxy(taxonName, NonViralName.class).getAuthorshipCache();
1368
				isNonViralName = true;
1369
			}
1370
			// For a misapplied name without an authorshipCache the authorString should be set to "auct."
1371
			if (isMisappliedName(taxon) && authorshipCache == null) {
1372
				// Set authorshipCache to "auct."
1373
				result = PesiTransformer.AUCT_STRING;
1374
			}else{
1375
				result = authorshipCache;
1376
			}
1377
			if (taxonName == null){
1378
				logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1379
			}else if (! isNonViralName){
1380
				logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1381
			}
1382
			
1383
		} catch (Exception e) {
1384
			e.printStackTrace();
1385
		}
1386
		
1387
		if (StringUtils.isBlank(result)) {
1388
			return null;
1389
		} else {
1390
			return result;
1391
		}
1392
	}
1393

    
1394
	
1395
	/**
1396
	 * Checks whether a given TaxonName is a misapplied name.
1397
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1398
	 * @return Whether the given TaxonName is a misapplied name or not.
1399
	 */	
1400
	private static boolean isMisappliedName(TaxonNameBase taxonName) {
1401
		boolean result = false;
1402
		Set<Taxon> taxa = taxonName.getTaxa();
1403
		if (taxa.size() == 1){
1404
			return isMisappliedName(taxa.iterator().next());
1405
		}else if (taxa.size() > 1){
1406
			logger.warn("TaxonNameBase has " + taxa.size() + " taxa attached. Can't define if it is a misapplied name.");
1407
		}
1408
		return result;
1409
	}
1410
		
1411
	
1412
	
1413

    
1414
	/**
1415
	 * Checks whether a given Taxon is a misapplied name.
1416
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1417
	 * @return Whether the given TaxonName is a misapplied name or not.
1418
	 */
1419
	private static boolean isMisappliedName(TaxonBase<?> taxon) {
1420
		boolean result = false;
1421
		
1422
		if (! taxon.isInstanceOf(Taxon.class)){
1423
			return false;
1424
		}
1425
		Set<TaxonRelationship> taxonRelations = CdmBase.deproxy(taxon, Taxon.class).getRelationsFromThisTaxon();
1426
		for (TaxonRelationship taxonRelationship : taxonRelations) {
1427
			TaxonRelationshipType taxonRelationshipType = taxonRelationship.getType();
1428
			if (taxonRelationshipType.equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())) {
1429
				result = true;
1430
			}
1431
		}
1432
		return result;
1433
	}
1434

    
1435
	
1436
	/**
1437
	 * Returns the <code>DisplayName</code> attribute.
1438
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1439
	 * @return The <code>DisplayName</code> attribute.
1440
	 * @see MethodMapper
1441
	 */
1442
	@SuppressWarnings("unused")
1443
	private static String getDisplayName(TaxonNameBase<?,?> taxonName) {
1444
		// TODO: extension?
1445
		if (taxonName == null) {
1446
			return null;
1447
		}else{
1448
		
1449
			INonViralNameCacheStrategy cacheStrategy = getCacheStrategy(taxonName);
1450
			
1451
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1452
			NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1453
			return cacheStrategy.getFullTitleCache(nvn, tagRules);
1454
		}
1455
	}
1456

    
1457
	
1458
	/**
1459
	 * Returns the <code>NameStatusFk</code> attribute.
1460
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1461
	 * @return The <code>NameStatusFk</code> attribute.
1462
	 * @see MethodMapper
1463
	 */
1464
	@SuppressWarnings("unused")
1465
	private static Integer getNameStatusFk(TaxonNameBase<?,?> taxonName) {
1466
		Integer result = null;
1467

    
1468
		NomenclaturalStatus state = getNameStatus(taxonName);
1469
		if (state != null) {
1470
			result = PesiTransformer.nomStatus2nomStatusFk(state.getType());
1471
		}
1472
		return result;
1473
	}
1474
	
1475
	/**
1476
	 * Returns the <code>NameStatusCache</code> attribute.
1477
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1478
	 * @return The <code>NameStatusCache</code> attribute.
1479
	 * @throws UndefinedTransformerMethodException 
1480
	 * @see MethodMapper
1481
	 */
1482
	@SuppressWarnings("unused")
1483
	private static String getNameStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1484
		String result = null;
1485
		NomenclaturalStatus status = getNameStatus(taxonName);
1486
		if (status != null) {
1487
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1488
		}
1489
		return result;
1490
	}
1491
	
1492
	
1493
	private static NomenclaturalStatus getNameStatus(TaxonNameBase<?,?> taxonName) {
1494
		
1495
		try {
1496
			if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1497
				NonViralName<?> nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1498
				Set<NomenclaturalStatus> states = nonViralName.getStatus();
1499
				if (states.size() == 1) {
1500
					NomenclaturalStatus status = states.iterator().next();
1501
					return status;
1502
				} else if (states.size() > 1) {
1503
					logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1504
				}
1505
			}
1506
		
1507
		} catch (Exception e) {
1508
			e.printStackTrace();
1509
		}
1510
		return null;
1511
	}
1512
	/**
1513
	 * Returns the <code>TaxonStatusFk</code> attribute.
1514
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1515
	 * @param state The {@link PesiExportState PesiExportState}.
1516
	 * @return The <code>TaxonStatusFk</code> attribute.
1517
	 * @see MethodMapper
1518
	 */
1519
	@SuppressWarnings("unused")
1520
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1521
		Integer result = null;
1522
		
1523
		try {
1524
			if (isMisappliedName(taxon)) {
1525
				Synonym synonym = Synonym.NewInstance(null, null);
1526
				
1527
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1528
				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1529
			} else {
1530
				result = PesiTransformer.taxonBase2statusFk(taxon);
1531
			}
1532
		
1533
		} catch (Exception e) {
1534
			e.printStackTrace();
1535
		}
1536
		return result;
1537
	}
1538
	
1539
	/**
1540
	 * Returns the <code>TaxonStatusCache</code> attribute.
1541
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1542
	 * @param state The {@link PesiExportState PesiExportState}.
1543
	 * @return The <code>TaxonStatusCache</code> attribute.
1544
	 * @see MethodMapper
1545
	 */
1546
	@SuppressWarnings("unused")
1547
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) {
1548
		String result = null;
1549
		
1550
		try {
1551
			if (isMisappliedName(taxon)) {
1552
				Synonym synonym = Synonym.NewInstance(null, null);
1553
				
1554
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1555
				result = PesiTransformer.taxonBase2statusCache(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1556
			} else {
1557
				result = PesiTransformer.taxonBase2statusCache(taxon);
1558
			}
1559
		
1560
		} catch (Exception e) {
1561
			e.printStackTrace();
1562
		}
1563
		return result;
1564
	}
1565
	
1566
	/**
1567
	 * Returns the <code>TypeNameFk</code> attribute.
1568
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1569
	 * @param state The {@link PesiExportState PesiExportState}.
1570
	 * @return The <code>TypeNameFk</code> attribute.
1571
	 * @see MethodMapper
1572
	 */
1573
	private static Integer getTypeNameFk(TaxonNameBase<?,?> taxonNameBase, PesiExportState state) {
1574
		Integer result = null;
1575
		if (taxonNameBase != null) {
1576
			Set<NameTypeDesignation> nameTypeDesignations = taxonNameBase.getNameTypeDesignations();
1577
			if (nameTypeDesignations.size() == 1) {
1578
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1579
				if (nameTypeDesignation != null) {
1580
					TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1581
					if (typeName != null) {
1582
						result = state.getDbId(typeName);
1583
					}
1584
				}
1585
			} else if (nameTypeDesignations.size() > 1) {
1586
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonNameBase.getUuid() + " (" + taxonNameBase.getTitleCache() + ")");
1587
			}
1588
		}
1589
		return result;
1590
	}
1591
	
1592
	/**
1593
	 * Returns the <code>TypeFullnameCache</code> attribute.
1594
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1595
	 * @return The <code>TypeFullnameCache</code> attribute.
1596
	 * @see MethodMapper
1597
	 */
1598
	@SuppressWarnings("unused")
1599
	private static String getTypeFullnameCache(TaxonNameBase<?,?> taxonName) {
1600
		String result = null;
1601
		
1602
		try {
1603
		if (taxonName != null) {
1604
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1605
			if (nameTypeDesignations.size() == 1) {
1606
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1607
				if (nameTypeDesignation != null) {
1608
					TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1609
					if (typeName != null) {
1610
						result = typeName.getTitleCache();
1611
					}
1612
				}
1613
			} else if (nameTypeDesignations.size() > 1) {
1614
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1615
			}
1616
		}
1617
		
1618
		} catch (Exception e) {
1619
			e.printStackTrace();
1620
		}
1621
		return result;
1622
	}
1623

    
1624
	
1625
	/**
1626
	 * Returns the <code>QualityStatusFk</code> attribute.
1627
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1628
	 * @return The <code>QualityStatusFk</code> attribute.
1629
	 * @see MethodMapper
1630
	 */
1631
	private static Integer getQualityStatusFk(TaxonNameBase taxonName) {
1632
		BitSet sources = getSources(taxonName);
1633
		return PesiTransformer.getQualityStatusKeyBySource(sources);
1634
	}
1635

    
1636
	
1637
	/**
1638
	 * Returns the <code>QualityStatusCache</code> attribute.
1639
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1640
	 * @return The <code>QualityStatusCache</code> attribute.
1641
	 * @throws UndefinedTransformerMethodException 
1642
	 * @see MethodMapper
1643
	 */
1644
	@SuppressWarnings("unused")
1645
	private static String getQualityStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1646
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1647
	}
1648
	
1649
	/**
1650
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1651
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1652
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1653
	 * @see MethodMapper
1654
	 */
1655
	@SuppressWarnings("unused")
1656
	private static Integer getTypeDesignationStatusFk(TaxonNameBase<?,?> taxonName) {
1657
		Integer result = null;
1658
		
1659
		try {
1660
		if (taxonName != null) {
1661
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1662
			if (typeDesignations.size() == 1) {
1663
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1664
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1665
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1666
			} else if (typeDesignations.size() > 1) {
1667
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1668
			}
1669
		}
1670
		
1671
		} catch (Exception e) {
1672
			e.printStackTrace();
1673
		}
1674
		return result;
1675
	}
1676

    
1677
	/**
1678
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1679
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1680
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1681
	 * @see MethodMapper
1682
	 */
1683
	@SuppressWarnings("unused")
1684
	private static String getTypeDesignationStatusCache(TaxonNameBase<?,?> taxonName) {
1685
		String result = null;
1686
		
1687
		try {
1688
		if (taxonName != null) {
1689
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1690
			if (typeDesignations.size() == 1) {
1691
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1692
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1693
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
1694
			} else if (typeDesignations.size() > 1) {
1695
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1696
			}
1697
		}
1698
		
1699
		} catch (Exception e) {
1700
			e.printStackTrace();
1701
		}
1702
		return result;
1703
	}
1704
	
1705
	/**
1706
	 * Returns the <code>FossilStatusFk</code> attribute.
1707
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1708
	 * @return The <code>FossilStatusFk</code> attribute.
1709
	 * @see MethodMapper
1710
	 */
1711
	@SuppressWarnings("unused")
1712
	private static Integer getFossilStatusFk(TaxonNameBase<?,?> taxonNameBase) {
1713
		Integer result = null;
1714
//		Taxon taxon;
1715
//		if (taxonBase.isInstanceOf(Taxon.class)) {
1716
//			taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1717
//			Set<TaxonDescription> specimenDescription = taxon.;
1718
//			result = PesiTransformer.fossil2FossilStatusId(fossil);
1719
//		}
1720
		return result;
1721
	}
1722
	
1723
	/**
1724
	 * Returns the <code>FossilStatusCache</code> attribute.
1725
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1726
	 * @return The <code>FossilStatusCache</code> attribute.
1727
	 * @see MethodMapper
1728
	 */
1729
	@SuppressWarnings("unused")
1730
	private static String getFossilStatusCache(TaxonNameBase<?,?> taxonName) {
1731
		// TODO
1732
		String result = null;
1733
		return result;
1734
	}
1735
	
1736
	/**
1737
	 * Returns the <code>IdInSource</code> attribute.
1738
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1739
	 * @return The <code>IdInSource</code> attribute.
1740
	 * @see MethodMapper
1741
	 */
1742
	@SuppressWarnings("unused")
1743
	private static String getIdInSource(IdentifiableEntity taxonName) {
1744
		String result = null;
1745
		
1746
		try {
1747
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
1748
			for (IdentifiableSource source : sources) {
1749
				Reference<?> ref = source.getCitation();
1750
				UUID refUuid = ref.getUuid();
1751
				if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed)){
1752
					result = "NameId: " + source.getIdInSource();
1753
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
1754
					result = "TAX_ID: " + source.getIdInSource();
1755
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
1756
					result = "tu_id: " + source.getIdInSource();
1757
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //INdex Fungorum
1758
					result = "if_id: " + source.getIdInSource();
1759
				}else{
1760
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
1761
				}
1762
				
1763
				String sourceIdNameSpace = source.getIdNamespace();
1764
				if (sourceIdNameSpace != null) {
1765
					if (sourceIdNameSpace.equals("originalGenusId")) {
1766
						result = "Nominal Taxon from TAX_ID: " + source.getIdInSource();
1767
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
1768
						result = "Inferred epithet from TAX_ID: " + source.getIdInSource();
1769
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
1770
						result = "Inferred genus from TAX_ID: " + source.getIdInSource();
1771
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
1772
						result = "Potential combination from TAX_ID: " + source.getIdInSource();
1773
					} 
1774
				}
1775
			}
1776
		} catch (Exception e) {
1777
			e.printStackTrace();
1778
		}
1779

    
1780
		if (result == null) {
1781
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1782
		}
1783
		return result;
1784
	}
1785
	
1786
	/**
1787
	 * Returns the idInSource for a given TaxonName only.
1788
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1789
	 * @return The idInSource.
1790
	 */
1791
	private static String getIdInSourceOnly(IdentifiableEntity identEntity) {
1792
		String result = null;
1793
		
1794
		// Get the sources first
1795
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
1796

    
1797
		// Determine the idInSource
1798
		if (sources.size() == 1) {
1799
			IdentifiableSource source = sources.iterator().next();
1800
			if (source != null) {
1801
				result = source.getIdInSource();
1802
			}
1803
		} else if (sources.size() > 1) {
1804
			int count = 1;
1805
			result = "";
1806
			for (IdentifiableSource source : sources) {
1807
				result += source.getIdInSource();
1808
				if (count < sources.size()) {
1809
					result += "; ";
1810
				}
1811
				count++;
1812
			}
1813

    
1814
		}
1815
		
1816
		return result;
1817
	}
1818
	
1819
	/**
1820
	 * Returns the Sources for a given TaxonName only.
1821
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1822
	 * @return The Sources.
1823
	 */
1824
	private static Set<IdentifiableSource> getPesiSources(IdentifiableEntity identEntity) {
1825
		Set<IdentifiableSource> sources = new java.util.HashSet<IdentifiableSource>();
1826

    
1827
		//Taxon Names
1828
		if (identEntity.isInstanceOf(TaxonNameBase.class)){
1829
			// Sources from TaxonName
1830
			TaxonNameBase taxonName = CdmBase.deproxy(identEntity, TaxonNameBase.class);
1831
			sources = filterPesiSources(identEntity.getSources());
1832
			if (sources.size() > 1) {
1833
				logger.warn("This TaxonName has more than one Source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() + ")");
1834
			}
1835
			
1836
			// name has no PESI source, take sources from TaxonBase
1837
			if (sources == null || sources.isEmpty()) {
1838
				Set<TaxonBase> taxa = taxonName.getTaxonBases();
1839
				for (TaxonBase taxonBase: taxa){
1840
					sources.addAll(filterPesiSources(taxonBase.getSources()));
1841
				}
1842
			}
1843

    
1844
		//for TaxonBases
1845
		}else if (identEntity.isInstanceOf(TaxonBase.class)){
1846
			sources = filterPesiSources(identEntity.getSources());	
1847
		}
1848

    
1849
		
1850
		if (sources == null || sources.isEmpty()) {
1851
			logger.warn("This TaxonName has no PESI Sources: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
1852
		}else if (sources.size() > 1){
1853
			logger.warn("This Taxon(Name) has more than 1 PESI source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
1854
		}
1855
		return sources;
1856
	}
1857
	
1858
	// return all sources with a PESI reference	
1859
	private static Set<IdentifiableSource> filterPesiSources(Set<? extends IdentifiableSource> sources) {
1860
		Set<IdentifiableSource> result = new HashSet<IdentifiableSource>();
1861
		for (IdentifiableSource source : sources){
1862
			Reference ref = source.getCitation();
1863
			UUID refUuid = ref.getUuid();
1864
			if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed) || 
1865
				refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)||
1866
				refUuid.equals(PesiTransformer.uuidSourceRefErms)||
1867
				refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum) ){
1868
				result.add(source);
1869
			}
1870
		}
1871
		return result;
1872
	}
1873

    
1874
	/**
1875
	 * Returns the <code>GUID</code> attribute.
1876
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1877
	 * @return The <code>GUID</code> attribute.
1878
	 * @see MethodMapper
1879
	 */
1880
	private static String getGUID(TaxonBase<?> taxon) {
1881
		if (taxon.getLsid() != null ){
1882
			return taxon.getLsid().getLsid();
1883
		}else{
1884
			return taxon.getUuid().toString();
1885
		}
1886
	}
1887
	
1888
	/**
1889
	 * Returns the <code>DerivedFromGuid</code> attribute.
1890
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1891
	 * @return The <code>DerivedFromGuid</code> attribute.
1892
	 * @see MethodMapper
1893
	 */
1894
	@SuppressWarnings("unused")
1895
	private static String getDerivedFromGuid(TaxonBase<?> taxon) {
1896
		String result = null;
1897
		try {
1898
		// The same as GUID for now
1899
		result = getGUID(taxon);
1900
		} catch (Exception e) {
1901
			e.printStackTrace();
1902
		}
1903
		return result;
1904
	}
1905
	
1906
	/**
1907
	 * Returns the <code>CacheCitation</code> attribute.
1908
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1909
	 * @return The CacheCitation.
1910
	 * @see MethodMapper
1911
	 */
1912
	@SuppressWarnings("unused")
1913
	private static String getCacheCitation(TaxonBase taxon) {
1914
		// !!! See also doPhaseUpdates
1915
		
1916
		TaxonNameBase<?,?> taxonName = taxon.getName();
1917
		String result = "";
1918
		//TODO implement anew for taxa
1919
		try {
1920
			BitSet sources = getSources(taxonName);
1921
			if (sources.isEmpty()) {
1922
//				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1923
			} else if (sources.get(PesiTransformer.SOURCE_ERMS)) {
1924
				// 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...
1925
				// 		 So the following code is some kind of harmless assumption.
1926
				Set<Extension> extensions = taxonName.getExtensions();
1927
				for (Extension extension : extensions) {
1928
					if (extension.getType().equals(cacheCitationExtensionType)) {
1929
						result = extension.getValue();
1930
					}
1931
				}
1932
			} else {
1933
				String expertName = getExpertName(taxon);
1934
				String webShowName = getWebShowName(taxonName);
1935
				
1936
				// idInSource only
1937
				String idInSource = getIdInSourceOnly(taxonName);
1938
				
1939
				// build the cacheCitation
1940
				if (expertName != null) {
1941
					result += expertName + ". ";
1942
				} else {
1943
					if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
1944
				}
1945
				if (webShowName != null) {
1946
					result += webShowName + ". ";
1947
				} else {
1948
					logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1949
				}
1950
				
1951
				if (getOriginalDB(taxonName).equals("FaEu")) {
1952
					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
1953
				} else if (getOriginalDB(taxonName).equals("EM")) {
1954
					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
1955
				}
1956
				
1957
				if (idInSource != null) {
1958
					result += idInSource;
1959
				} else {
1960
					logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1961
				}
1962
			}
1963
		} catch (Exception e) {
1964
			e.printStackTrace();
1965
		}
1966
		
1967
		if (StringUtils.isBlank(result)) {
1968
			return null;
1969
		} else {
1970
			return result;
1971
		}
1972
	}
1973
	
1974
	/**
1975
	 * Returns the <code>OriginalDB</code> attribute.
1976
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1977
	 * @return The <code>OriginalDB</code> attribute.
1978
	 * @see MethodMapper
1979
	 */
1980
	private static String getOriginalDB(IdentifiableEntity identEntity) {
1981
		// Sources from TaxonName
1982
		BitSet sources  = getSources(identEntity);
1983
		return PesiTransformer.getOriginalDbBySources(sources);
1984
	}
1985
	
1986
	/**
1987
	 * Returns the <code>LastAction</code> attribute.
1988
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1989
	 * @return The <code>LastAction</code> attribute.
1990
	 * @see MethodMapper
1991
	 */
1992
	@SuppressWarnings("unused")
1993
	private static String getLastAction(IdentifiableEntity<?> identEntity) {
1994
		String result = null;
1995
		try {
1996
		Set<Extension> extensions = identEntity.getExtensions();
1997
		for (Extension extension : extensions) {
1998
			if (extension.getType().equals(lastActionExtensionType)) {
1999
				result = extension.getValue();
2000
			}
2001
		}
2002
		} catch (Exception e) {
2003
			e.printStackTrace();
2004
		}
2005
		return result;
2006
	}
2007
	
2008
	/**
2009
	 * Returns the <code>LastActionDate</code> attribute.
2010
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2011
	 * @return The <code>LastActionDate</code> attribute.
2012
	 * @see MethodMapper
2013
	 */
2014
	@SuppressWarnings({ "unused" })
2015
	private static DateTime getLastActionDate(IdentifiableEntity identEntity) {
2016
		DateTime result = null;
2017
		try {
2018
			Set<Extension> extensions = identEntity.getExtensions();
2019
			for (Extension extension : extensions) {
2020
				if (extension.getType().equals(lastActionDateExtensionType)) {
2021
					String dateTime = extension.getValue();
2022
					if (dateTime != null) {
2023
						DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
2024
						result = formatter.parseDateTime(dateTime);
2025
					}
2026
				}
2027
			}
2028
		} catch (Exception e) {
2029
			e.printStackTrace();
2030
		}
2031
		return result;
2032
	}
2033
	
2034
	/**
2035
	 * Returns the <code>ExpertName</code> attribute.
2036
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2037
	 * @return The <code>ExpertName</code> attribute.
2038
	 * @see MethodMapper
2039
	 */
2040
	@SuppressWarnings("unused")
2041
	private static String getExpertName(TaxonBase<?> taxonName) {
2042
		String result = null;
2043
		try {
2044
		Set<Extension> extensions = taxonName.getExtensions();
2045
		for (Extension extension : extensions) {
2046
			if (extension.getType().equals(expertNameExtensionType)) {
2047
				result = extension.getValue();
2048
			}
2049
		}
2050
		} catch (Exception e) {
2051
			e.printStackTrace();
2052
		}
2053
		return result;
2054
	}
2055
	
2056
	/**
2057
	 * Returns the <code>ExpertFk</code> attribute.
2058
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2059
	 * @param state The {@link PesiExportState PesiExportState}.
2060
	 * @return The <code>ExpertFk</code> attribute.
2061
	 * @see MethodMapper
2062
	 */
2063
	private static Integer getExpertFk(Reference<?> reference, PesiExportState state) {
2064
		Integer result = state.getDbId(reference);
2065
		return result;
2066
	}
2067
	
2068
	/**
2069
	 * Returns the <code>SpeciesExpertName</code> attribute.
2070
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2071
	 * @return The <code>SpeciesExpertName</code> attribute.
2072
	 * @see MethodMapper
2073
	 */
2074
	@SuppressWarnings("unused")
2075
	private static String getSpeciesExpertName(TaxonBase<?> taxonName) {
2076
		String result = null;
2077
		try {
2078
		Set<Extension> extensions = taxonName.getExtensions();
2079
		for (Extension extension : extensions) {
2080
			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2081
				result = extension.getValue();
2082
			}
2083
		}
2084
		} catch (Exception e) {
2085
			e.printStackTrace();
2086
		}
2087
		return result;
2088
	}
2089
	
2090
	/**
2091
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2092
	 * @param reference The {@link Reference Reference}.
2093
	 * @param state The {@link PesiExportState PesiExportState}.
2094
	 * @return The <code>SpeciesExpertFk</code> attribute.
2095
	 * @see MethodMapper
2096
	 */
2097
	private static Integer getSpeciesExpertFk(Reference<?> reference, PesiExportState state) {
2098
		Integer result = state.getDbId(reference);
2099
		return result;
2100
	}
2101
	
2102
	
2103
	/**
2104
	 * Returns the source (E+M, Fauna Europaea, Index Fungorum, ERMS) of a given
2105
	 * Identifiable Entity as a BitSet
2106
	 * @param identEntity
2107
	 * @return
2108
	 */
2109
	private static BitSet getSources(IdentifiableEntity<?> identEntity){
2110
		BitSet bitSet = new BitSet();
2111
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2112
		for (IdentifiableSource source : sources) {
2113
			Reference<?> ref = source.getCitation();
2114
			UUID refUuid = ref.getUuid();
2115
			if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed)){
2116
				bitSet.set(PesiTransformer.SOURCE_EM);
2117
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2118
				bitSet.set(PesiTransformer.SOURCE_FE);
2119
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2120
				bitSet.set(PesiTransformer.SOURCE_IF);
2121
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //INdex Fungorum
2122
				bitSet.set(PesiTransformer.SOURCE_ERMS);
2123
			}else{
2124
				if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2125
			}
2126
		}
2127
		return bitSet;
2128
		
2129
	}
2130
	
2131
	private static NonViralNameDefaultCacheStrategy getCacheStrategy(TaxonNameBase<?, ?> taxonName) {
2132
		NonViralNameDefaultCacheStrategy cacheStrategy;
2133
		if (taxonName.isInstanceOf(ZoologicalName.class)){
2134
			cacheStrategy = zooNameStrategy;
2135
		}else if (taxonName.isInstanceOf(BotanicalName.class)) {
2136
			cacheStrategy = botanicalNameStrategy;
2137
		}else{
2138
			logger.error("Unhandled taxon name type. Can't define strategy class");
2139
			cacheStrategy = botanicalNameStrategy;
2140
		}
2141
		return cacheStrategy;
2142
	}
2143

    
2144
//	/**
2145
//	 * Returns the <code>SourceFk</code> attribute.
2146
//	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2147
//	 * @param state The {@link PesiExportState PesiExportState}.
2148
//	 * @return The <code>SourceFk</code> attribute.
2149
//	 */
2150
//	@SuppressWarnings("unused")
2151
//	private static Integer getSourceFk(TaxonNameBase<?,?> taxonName, PesiExportState state) {
2152
//		Integer result = null;
2153
//		
2154
//		try {
2155
//			TaxonBase<?> taxonBase = getSourceTaxonBase(taxonName);
2156
//	
2157
//			if (taxonBase != null) {
2158
//				result = state.getDbId(taxonBase.getSec());
2159
//			}
2160
//		} catch (Exception e) {
2161
//			e.printStackTrace();
2162
//		}
2163
//		return result;
2164
//	}
2165

    
2166

    
2167
//	/**
2168
//	 * Determines the TaxonBase of a TaxonName.
2169
//	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2170
//	 * @return The TaxonBase.
2171
//	 */
2172
//	private static TaxonBase<?> getSourceTaxonBase(TaxonNameBase<?,?> taxonName) {
2173
//		TaxonBase<?> taxonBase = null;
2174
//		Set<Taxon> taxa = taxonName.getTaxa();
2175
//		if (taxa.size() == 1) {
2176
//			taxonBase =taxa.iterator().next();
2177
//		} else if (taxa.size() > 1) {
2178
//			logger.warn("This TaxonName has " + taxa.size() + " Taxa: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2179
//		}
2180
//		
2181
//		Set<Synonym> synonyms  = taxonName.getSynonyms();
2182
//		if (synonyms.size() == 1) {
2183
//			taxonBase = synonyms.iterator().next();
2184
//		} else if (synonyms.size() > 1) {
2185
//			logger.warn("This TaxonName has " + synonyms.size() + " Synonyms: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2186
//		}
2187
//		return taxonBase;
2188
//	}
2189
	
2190
	/**
2191
	 * Returns the CDM to PESI specific export mappings.
2192
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2193
	 */
2194
	private PesiExportMapping getMapping() {
2195
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2196
		ExtensionType extensionType = null;
2197
		
2198
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2199
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2200
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2201
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2202
		
2203
		// QualityStatus (Fk, Cache)
2204
//		extensionType = (ExtensionType)getTermService().find(ErmsTransformer.uuidQualityStatus);
2205
//		if (extensionType != null) {
2206
//			mapping.addMapper(DbExtensionMapper.NewInstance(extensionType, "QualityStatusCache"));
2207
//		} else {
2208
//			mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this));
2209
//		}
2210
//		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this)); // PesiTransformer.QualityStatusCache2QualityStatusFk?
2211

    
2212
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2213
		
2214
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2215
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2216
		
2217
		//handled by name mapping
2218
//		mapping.addMapper(MethodMapper.NewInstance("LastAction", this.getClass(), "getLastAction",  IdentifiableEntity.class));
2219
//		mapping.addMapper(MethodMapper.NewInstance("LastActionDate", this.getClass(), "getLastActionDate",  IdentifiableEntity.class));
2220
//		mapping.addMapper(MethodMapper.NewInstance("SpeciesExpertName", this));
2221

    
2222
		
2223
		mapping.addMapper(MethodMapper.NewInstance("ExpertName", this));
2224
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misallied Names are handled differently
2225
		
2226
		
2227
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonNameBase.class, "Name"));
2228
		
2229
		addNameMappers(mapping);
2230

    
2231
		return mapping;
2232
	}
2233
	
2234
	/**
2235
	 * Returns the CDM to PESI specific export mappings.
2236
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2237
	 */
2238
	private PesiExportMapping getPureNameMapping() {
2239
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2240
		
2241
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2242

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

    
2245
//		mapping.addMapper(MethodMapper.NewInstance("LastAction", this.getClass(), "getLastAction", IdentifiableEntity.class));
2246
//		mapping.addMapper(MethodMapper.NewInstance("LastActionDate",  this.getClass(), "getLastAction", IdentifiableEntity.class));
2247
		
2248
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonNameBase.class));
2249
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonNameBase.class));
2250
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonNameBase.class));
2251
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
2252
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , PesiTransformer.T_STATUS_STR_UNACCEPTED));
2253
		
2254
		addNameMappers(mapping);
2255
		//TODO add author mapper, TypeNameFk
2256

    
2257
		return mapping;
2258
	}
2259

    
2260
	private void addNameMappers(PesiExportMapping mapping) {
2261
		ExtensionType extensionType;
2262
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2263
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2264
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2265
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2266
		
2267
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2268
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonNameBase.class));
2269
		
2270
//		mapping.addMapper(DbStringMapper.NewInstance("TitleCache", "FullName"));    //does not work as we need other cache strategy
2271
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonNameBase.class));
2272
		
2273
		
2274
		//TODO FIXME incorrect mapping -> should be ref +  microref but is only microref
2275
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "NomRefString"));
2276
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonNameBase.class));
2277
		
2278
		// DisplayName
2279
		extensionType = (ExtensionType)getTermService().find(ErmsTransformer.uuidDisplayName);		
2280
		if (extensionType != null) {
2281
			mapping.addMapper(DbExtensionMapper.NewInstance(extensionType, "DisplayName"));
2282
		} else {
2283
			mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonNameBase.class));
2284
		}
2285

    
2286
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonNameBase.class));
2287
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2288
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonNameBase.class));
2289
		//TODO TypeNameFk
2290
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonNameBase.class));
2291
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2292
		
2293
		
2294
		// FossilStatus (Fk, Cache)
2295
		extensionType = (ExtensionType)getTermService().find(ErmsTransformer.uuidFossilStatus);
2296
		if (extensionType != null) {
2297
			mapping.addMapper(DbExtensionMapper.NewInstance(extensionType, "FossilStatusCache"));
2298
		} else {
2299
			mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, TaxonNameBase.class));
2300
		}
2301
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, TaxonNameBase.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2302
		
2303
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2304
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2305

    
2306
		mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2307

    
2308
	}
2309

    
2310
}
(16-16/17)