Project

General

Profile

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

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

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

    
34
import eu.etaxonomy.cdm.api.service.TaxonServiceImpl;
35
import eu.etaxonomy.cdm.common.CdmUtils;
36
import eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer;
37
import eu.etaxonomy.cdm.io.common.Source;
38
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
39
import eu.etaxonomy.cdm.io.common.mapping.out.DbConstantMapper;
40
import eu.etaxonomy.cdm.io.common.mapping.out.DbExtensionMapper;
41
import eu.etaxonomy.cdm.io.common.mapping.out.DbLastActionMapper;
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.common.Marker;
57
import eu.etaxonomy.cdm.model.common.MarkerType;
58
import eu.etaxonomy.cdm.model.common.RelationshipBase;
59
import eu.etaxonomy.cdm.model.name.BacterialName;
60
import eu.etaxonomy.cdm.model.name.BotanicalName;
61
import eu.etaxonomy.cdm.model.name.HybridRelationship;
62
import eu.etaxonomy.cdm.model.name.NameRelationship;
63
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
64
import eu.etaxonomy.cdm.model.name.NameTypeDesignationStatus;
65
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
66
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
67
import eu.etaxonomy.cdm.model.name.NonViralName;
68
import eu.etaxonomy.cdm.model.name.Rank;
69
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
70
import eu.etaxonomy.cdm.model.name.ZoologicalName;
71
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
72
import eu.etaxonomy.cdm.model.reference.Reference;
73
import eu.etaxonomy.cdm.model.taxon.Classification;
74
import eu.etaxonomy.cdm.model.taxon.Synonym;
75
import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
76
import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
77
import eu.etaxonomy.cdm.model.taxon.Taxon;
78
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
79
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
80
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
81
import eu.etaxonomy.cdm.profiler.ProfilerController;
82
import eu.etaxonomy.cdm.strategy.cache.HTMLTagRules;
83
import eu.etaxonomy.cdm.strategy.cache.TagEnum;
84
import eu.etaxonomy.cdm.strategy.cache.name.BacterialNameDefaultCacheStrategy;
85
import eu.etaxonomy.cdm.strategy.cache.name.BotanicNameDefaultCacheStrategy;
86
import eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy;
87
import eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy;
88
import eu.etaxonomy.cdm.strategy.cache.name.ZooNameNoMarkerCacheStrategy;
89

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

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

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

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

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

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

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

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

    
215
			//"PHASE 1b: Handle names without taxa ...
216
			success &= doNames(state, additionalSourceMapping);
217

    
218
			
219
			// 2nd Round: Add ParentTaxonFk to each taxon
220
			success &= doPhase02(state);
221
			
222
			//PHASE 3: Add Rank data, KingdomFk, TypeNameFk ...
223
			success &= doPhase03(state);
224
			
225
			// 4nd Round: Add TreeIndex to each taxon
226
			success &= doPhase04(state);
227
						
228
			
229
			//"PHASE 4: Creating Inferred Synonyms...
230
			success &= doPhase05(state, mapping, synonymRelMapping);
231
			
232
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
233

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

    
245
	
246
	private void initPreparedStatements(PesiExportState state) throws SQLException {
247
		initTreeIndexStatement(state);
248
		initRankExpertsUpdateStmt(state);
249
		initRankUpdateStatement(state);
250
		
251
		initParentFkStatement(state);
252
	}
253

    
254
	// Prepare TreeIndex-And-KingdomFk-Statement
255
	private void initTreeIndexStatement(PesiExportState state) throws SQLException {
256
		Connection connection = state.getConfig().getDestination().getConnection();
257
		String parentTaxonFk_TreeIndex_KingdomFkSql = "UPDATE Taxon SET ParentTaxonFk = ?, TreeIndex = ? WHERE TaxonId = ?"; 
258
		parentTaxonFk_TreeIndex_KingdomFkStmt = connection.prepareStatement(parentTaxonFk_TreeIndex_KingdomFkSql);
259
	}
260

    
261
	// Prepare TreeIndex-And-KingdomFk-Statement
262
	private void initParentFkStatement(PesiExportState state) throws SQLException {
263
		Connection connection = state.getConfig().getDestination().getConnection();
264
		String parentTaxonFkSql = "UPDATE Taxon SET ParentTaxonFk = ? WHERE TaxonId = ?"; 
265
		parentTaxonFkStmt = connection.prepareStatement(parentTaxonFkSql);
266
	}
267
	
268
	private void initRankUpdateStatement(PesiExportState state) throws SQLException {
269
		Connection connection = state.getConfig().getDestination().getConnection();
270
		String rankSql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, KingdomFk = ? WHERE TaxonId = ?";
271
		rankUpdateStmt = connection.prepareStatement(rankSql);
272
	}
273

    
274
	private void initRankExpertsUpdateStmt(PesiExportState state) throws SQLException {
275
//		String sql_old = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ?, " +
276
//				"ExpertFk = ?, SpeciesExpertFk = ? WHERE TaxonId = ?";
277
		//TODO handle experts GUIDs
278
		Connection connection = state.getConfig().getDestination().getConnection();
279
		
280
		String sql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ? " +
281
				" WHERE TaxonId = ?";
282
		rankTypeExpertsUpdateStmt = connection.prepareStatement(sql);
283
	}
284

    
285
	private boolean doPhase01(PesiExportState state, PesiExportMapping mapping, PesiExportMapping additionalSourceMapping) throws SQLException {
286
		int count = 0;
287
		int pastCount = 0;
288
		List<TaxonBase> list;
289
		boolean success = true;
290
		// Get the limit for objects to save within a single transaction.
291
		int limit = state.getConfig().getLimitSave();
292

    
293
		
294
		logger.info("PHASE 1: Export Taxa...limit is " + limit);
295
		// Start transaction
296
		TransactionStatus txStatus = startTransaction(true);
297
		logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
298
		
299
		
300
		
301
		int partitionCount = 0;
302

    
303
		logger.info("Taking snapshot at the beginning of phase 1 of taxonExport");
304
		ProfilerController.memorySnapshot();
305
		while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)) != null   ) {
306
			
307
			logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
308
			for (TaxonBase<?> taxon : list) {
309
				doCount(count++, modCount, pluralString);
310
				TaxonNameBase<?,?> taxonName = taxon.getName();
311
				NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
312
								
313
				if (! nvn.isProtectedTitleCache()){
314
					nvn.setTitleCache(null, false);	
315
				}
316
				if (! nvn.isProtectedNameCache()){
317
					nvn.setNameCache(null, false);	
318
				}
319
				if (! nvn.isProtectedFullTitleCache()){
320
					nvn.setFullTitleCache(null, false);	
321
				}
322
				if (! nvn.isProtectedAuthorshipCache()){
323
					nvn.setAuthorshipCache(null, false);	
324
				}
325
				
326
				//core mapping
327
				success &= mapping.invoke(taxon);
328
				//additional source
329
				if (nvn.getNomenclaturalReference() != null || StringUtils.isNotBlank(nvn.getNomenclaturalMicroReference() )){
330
					additionalSourceMapping.invoke(taxon);
331
				}
332
				
333
				validatePhaseOne(taxon, nvn);
334
				taxon = null;
335
				nvn = null;
336
				taxonName = null;
337

    
338
				
339
				
340
			}
341
			
342

    
343
			// Commit transaction
344
			commitTransaction(txStatus);
345
			logger.debug("Committed transaction.");
346
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
347
			pastCount = count;
348
			/*logger.warn("Taking snapshot at the end of the loop of phase 1 of taxonExport");
349
			ProfilerController.memorySnapshot();
350
			*/
351
			// Start transaction
352
			txStatus = startTransaction(true);
353
			logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
354
			
355
		}
356
		if (list == null ) {
357
			logger.info("No " + pluralString + " left to fetch.");
358
		}
359
		
360
		
361
		
362
		// Commit transaction
363
		commitTransaction(txStatus);
364
		txStatus = null;
365
		logger.debug("Committed transaction.");
366
		list = null;
367
		if (logger.isDebugEnabled()){
368
			logger.debug("Taking snapshot at the end of phase 1 of taxonExport");
369
			ProfilerController.memorySnapshot();
370
		}
371
		return success;
372
	}
373

    
374

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

    
434
	
435
	
436
	/**
437
	 * 2nd Round: Add ParentTaxonFk to each taxon and add Biota if not exists
438
	 * @param state
439
	 * @return
440
	 */
441
	private boolean doPhase02(PesiExportState state) {
442
		int count = 0;
443
		int pastCount = 0;
444
		boolean success = true;
445
		if (! state.getConfig().isDoParentAndBiota()){
446
			logger.info ("Ignore PHASE 2: Make ParentFk and Biota...");
447
			return success;
448
		}
449
		
450
		List<Taxon> list;
451
		
452
		// Get the limit for objects to save within a single transaction.
453
		int limit = state.getConfig().getLimitSave();
454
		
455
		insertBiota(state);
456
		
457
		logger.info("PHASE 2: Make ParentFk and Biota ... limit is " + limit);
458
		// Start transaction
459
		TransactionStatus txStatus = startTransaction(true);
460
		int partitionCount = 0;
461

    
462
//		ProfilerController.memorySnapshot();
463
		while ((list = getNextTaxonPartition(Taxon.class, limit, partitionCount++, null)) != null   ) {
464
			
465
			logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
466
			for (Taxon taxon : list) {
467
				for (TaxonNode node : taxon.getTaxonNodes()){
468
					doCount(count++, modCount, pluralString);
469
					TaxonNode parentNode = node.getParent();
470
					if (parentNode != null && parentNode.getTaxon() != null){  //new root node handling requires has root taxon with taxon == null
471
						int childId = state.getDbId( taxon); 
472
						int parentId = state.getDbId(parentNode.getTaxon());
473
						success &= invokeParentTaxonFk(parentId, childId);
474
					}
475
				}
476
				
477
			}
478
			
479
			// Commit transaction
480
			commitTransaction(txStatus);
481
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
482
			pastCount = count;
483
			// Start transaction
484
			txStatus = startTransaction(true);
485
			logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
486
			
487
		}
488
		if (list == null ) {
489
			logger.info("No " + pluralString + " left to fetch.");
490
		}
491
		
492
		// Commit transaction
493
		commitTransaction(txStatus);
494
		
495
		return success;
496
		
497
	}
498

    
499
	/**
500
	 * Inserts the Biota Taxon if not yet exists.
501
	 * @param state
502
	 * @throws SQLException
503
	 */
504
	private void insertBiota(PesiExportState state) {
505
		try {
506
			ResultSet rs = state.getConfig().getDestination().getResultSet("SELECT * FROM Taxon WHERE GenusOrUninomial = 'Biota' ");
507
			if (rs.next() == false){
508
				int biotaId = state.getConfig().getNameIdStart() -1 ;
509
				String sqlInsertBiota = "INSERT INTO Taxon (TaxonId, KingdomFk, RankFk, RankCache, GenusOrUninomial, WebSearchName, WebShowName, FullName, DisplayName, TaxonStatusFk, TaxonStatusCache) " +
510
									       " VALUES (" + biotaId + ",    0,    0,   'Superdomain',   'Biota',          'Biota',  '<i>Biota</i>',   'Biota', '<i>Biota</i>',  1 ,      'accepted')";
511
				state.getConfig().getDestination().update(sqlInsertBiota);
512
			}
513
		} catch (SQLException e) {
514
			logger.warn ("Biota could not be requested or inserted");
515
		}
516
	}
517
	
518
	// 4th round: Add TreeIndex to each taxon
519
	private boolean doPhase04(PesiExportState state) {
520
		boolean success = true;
521
		
522
		logger.info("PHASE 4: Make TreeIndex ... ");
523
	
524
		//TODO test if possible to move to phase 02 
525
		String sql = " UPDATE Taxon SET ParentTaxonFk = (Select TaxonId from Taxon where RankFk = 0) " +
526
				" WHERE (RankFk = 10) and TaxonStatusFk = 1 ";
527
		state.getConfig().getDestination().update(sql);
528
		
529
		state.getConfig().getDestination().update("EXEC dbo.recalculateallstoredpaths");
530
		
531
		return success;
532
		
533
	}
534
	
535
	
536
	// 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
537
	private boolean doPhase02_OLD(PesiExportState state) {
538
		boolean success = true;
539
		if (! state.getConfig().isDoTreeIndex()){
540
			logger.info ("Ignore PHASE 2: ParentTaxonFk and TreeIndex");
541
			return success;
542
		}
543
		
544
		List<Classification> classificationList = null;
545
		logger.info("PHASE 2: Add ParenTaxonFk and TreeIndex...");
546
		
547
		// Specify starting ranks for tree traversing
548
		rankList.add(Rank.KINGDOM());
549
		rankList.add(Rank.GENUS());
550

    
551
		// Specify where to stop traversing (value) when starting at a specific Rank (key)
552
		rank2endRankMap.put(Rank.GENUS(), null); // Since NULL does not match an existing Rank, traverse all the way down to the leaves
553
		rank2endRankMap.put(Rank.KINGDOM(), Rank.GENUS()); // excludes rank genus
554
		
555
		StringBuffer treeIndex = new StringBuffer();
556
		
557
		// Retrieve list of classifications
558
		TransactionStatus txStatus = startTransaction(true);
559
		logger.info("Started transaction for parentFk and treeIndex. Fetching all classifications...");
560
		classificationList = getClassificationService().listClassifications(null, 0, null, null);
561
		commitTransaction(txStatus);
562
		logger.debug("Committed transaction.");
563

    
564
		logger.info("Fetched " + classificationList.size() + " classification(s).");
565

    
566
		setTreeIndexAnnotationType(getAnnotationType(uuidTreeIndex, "TreeIndex", "TreeIndex", "TI"));
567
		List<TaxonNode> rankSpecificRootNodes;
568
		for (Classification classification : classificationList) {
569
			for (Rank rank : rankList) {
570
				
571
				txStatus = startTransaction(true);
572
				logger.info("Started transaction to fetch all rootNodes specific to Rank " + rank.getLabel() + " ...");
573

    
574
				rankSpecificRootNodes = getClassificationService().loadRankSpecificRootNodes(classification, rank, null, null, null);
575
				logger.info("Fetched " + rankSpecificRootNodes.size() + " RootNodes for Rank " + rank.getLabel());
576

    
577
				commitTransaction(txStatus);
578
				logger.debug("Committed transaction.");
579

    
580
				for (TaxonNode rootNode : rankSpecificRootNodes) {
581
					txStatus = startTransaction(false);
582
					Rank endRank = rank2endRankMap.get(rank);
583
					if (endRank != null) {
584
						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till Rank " + endRank.getLabel() + " ...");
585
					} else {
586
						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till leaves are reached ...");
587
					}
588

    
589
					TaxonNode newNode = getTaxonNodeService().load(rootNode.getUuid());
590

    
591
					if (isPesiTaxon(newNode.getTaxon())){
592
						TaxonNode parentNode = newNode.getParent();
593
						if (rank.equals(Rank.KINGDOM())) {
594
							treeIndex = new StringBuffer();
595
							treeIndex.append("#");
596
						} else {
597
							// Get treeIndex from parentNode
598
							if (parentNode != null) {
599
								boolean annotationFound = false;
600
								Set<Annotation> annotations = parentNode.getAnnotations();
601
								for (Annotation annotation : annotations) {
602
									AnnotationType annotationType = annotation.getAnnotationType();
603
									if (annotationType != null && annotationType.equals(getTreeIndexAnnotationType())) {
604
										treeIndex = new StringBuffer(CdmUtils.Nz(annotation.getText()));
605
										annotationFound = true;
606
	//									logger.error("treeIndex: " + treeIndex);
607
										break;
608
									}
609
								}
610
								if (!annotationFound) {
611
									// This should not happen because it means that the treeIndex was not set correctly as an annotation to parentNode
612
									logger.error("TreeIndex could not be read from annotation of TaxonNode: " + parentNode.getUuid() + ", Taxon: " + parentNode.getTaxon().getUuid());
613
									treeIndex = new StringBuffer();
614
									treeIndex.append("#");
615
								}
616
							} else {
617
								// TreeIndex could not be determined, but it's unclear how to proceed to generate a correct treeIndex if the parentNode is NULL
618
								logger.error("ParentNode for RootNode is NULL. TreeIndex could not be determined: " + newNode.getUuid());
619
								treeIndex = new StringBuffer(); // This just prevents growing of the treeIndex in a wrong manner
620
								treeIndex.append("#");
621
							}
622
						}
623
						nomenclaturalCode = newNode.getTaxon().getName().getNomenclaturalCode();
624
						kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
625
						traverseTree(newNode, parentNode, treeIndex, endRank, state);
626
						parentNode =null;
627
					}else{
628
						logger.debug("Taxon is not a PESI taxon: " + newNode.getTaxon().getUuid());
629
					}
630
					
631
					newNode = null;
632
					
633
					try {
634
						commitTransaction(txStatus);
635
						logger.debug("Committed transaction.");
636
					} catch (Exception e) {
637
						logger.error(e.getMessage());
638
						e.printStackTrace();
639
					}
640

    
641
				}
642
				rankSpecificRootNodes = null;
643
			}
644
			
645
		}
646
		
647
		logger.warn("Taking snapshot at the end of phase 2 of taxonExport");
648
		ProfilerController.memorySnapshot();
649
		return success;
650
	}	
651

    
652
	//PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...
653
	private boolean doPhase03(PesiExportState state) {
654
		int count = 0;
655
		int pastCount = 0;
656
		boolean success = true;
657
		if (! state.getConfig().isDoTreeIndex()){
658
			logger.info ("Ignore PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
659
			return success;
660
		}
661
		// Get the limit for objects to save within a single transaction.
662
		int limit = state.getConfig().getLimitSave();
663

    
664
		List<TaxonBase> list;
665
		logger.info("PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
666
		// Be sure to add rank information, KingdomFk, TypeNameFk, expertFk and speciesExpertFk to every taxonName
667
		
668
		// Start transaction
669
		TransactionStatus txStatus = startTransaction(true);
670
		logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
671
		int partitionCount = 0;
672
		while ((list = getNextTaxonPartition(TaxonBase.class, limit, partitionCount++, null)) != null) {
673

    
674
			logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
675
			for (TaxonBase<?> taxon : list) {
676
				TaxonNameBase<?,?> taxonName = taxon.getName();
677
				// Determine expertFk
678
//				Integer expertFk = makeExpertFk(state, taxonName);
679
//
680
//				// Determine speciesExpertFk
681
//				Integer speciesExpertFk = makeSpeciesExpertFk(state, taxonName);
682

    
683
				doCount(count++, modCount, pluralString);
684
				Integer typeNameFk = getTypeNameFk(taxonName, state);
685
				kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
686
				
687
				//TODO why are expertFks needed? (Andreas M.)
688
//				if (expertFk != null || speciesExpertFk != null) {
689
					invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, nomenclaturalCode, state.getDbId(taxon), 
690
							typeNameFk, kingdomFk, state);
691
//				}
692
					
693
					taxon = null;
694
					taxonName = null;
695
			}
696

    
697
			// Commit transaction
698
			commitTransaction(txStatus);
699
			logger.debug("Committed transaction.");
700
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
701
			pastCount = count;
702

    
703
			// Start transaction
704
			txStatus = startTransaction(true);
705
			logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
706
		}
707
		if (list == null) {
708
			logger.info("No " + pluralString + " left to fetch.");
709
		}
710
		
711
		list = null;
712
		
713
		// Commit transaction
714
		commitTransaction(txStatus);
715
		
716
		logger.debug("Committed transaction.");
717
		logger.debug("Try to take snapshot at the end of phase 3 of taxonExport, number of partitions: " + partitionCount);
718
		ProfilerController.memorySnapshot();
719
		return success;
720
	}
721
	
722
	//	"PHASE 5: Creating Inferred Synonyms..."
723
	private boolean doPhase05(PesiExportState state, PesiExportMapping mapping, PesiExportMapping synRelMapping) throws SQLException {
724
		int count;
725
		int pastCount;
726
		boolean success = true;
727
		// Get the limit for objects to save within a single transaction.
728
		if (! state.getConfig().isDoInferredSynonyms()){
729
			logger.info ("Ignore PHASE 5: Creating Inferred Synonyms...");
730
			return success;
731
		}
732
		
733
		int limit = state.getConfig().getLimitSave();
734
		// Create inferred synonyms for accepted taxa
735
		logger.info("PHASE 4: Creating Inferred Synonyms...");
736

    
737
		// Determine the count of elements in datawarehouse database table Taxon
738
		currentTaxonId = determineTaxonCount(state);
739
		currentTaxonId++;
740

    
741
		count = 0;
742
		pastCount = 0;
743
		int pageSize = limit;
744
		int pageNumber = 1;
745
		String inferredSynonymPluralString = "Inferred Synonyms";
746
		
747
		// Start transaction
748
		TransactionStatus txStatus = startTransaction(true);
749
		logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
750
		List<TaxonBase> taxonList = null;
751
		
752
		
753
		
754
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", Rank.SPECIES(), pageSize, pageNumber)).size() > 0) {
755
			HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
756

    
757
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
758
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
759
					synRelMapping, taxonList));
760
			
761
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
762
			// Commit transaction
763
			commitTransaction(txStatus);
764
			logger.debug("Committed transaction.");
765
			logger.info("Exported " + (taxonList.size()) + " " + inferredSynonymPluralString + ". Total: " + count);
766
			//pastCount = count;
767
			
768
			// Save Rank Data and KingdomFk for inferred synonyms
769
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
770
				invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomenclaturalCode, taxonFk, kingdomFk, state);
771
			}
772

    
773
			// Start transaction
774
			txStatus = startTransaction(true);
775
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
776
			
777
			// Increment pageNumber
778
			pageNumber++;
779
		}
780
		taxonList = null;
781
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", Rank.SUBSPECIES(), pageSize, pageNumber)).size() > 0) {
782
			HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
783

    
784
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
785
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
786
					synRelMapping, taxonList));
787
			;
788
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
789
			// Commit transaction
790
			commitTransaction(txStatus);
791
			logger.debug("Committed transaction.");
792
			logger.info("Exported " + taxonList.size()+ " " + inferredSynonymPluralString + ". Total: " + count);
793
			//pastCount = count;
794
			
795
			// Save Rank Data and KingdomFk for inferred synonyms
796
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
797
				invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomenclaturalCode, taxonFk, kingdomFk, state);
798
			}
799

    
800
			// Start transaction
801
			txStatus = startTransaction(true);
802
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
803
			
804
			// Increment pageNumber
805
			pageNumber++;
806
		}
807
		if (taxonList.size() == 0) {
808
			logger.info("No " + parentPluralString + " left to fetch.");
809
		}
810
		
811
		taxonList = null;
812
//		logger.warn("Taking snapshot at the end of phase 4 of taxonExport");
813
//		ProfilerController.memorySnapshot();
814
		
815
		// Commit transaction
816
		commitTransaction(txStatus);
817
		System.gc();
818
		logger.debug("Taking snapshot at the end of phase 4 after gc() of taxonExport");
819
		ProfilerController.memorySnapshot();
820
		logger.debug("Committed transaction.");
821
		return success;
822
	}
823

    
824
	/**
825
	 * @param state
826
	 * @param mapping
827
	 * @param synRelMapping
828
	 * @param currentTaxonId
829
	 * @param taxonList
830
	 * @param inferredSynonymsDataToBeSaved
831
	 * @return
832
	 */
833
	private HashMap<Integer, TaxonNameBase<?, ?>> createInferredSynonymsForTaxonList(PesiExportState state,
834
			PesiExportMapping mapping, PesiExportMapping synRelMapping,	 List<TaxonBase> taxonList) {
835
		
836
		Taxon acceptedTaxon;
837
		Classification classification = null;
838
		List<Synonym> inferredSynonyms = null;
839
		boolean localSuccess = true;
840
		
841
		HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
842
		
843
		for (TaxonBase<?> taxonBase : taxonList) {
844
		
845
			if (taxonBase.isInstanceOf(Taxon.class)) { // this should always be the case since we should have fetched accepted taxon only, but you never know...
846
				acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
847
				TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();
848
				
849
				if (taxonName.isInstanceOf(ZoologicalName.class)) {
850
					nomenclaturalCode  = taxonName.getNomenclaturalCode();
851
					kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
852

    
853
					Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
854
					TaxonNode singleNode = null;
855
					
856
					if (taxonNodes.size() > 0) {
857
						// Determine the classification of the current TaxonNode
858
						
859
						singleNode = taxonNodes.iterator().next();
860
						if (singleNode != null) {
861
							classification = singleNode.getClassification();
862
						} else {
863
							logger.error("A TaxonNode belonging to this accepted Taxon is NULL: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() +")");
864
						}
865
					} else {
866
						// Classification could not be determined directly from this TaxonNode
867
						// The stored classification from another TaxonNode is used. It's a simple, but not a failsafe fallback solution.
868
						if (taxonNodes.size() == 0) {
869
							//logger.error("Classification could not be determined directly from this Taxon: " + acceptedTaxon.getUuid() + " is misapplication? "+acceptedTaxon.isMisapplication()+ "). The classification of the last taxon is used");
870
						
871
						}
872
					}
873
					
874
					if (classification != null) {
875
						try{
876
							TaxonNameBase name = acceptedTaxon.getName();
877
							//if (name.isSpecies() || name.isInfraSpecific()){
878
								inferredSynonyms  = getTaxonService().createAllInferredSynonyms(acceptedTaxon, classification, true);
879
							//}
880
//								inferredSynonyms = getTaxonService().createInferredSynonyms(classification, acceptedTaxon, SynonymRelationshipType.INFERRED_GENUS_OF());
881
							if (inferredSynonyms != null) {
882
								for (Synonym synonym : inferredSynonyms) {
883
//									TaxonNameBase<?,?> synonymName = synonym.getName();
884
									MarkerType markerType =getUuidMarkerType(PesiTransformer.uuidMarkerGuidIsMissing, state);
885
									synonym.addMarker(Marker.NewInstance(markerType, true));
886
									// Both Synonym and its TaxonName have no valid Id yet
887
									synonym.setId(currentTaxonId++);
888
									
889
									
890
									localSuccess &= mapping.invoke(synonym);
891
									//get SynonymRelationship and export
892
									if (synonym.getSynonymRelations().isEmpty() ){
893
										SynonymRelationship synRel;					
894
										IdentifiableSource source = synonym.getSources().iterator().next();
895
										if (source.getIdNamespace().contains("Potential combination")){
896
											synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
897
											logger.error(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to potential combination");
898
										} else if (source.getIdNamespace().contains("Inferred Genus")){
899
											synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.INFERRED_GENUS_OF());
900
											logger.error(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to inferred genus");
901
										} else if (source.getIdNamespace().contains("Inferred Epithet")){
902
											synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.INFERRED_EPITHET_OF());
903
											logger.error(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to inferred epithet");
904
										} else{
905
											synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.INFERRED_SYNONYM_OF());
906
											logger.error(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to inferred synonym");
907
										}
908
										
909
										localSuccess &= synRelMapping.invoke(synRel);
910
										if (!localSuccess) {
911
											logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
912
										}
913
										synRel = null;
914
									} else {
915
										for (SynonymRelationship synRel: synonym.getSynonymRelations()){
916
											localSuccess &= synRelMapping.invoke(synRel);
917
											if (!localSuccess) {
918
												logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
919
											} else {
920
												logger.info("Synonym relationship successfully exported: " + synonym.getTitleCache() + "  " +acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
921
											}
922
											synRel = null;
923
										}
924
									}
925
									
926
									inferredSynonymsDataToBeSaved.put(synonym.getId(), synonym.getName());
927
								}
928
							}
929
						}catch(Exception e){
930
							logger.error(e.getMessage());
931
							e.printStackTrace();
932
						}
933
					} else {
934
						logger.error("Classification is NULL. Inferred Synonyms could not be created for this Taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() + ")");
935
					}
936
				} else {
937
//							logger.error("TaxonName is not a ZoologicalName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
938
				}
939
			} else {
940
				logger.error("This TaxonBase is not a Taxon even though it should be: " + taxonBase.getUuid() + " (" + taxonBase.getTitleCache() + ")");
941
			}
942
		}
943
		taxonList = null;
944
		return inferredSynonymsDataToBeSaved;
945
	}
946
	
947

    
948
	/**
949
	 * Handles names that do not appear in taxa
950
	 * @param state
951
	 * @param mapping
952
	 * @return
953
	 */
954
	private boolean doNames(PesiExportState state, PesiExportMapping additionalSourceMapping)  throws SQLException {
955
		
956
		boolean success = true;
957
		if (! state.getConfig().isDoPureNames()){
958
			logger.info ("Ignore PHASE 1b: PureNames");
959
			return success;
960
		}
961
		
962
		try {
963
			PesiExportMapping mapping = getPureNameMapping(state);
964
			mapping.initialize(state);
965
			int count = 0;
966
			int pastCount = 0;
967
			List<NonViralName<?>> list;
968
			success = true;
969
			// Get the limit for objects to save within a single transaction.
970
			int limit = state.getConfig().getLimitSave();
971

    
972
			
973
			logger.info("PHASE 1b: Export Pure Names ...");
974
			// Start transaction
975
			TransactionStatus txStatus = startTransaction(true);
976
			logger.info("Started new transaction for Pure Names. Fetching some " + pluralString + " (max: " + limit + ") ...");
977
			
978
			int partitionCount = 0;
979
			while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null   ) {
980

    
981
				logger.info("Fetched " + list.size() + " names without taxa. Exporting...");
982
				for (TaxonNameBase<?,?> taxonName : list) {
983
					doCount(count++, modCount, pluralString);
984
					success &= mapping.invoke(taxonName);
985
					//additional source
986
					if (taxonName.getNomenclaturalReference() != null || StringUtils.isNotBlank(taxonName.getNomenclaturalMicroReference() )){
987
						additionalSourceMapping.invoke(taxonName);
988
					}
989
				}
990

    
991
				// Commit transaction
992
				commitTransaction(txStatus);
993
				logger.debug("Committed transaction.");
994
				logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
995
				pastCount = count;
996

    
997
				// Start transaction
998
				txStatus = startTransaction(true);
999
				logger.info("Started new transaction for PureNames. Fetching some " + pluralString + " (max: " + limit + ") ...");
1000
			}
1001
			if (list == null) {
1002
				logger.info("No " + pluralString + " left to fetch.");
1003
			}
1004
			// Commit transaction
1005
			commitTransaction(txStatus);
1006
			logger.debug("Committed transaction.");
1007
		} catch (Exception e) {
1008
			logger.error("Error occurred in pure name export");
1009
			e.printStackTrace();
1010
			success = false;
1011
		}
1012
		return success;
1013
	}
1014

    
1015
	/**
1016
	 * Determines the current number of entries in the DataWarehouse database table <code>Taxon</code>.
1017
	 * @param state The {@link PesiExportState PesiExportState}.
1018
	 * @return The count.
1019
	 */
1020
	private Integer determineTaxonCount(PesiExportState state) {
1021
		Integer result = null;
1022
		PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
1023
		
1024
		String sql;
1025
		Source destination =  pesiConfig.getDestination();
1026
		sql = "SELECT max(taxonId) FROM Taxon";
1027
		destination.setQuery(sql);
1028
		ResultSet resultSet = destination.getResultSet();
1029
		try {
1030
			resultSet.next();
1031
			result = resultSet.getInt(1);
1032
		} catch (SQLException e) {
1033
			logger.error("TaxonCount could not be determined: " + e.getMessage());
1034
			e.printStackTrace();
1035
		}
1036
		return result;
1037
	}
1038
	
1039
	/**
1040
	 * Checks whether a parent at specific level has a specific Rank.
1041
	 * @param taxonName A {@link TaxonNameBase TaxonName}.
1042
	 * @param level The ancestor level.
1043
	 * @param ancestorRank The ancestor rank.
1044
	 * @return Whether a parent at a specific level has a specific Rank.
1045
	 */
1046
	private boolean validateAncestorOfSpecificRank(TaxonBase<?> taxonBase, int level, Rank ancestorRank) {
1047
		boolean result = false;
1048
		TaxonNode parentNode = null;
1049
		if (taxonBase.isInstanceOf(Taxon.class)){
1050
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1051
			// Get ancestor Taxon via TaxonNode
1052
			Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();
1053
			if (taxonNodes.size() == 1) {
1054
				TaxonNode taxonNode = taxonNodes.iterator().next();
1055
				if (taxonNode != null) {
1056
					for (int i = 0; i < level; i++) {
1057
						if (taxonNode != null) {
1058
							taxonNode  = taxonNode.getParent();
1059
						}
1060
					}
1061
					parentNode = taxonNode;
1062
				}
1063
			} else if (taxonNodes.size() > 1) {
1064
				logger.error("This taxon has " + taxonNodes.size() + " taxonNodes: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1065
			}
1066
		}
1067
		//compare
1068
		if (parentNode != null) {
1069
			TaxonNode node = CdmBase.deproxy(parentNode, TaxonNode.class);
1070
			Taxon parentTaxon = node.getTaxon();
1071
			if (parentTaxon != null) {
1072
				TaxonNameBase<?,?> parentTaxonName = parentTaxon.getName();
1073
				if (parentTaxonName != null && parentTaxonName.getRank().equals(ancestorRank)) {
1074
					result = true;
1075
				}
1076
			} else if (parentNode.treeIndex().matches("#t\\d+#\\d+#")) {
1077
				//do nothing (is root node)
1078
			} else {
1079
				logger.error("This TaxonNode has no Taxon: " + node.getUuid());
1080
			}
1081
		}
1082
		return result;
1083
	}
1084

    
1085
	/**
1086
	 * Returns the AnnotationType for a given UUID.
1087
	 * @param uuid The Annotation UUID.
1088
	 * @param label The Annotation label.
1089
	 * @param text The Annotation text.
1090
	 * @param labelAbbrev The Annotation label abbreviation.
1091
	 * @return The AnnotationType.
1092
	 */
1093
	protected AnnotationType getAnnotationType(UUID uuid, String label, String text, String labelAbbrev){
1094
		AnnotationType annotationType = (AnnotationType)getTermService().find(uuid);
1095
		if (annotationType == null) {
1096
			annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
1097
			annotationType.setUuid(uuid);
1098
//			annotationType.setVocabulary(AnnotationType.EDITORIAL().getVocabulary());
1099
			getTermService().save(annotationType);
1100
		}
1101
		return annotationType;
1102
	}
1103

    
1104
	/**
1105
	 * Traverses the classification recursively and stores determined values for every Taxon.
1106
	 * @param childNode The {@link TaxonNode TaxonNode} to process.
1107
	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1108
	 * @param treeIndex The TreeIndex at the current level.
1109
	 * @param fetchLevel Rank to stop fetching at.
1110
	 * @param state The {@link PesiExportState PesiExportState}.
1111
	 */
1112
	private void traverseTree(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, Rank fetchLevel, PesiExportState state) {
1113
		// Traverse all branches from this childNode until specified fetchLevel is reached.
1114
		StringBuffer localTreeIndex = new StringBuffer(treeIndex);
1115
		Taxon childTaxon = childNode.getTaxon();
1116
		if (childTaxon != null) {
1117
			if (isPesiTaxon(childTaxon)){
1118
				Integer taxonId = state.getDbId(childTaxon);
1119
				TaxonNameBase<?,?> childName = childTaxon.getName();
1120
				if (taxonId != null) {
1121
					Rank childRank = childName.getRank();
1122
					if (childRank != null) {
1123
						if (! childRank.equals(fetchLevel)) {
1124
	
1125
							localTreeIndex.append(taxonId + "#");
1126
							
1127
							saveData(childNode, parentNode, localTreeIndex, state, taxonId);
1128
	
1129
							// Store treeIndex as annotation for further use
1130
							Annotation annotation = Annotation.NewInstance(localTreeIndex.toString(), getTreeIndexAnnotationType(), Language.DEFAULT());
1131
							childNode.addAnnotation(annotation);
1132
	
1133
							for (TaxonNode newNode : childNode.getChildNodes()) {
1134
								if (newNode.getTaxon() != null && isPesiTaxon(newNode.getTaxon())){
1135
									traverseTree(newNode, childNode, localTreeIndex, fetchLevel, state);
1136
								}
1137
							}
1138
							
1139
						} else {
1140
	//						logger.debug("Target Rank " + fetchLevel.getLabel() + " reached");
1141
							return;
1142
						}
1143
					} else {
1144
						logger.error("Rank is NULL. FetchLevel can not be checked: " + childName.getUuid() + " (" + childName.getTitleCache() + ")");
1145
					}
1146
				} else {
1147
					logger.error("Taxon can not be found in state: " + childTaxon.getUuid() + " (" + childTaxon.getTitleCache() + ")");
1148
				}
1149
			}else{
1150
				if (logger.isDebugEnabled()){ 
1151
					logger.debug("Taxon is not a PESI taxon: " + childTaxon.getUuid());
1152
				}
1153
			}
1154

    
1155
		} else {
1156
			logger.error("Taxon is NULL for TaxonNode: " + childNode.getUuid());
1157
		}
1158
	}
1159

    
1160
	/**
1161
	 * Stores values in database for every recursive round.
1162
	 * @param childNode The {@link TaxonNode TaxonNode} to process.
1163
	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1164
	 * @param treeIndex The TreeIndex at the current level.
1165
	 * @param state The {@link PesiExportState PesiExportState}.
1166
	 * @param currentTaxonFk The TaxonFk to store the values for.
1167
	 */
1168
	private void saveData(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, PesiExportState state, Integer currentTaxonFk) {
1169
		// We are differentiating kingdoms by the nomenclatural code for now.
1170
		// This needs to be handled in a better way as soon as we know how to differentiate between more kingdoms.
1171
		Taxon childTaxon = childNode.getTaxon();
1172
		if (isPesiTaxon(childTaxon)) {
1173
			TaxonBase<?> parentTaxon = null;
1174
			if (parentNode != null) {
1175
				parentTaxon = parentNode.getTaxon();
1176
				
1177
			}
1178

    
1179
			invokeParentTaxonFkAndTreeIndex(state.getDbId(parentTaxon), currentTaxonFk,	treeIndex);
1180
		}
1181
		
1182
	}
1183

    
1184
	/**
1185
	 * Inserts values into the Taxon database table.
1186
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1187
	 * @param state The {@link PesiExportState PesiExportState}.
1188
	 * @param stmt The prepared statement.
1189
	 * @return Whether save was successful or not.
1190
	 */
1191
	protected boolean invokeParentTaxonFkAndTreeIndex(Integer parentTaxonFk, Integer currentTaxonFk, StringBuffer treeIndex) {
1192
		try {
1193
			if (parentTaxonFk != null) {
1194
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(1, parentTaxonFk);
1195
			} else {
1196
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(1, null);
1197
			}
1198

    
1199
			if (treeIndex != null) {
1200
				parentTaxonFk_TreeIndex_KingdomFkStmt.setString(2, treeIndex.toString());
1201
			} else {
1202
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(2, null);
1203
			}
1204

    
1205
			if (currentTaxonFk != null) {
1206
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(3, currentTaxonFk);
1207
			} else {
1208
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(3, null);
1209
			}
1210
			
1211
			parentTaxonFk_TreeIndex_KingdomFkStmt.executeUpdate();
1212
			return true;
1213
		} catch (SQLException e) {
1214
			logger.error("ParentTaxonFk (" + parentTaxonFk ==null? "-":parentTaxonFk + ") and TreeIndex could not be inserted into database for taxon "+ (currentTaxonFk == null? "-" :currentTaxonFk) + ": " + e.getMessage());
1215
			e.printStackTrace();
1216
			return false;
1217
		}
1218
	}
1219
	
1220
	protected boolean invokeParentTaxonFk(Integer parentId, Integer childId) {
1221
		try {
1222
			parentTaxonFkStmt.setInt(1, parentId);
1223
			parentTaxonFkStmt.setInt(2, childId);
1224
			parentTaxonFkStmt.executeUpdate();
1225
			return true;
1226
		} catch (SQLException e) {
1227
			logger.warn("ParentTaxonFk (" + parentId ==null? "-":parentId + ") could not be inserted into database for taxon "+ (childId == null? "-" :childId) + ": " + e.getMessage());
1228
			e.printStackTrace();
1229
			return false;
1230
		}
1231
	}
1232

    
1233

    
1234
	/**
1235
	 * Inserts Rank data and KingdomFk into the Taxon database table.
1236
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1237
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1238
	 * @param taxonFk The TaxonFk to store the values for.
1239
	 * @param state 
1240
	 * @param kindomFk The KingdomFk.
1241
	 * @return Whether save was successful or not.
1242
	 */
1243
	private boolean invokeRankDataAndKingdomFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, Integer taxonFk, Integer kingdomFk, PesiExportState state) {
1244
		try {
1245
			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1246
			if (rankFk != null) {
1247
				rankUpdateStmt.setInt(1, rankFk);
1248
			} else {
1249
				rankUpdateStmt.setObject(1, null);
1250
			}
1251
	
1252
			String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
1253
			if (rankCache != null) {
1254
				rankUpdateStmt.setString(2, rankCache);
1255
			} else {
1256
				rankUpdateStmt.setObject(2, null);
1257
			}
1258
			
1259
			if (kingdomFk != null) {
1260
				rankUpdateStmt.setInt(3, kingdomFk);
1261
			} else {
1262
				rankUpdateStmt.setObject(3, null);
1263
			}
1264
			
1265
			if (taxonFk != null) {
1266
				rankUpdateStmt.setInt(4, taxonFk);
1267
			} else {
1268
				rankUpdateStmt.setObject(4, null);
1269
			}
1270
			
1271
			rankUpdateStmt.executeUpdate();
1272
			return true;
1273
		} catch (SQLException e) {
1274
			logger.error("Data (RankFk, RankCache, KingdomFk) could not be inserted into database: " + e.getMessage());
1275
			e.printStackTrace();
1276
			return false;
1277
		}
1278
	}
1279

    
1280
	/**
1281
	 * Inserts Rank data, TypeNameFk, KingdomFk, expertFk and speciesExpertFk into the Taxon database table.
1282
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1283
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1284
	 * @param taxonFk The TaxonFk to store the values for.
1285
	 * @param typeNameFk The TypeNameFk.
1286
	 * @param state 
1287
	 * @param kindomFk The KingdomFk.
1288
	 * @param expertFk The ExpertFk.
1289
	 * @param speciesExpertFk The SpeciesExpertFk.
1290
	 * @return Whether save was successful or not.
1291
	 */
1292
	private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, 
1293
			Integer taxonFk, Integer typeNameFk, Integer kingdomFkk, PesiExportState state) {
1294
		try {
1295
			int index = 1;
1296
			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1297
			if (rankFk != null) {
1298
				rankTypeExpertsUpdateStmt.setInt(index++, rankFk);
1299
			} else {
1300
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1301
			}
1302
	
1303
			String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
1304
			if (rankCache != null) {
1305
				rankTypeExpertsUpdateStmt.setString(index++, rankCache);
1306
			} else {
1307
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1308
			}
1309
			
1310
			if (typeNameFk != null) {
1311
				rankTypeExpertsUpdateStmt.setInt(index++, typeNameFk);
1312
			} else {
1313
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1314
			}
1315
			
1316
			if (kingdomFk != null) {
1317
				rankTypeExpertsUpdateStmt.setInt(index++, kingdomFk);
1318
			} else {
1319
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1320
			}
1321
			
1322
//			if (expertFk != null) {
1323
//				rankTypeExpertsUpdateStmt.setInt(5, expertFk);
1324
//			} else {
1325
//				rankTypeExpertsUpdateStmt.setObject(5, null);
1326
//			}
1327
//
1328
//			//TODO handle experts GUIDS
1329
//			if (speciesExpertFk != null) {
1330
//				rankTypeExpertsUpdateStmt.setInt(6, speciesExpertFk);
1331
//			} else {
1332
//				rankTypeExpertsUpdateStmt.setObject(6, null);
1333
//			}
1334
//			
1335
			if (taxonFk != null) {
1336
				rankTypeExpertsUpdateStmt.setInt(index++, taxonFk);
1337
			} else {
1338
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1339
			}
1340

    
1341
			rankTypeExpertsUpdateStmt.executeUpdate();
1342
			return true;
1343
		} catch (SQLException e) {
1344
			logger.error("Data could not be inserted into database: " + e.getMessage());
1345
			e.printStackTrace();
1346
			return false;
1347
		} catch (Exception e) {
1348
			logger.error("Some exception occurred: " + e.getMessage());
1349
			e.printStackTrace();
1350
			return false;
1351
		}
1352
	}
1353

    
1354
	/**
1355
	 * Deletes all entries of database tables related to <code>Taxon</code>.
1356
	 * @param state The {@link PesiExportState PesiExportState}.
1357
	 * @return Whether the delete operation was successful or not.
1358
	 */
1359
	protected boolean doDelete(PesiExportState state) {
1360
		PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
1361
		
1362
		String sql;
1363
		Source destination =  pesiConfig.getDestination();
1364

    
1365
		// Clear Taxon
1366
		sql = "DELETE FROM " + dbTableName;
1367
		destination.setQuery(sql);
1368
		destination.update(sql);
1369
		return true;
1370
	}
1371

    
1372
	/* (non-Javadoc)
1373
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
1374
	 */
1375
	@Override
1376
	protected boolean isIgnore(PesiExportState state) {
1377
		return ! state.getConfig().isDoTaxa();
1378
	}
1379

    
1380
	
1381
	/**
1382
	 * Creates the kingdom fk.
1383
	 * @param taxonName
1384
	 * @return
1385
	 */
1386
	@SuppressWarnings("unused")  //used by mapper
1387
	private static Integer getKingdomFk(TaxonNameBase taxonName){
1388
		return PesiTransformer.nomenClaturalCode2Kingdom(taxonName.getNomenclaturalCode());
1389
	}
1390
	
1391
	/**
1392
	 * Creates the parent fk.
1393
	 * @param taxonName
1394
	 * @return
1395
	 */
1396
	@SuppressWarnings("unused")  //used by mapper
1397
	private static Integer getParentTaxonFk(TaxonBase<?> taxonBase, PesiExportState state){
1398
		if (taxonBase.isInstanceOf(Taxon.class)){
1399
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1400
			if (! isMisappliedName(taxon)){
1401
				Set<TaxonNode> nodes = taxon.getTaxonNodes();
1402
				if (nodes.size() == 0){
1403
					if (taxon.getName().getRank().isLower(Rank.KINGDOM())){
1404
						logger.warn("Accepted taxon has no parent. " + taxon.getTitleCache() + ", " +  taxon.getUuid());
1405
					}
1406
				}else if (nodes.size() > 1){
1407
					logger.warn("Taxon has more than 1 node attached. This is not supported by PESI export." +  taxon.getTitleCache() + ", " +  taxon.getUuid());
1408
				}else{
1409
					Taxon parent =nodes.iterator().next().getParent().getTaxon();
1410
					return state.getDbId(parent);
1411
				}
1412
			}
1413
		}
1414
		return null;
1415
	}
1416

    
1417
	/**
1418
	 * Returns the rankFk for the taxon name based on the names nomenclatural code.
1419
	 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1420
	 * @param taxonName
1421
	 * @return
1422
	 */
1423
	@SuppressWarnings("unused")  //used by mapper
1424
	private static Integer getRankFk(TaxonNameBase<?,?> taxonName) {
1425
		return getRankFk(taxonName, taxonName.getNomenclaturalCode());
1426
	}
1427
		
1428
	
1429
	/**
1430
	 * Returns the <code>RankFk</code> attribute.
1431
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1432
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1433
	 * @return The <code>RankFk</code> attribute.
1434
	 * @see MethodMapper
1435
	 */
1436
	private static Integer getRankFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode) {
1437
		Integer result = null;
1438
		try {
1439
			if (nomenclaturalCode != null) {
1440
				if (taxonName != null) {
1441
					if (taxonName.getRank() == null) {
1442
						logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1443
					} else {
1444
						result = PesiTransformer.rank2RankId(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1445
					}
1446
					if (result == null) {
1447
						logger.warn("Rank could not be determined for PESI-Kingdom-Id " + PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode) + " and TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1448
					}
1449
				}
1450
			}
1451
		} catch (Exception e) {
1452
			e.printStackTrace();
1453
		}
1454
		return result;
1455
	}
1456

    
1457
	/**
1458
	 * Returns the rank cache for the taxon name based on the names nomenclatural code.
1459
	 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1460
	 * @param taxonName
1461
	 * @return
1462
	 */
1463
	@SuppressWarnings("unused")  //used by mapper
1464
	private static String getRankCache(TaxonNameBase<?,?> taxonName, PesiExportState state) {
1465
		return getRankCache(taxonName, taxonName.getNomenclaturalCode(), state);
1466
	}
1467

    
1468
	
1469
	/**
1470
	 * Returns the <code>RankCache</code> attribute.
1471
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1472
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1473
	 * @param state 
1474
	 * @return The <code>RankCache</code> attribute.
1475
	 * @see MethodMapper
1476
	 */
1477
	private static String getRankCache(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, PesiExportState state) {
1478
		if (nomenclaturalCode != null) {
1479
			return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1480
		}else{
1481
			logger.warn("No nomenclatural code defined for name " + taxonName.getUuid());
1482
			return null;
1483
		}
1484
		
1485
	}
1486

    
1487
	
1488
	/**
1489
	 * Returns the <code>DisplayName</code> attribute.
1490
	 * @param taxon The {@link TaxonBase Taxon}.
1491
	 * @return The <code>DisplayName</code> attribute.
1492
	 * @see MethodMapper
1493
	 */
1494
	@SuppressWarnings("unused")  //used by Mapper
1495
	private static String getDisplayName(TaxonBase<?> taxon) {
1496
		TaxonNameBase<?,?> taxonName = taxon.getName();
1497
		String result = getDisplayName(taxonName);
1498
		if (isMisappliedName(taxon)){
1499
			result = result + " " + getAuthorString(taxon);
1500
		}
1501
		return result;
1502
	}
1503
	
1504
	/**
1505
	 * Returns the <code>AuthorString</code> attribute.
1506
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1507
	 * @return The <code>AuthorString</code> attribute.
1508
	 * @see MethodMapper
1509
	 */
1510
	@SuppressWarnings("unused") //used by mapper
1511
	protected static String getAuthorString(TaxonBase<?> taxon) {
1512
		try {
1513
			String result = null;
1514
			boolean isNonViralName = false;
1515
			String authorshipCache = null;
1516
			TaxonNameBase<?,?> taxonName = taxon.getName();
1517
			if (taxonName != null && taxonName.isInstanceOf(NonViralName.class)){
1518
				authorshipCache = CdmBase.deproxy(taxonName, NonViralName.class).getAuthorshipCache();
1519
				isNonViralName = true;
1520
			}
1521
			result = authorshipCache;
1522
			
1523
			// For a misapplied names there are special rules
1524
			if (isMisappliedName(taxon)){
1525
				if (taxon.getSec() != null){
1526
					String secTitle = taxon.getSec().getTitleCache();
1527
					if (! secTitle.startsWith("auct")){
1528
						secTitle = "sensu " + secTitle;
1529
					}else if (secTitle.equals("auct")){  //may be removed once the title cache is generated correctly for references with title auct. #
1530
						secTitle = "auct.";
1531
					}
1532
					return secTitle;
1533
				}else if (StringUtils.isBlank(authorshipCache)) {
1534
					// Set authorshipCache to "auct."
1535
					result = PesiTransformer.AUCT_STRING;
1536
				}else{
1537
					result = PesiTransformer.AUCT_STRING;
1538
//					result = authorshipCache;
1539
				}
1540
			}
1541
			
1542
			if (taxonName == null){
1543
				logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1544
			}else if (! isNonViralName){
1545
				logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1546
			}
1547
			
1548
			if (StringUtils.isBlank(result)) {
1549
				return null;
1550
			} else {
1551
				return result;
1552
			}
1553
		} catch (Exception e) {
1554
			e.printStackTrace();
1555
			return null;
1556
		}
1557
		
1558
	}
1559
		
1560
	
1561
	/**
1562
	 * Returns the <code>DisplayName</code> attribute.
1563
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1564
	 * @return The <code>DisplayName</code> attribute.
1565
	 * @see MethodMapper
1566
	 */
1567
	@SuppressWarnings("unused")  //used by Mapper
1568
	private static String getDisplayName(TaxonNameBase<?,?> taxonName) {
1569
		// TODO: extension?
1570
		if (taxonName == null) {
1571
			return null;
1572
		}else{
1573
			INonViralNameCacheStrategy<NonViralName<?>> cacheStrategy = getCacheStrategy(taxonName);
1574
			HTMLTagRules tagRules = new HTMLTagRules().
1575
					addRule(TagEnum.name, "i").
1576
					addRule(TagEnum.nomStatus, "@status@");
1577
			
1578
			NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1579
			String result = cacheStrategy.getFullTitleCache(nvn, tagRules);
1580
			cacheStrategy = null;
1581
			nvn = null;
1582
			return result.replaceAll(",?\\<@status@\\>.*\\</@status@\\>", "");
1583
		}
1584
	}
1585
	
1586

    
1587
	/**
1588
	 * Returns the <code>WebShowName</code> attribute for a taxon.
1589
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1590
	 * @return The <code>WebShowName</code> attribute.
1591
	 * @see MethodMapper
1592
	*/
1593
	@SuppressWarnings("unused")
1594
	private static String getWebShowName(TaxonBase<?> taxon) {
1595
		TaxonNameBase<?,?> taxonName = taxon.getName();
1596
		String result = getWebShowName(taxonName);
1597
		if (isMisappliedName(taxon)){
1598
			result = result + " " + getAuthorString(taxon);
1599
		}
1600
		return result;
1601
	}
1602
	
1603
	/**
1604
	 * Returns the <code>WebShowName</code> attribute.
1605
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1606
	 * @return The <code>WebShowName</code> attribute.
1607
	 * @see MethodMapper
1608
	 */
1609
	private static String getWebShowName(TaxonNameBase<?,?> taxonName) {
1610
		//TODO extensions?
1611
		if (taxonName == null) {
1612
			return null;
1613
		}else{
1614
			INonViralNameCacheStrategy<NonViralName<?>> cacheStrategy = getCacheStrategy(taxonName);
1615
		
1616
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1617
			NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1618
			String result = cacheStrategy.getTitleCache(nvn, tagRules);
1619
			cacheStrategy = null;
1620
			nvn = null;
1621
			return result;
1622
		}
1623
	}
1624

    
1625
 	
1626
	/**
1627
	 * Returns the <code>WebSearchName</code> attribute.
1628
	 * @param taxonName The {@link NonViralName NonViralName}.
1629
	 * @return The <code>WebSearchName</code> attribute.
1630
	 * @see MethodMapper
1631
	 */
1632
	@SuppressWarnings("unused")
1633
	private static String getWebSearchName(TaxonNameBase<?,?> taxonName) {
1634
		//TODO extensions?
1635
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1636
		NonViralNameDefaultCacheStrategy<NonViralName<?>> strategy = getCacheStrategy(nvn);
1637
		String result = strategy.getNameCache(nvn);
1638
		strategy = null;
1639
		nvn = null;
1640
		return result;
1641
	}
1642

    
1643

    
1644
	/**
1645
	 * Returns the <code>FullName</code> attribute.
1646
	 * @param taxonName The {@link NonViralName NonViralName}.
1647
	 * @return The <code>FullName</code> attribute.
1648
	 * @see MethodMapper
1649
	 */
1650
	@SuppressWarnings("unused")
1651
	private static String getFullName(TaxonNameBase taxonName) {
1652
		//TODO extensions?
1653
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1654
		String result = getCacheStrategy(nvn).getTitleCache(nvn);
1655
		Iterator<TaxonBase> taxa = taxonName.getTaxa().iterator();
1656
		if (taxonName.getTaxa().size() >0){
1657
			if (taxonName.getTaxa().size() == 1){
1658
				TaxonBase taxon = taxa.next();
1659
				if (isMisappliedName(taxon)){
1660
					result = result + " " + getAuthorString(taxon);
1661
				}
1662
				taxon = null;
1663
			}
1664
		}
1665
		taxa = null;
1666
		nvn = null;
1667
		return result;
1668
	}
1669
	
1670
	/**
1671
	 * Returns the <code>FullName</code> attribute.
1672
	 * @param taxon The {@link TaxonBase taxon}.
1673
	 * @return The <code>FullName</code> attribute.
1674
	 * @see MethodMapper
1675
	 */
1676
	/*@SuppressWarnings("unused")
1677
	private static String getFullName(TaxonBase taxon) {
1678
		//TODO extensions?
1679
		TaxonNameBase name = taxon.getName();
1680
		String result = getFullName(name);
1681
		if (isMisappliedName(taxon)){
1682
			result = result + " " + getAuthorString(taxon);
1683
		}
1684
		
1685
		return result;
1686
	}
1687
*/
1688
	
1689
	/**
1690
	 * Returns the nomenclatural reference which is the reference
1691
	 * including the detail (microreference).
1692
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1693
	 * @return The <code>AuthorString</code> attribute.
1694
	 * @see MethodMapper
1695
	 */
1696
	@SuppressWarnings("unused")
1697
	private static String getNomRefString(TaxonNameBase<?,?> taxonName) {
1698
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1699
		return ref == null ? null : ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1700
	}
1701
	
1702

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

    
1713
		NomenclaturalStatus state = getNameStatus(taxonName);
1714
		if (state != null) {
1715
			result = PesiTransformer.nomStatus2nomStatusFk(state.getType());
1716
		}
1717
		return result;
1718
	}
1719
	
1720
	/**
1721
	 * Returns the <code>NameStatusCache</code> attribute.
1722
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1723
	 * @return The <code>NameStatusCache</code> attribute.
1724
	 * @throws UndefinedTransformerMethodException 
1725
	 * @see MethodMapper
1726
	 */
1727
	@SuppressWarnings("unused")
1728
	private static String getNameStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1729
		String result = null;
1730
		NomenclaturalStatus status = getNameStatus(taxonName);
1731
		if (status != null) {
1732
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1733
		}
1734
		return result;
1735
	}
1736
	
1737
	
1738
	private static NomenclaturalStatus getNameStatus(TaxonNameBase<?,?> taxonName) {
1739
		try {
1740
			if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1741
				NonViralName<?> nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1742
				Set<NomenclaturalStatus> states = nonViralName.getStatus();
1743
				if (states.size() == 1) {
1744
					NomenclaturalStatus status = states.iterator().next();
1745
					return status;
1746
				} else if (states.size() > 1) {
1747
					logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1748
				}
1749
			}
1750
		
1751
		} catch (Exception e) {
1752
			e.printStackTrace();
1753
		}
1754
		return null;
1755
	}
1756
	/**
1757
	 * Returns the <code>TaxonStatusFk</code> attribute.
1758
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1759
	 * @param state The {@link PesiExportState PesiExportState}.
1760
	 * @return The <code>TaxonStatusFk</code> attribute.
1761
	 * @see MethodMapper
1762
	 */
1763
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1764
		Integer result = null;
1765
		
1766
		try {
1767
			if (isMisappliedName(taxon)) {
1768
				Synonym synonym = Synonym.NewInstance(null, null);
1769
				
1770
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1771
				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1772
			} else {
1773
				result = PesiTransformer.taxonBase2statusFk(taxon);
1774
			}
1775
		
1776
		} catch (Exception e) {
1777
			e.printStackTrace();
1778
		}
1779
		return result;
1780
	}
1781
	
1782
	/**
1783
	 * Returns the <code>TaxonStatusCache</code> attribute.
1784
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1785
	 * @param state The {@link PesiExportState PesiExportState}.
1786
	 * @return The <code>TaxonStatusCache</code> attribute.
1787
	 * @throws UndefinedTransformerMethodException 
1788
	 * @see MethodMapper
1789
	 */
1790
	@SuppressWarnings("unused")
1791
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1792
		return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1793
	}
1794
	
1795
	/**
1796
	 * Returns the <code>TypeNameFk</code> attribute.
1797
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1798
	 * @param state The {@link PesiExportState PesiExportState}.
1799
	 * @return The <code>TypeNameFk</code> attribute.
1800
	 * @see MethodMapper
1801
	 */
1802
	private static Integer getTypeNameFk(TaxonNameBase<?,?> taxonNameBase, PesiExportState state) {
1803
		Integer result = null;
1804
		if (taxonNameBase != null) {
1805
			Set<NameTypeDesignation> nameTypeDesignations = taxonNameBase.getNameTypeDesignations();
1806
			if (nameTypeDesignations.size() == 1) {
1807
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1808
				if (nameTypeDesignation != null) {
1809
					TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1810
					if (typeName != null) {
1811
						result = state.getDbId(typeName);
1812
					}
1813
				}
1814
			} else if (nameTypeDesignations.size() > 1) {
1815
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonNameBase.getUuid() + " (" + taxonNameBase.getTitleCache() + ")");
1816
			}
1817
		}
1818
		return result;
1819
	}
1820
	
1821
	/**
1822
	 * Returns the <code>TypeFullnameCache</code> attribute.
1823
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1824
	 * @return The <code>TypeFullnameCache</code> attribute.
1825
	 * @see MethodMapper
1826
	 */
1827
	@SuppressWarnings("unused")
1828
	private static String getTypeFullnameCache(TaxonNameBase<?,?> taxonName) {
1829
		String result = null;
1830
		
1831
		try {
1832
		if (taxonName != null) {
1833
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1834
			if (nameTypeDesignations.size() == 1) {
1835
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1836
				if (nameTypeDesignation != null) {
1837
					TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1838
					if (typeName != null) {
1839
						result = typeName.getTitleCache();
1840
					}
1841
				}
1842
			} else if (nameTypeDesignations.size() > 1) {
1843
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1844
			}
1845
		}
1846
		
1847
		} catch (Exception e) {
1848
			e.printStackTrace();
1849
		}
1850
		return result;
1851
	}
1852

    
1853
	
1854
	/**
1855
	 * Returns the <code>QualityStatusFk</code> attribute.
1856
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1857
	 * @return The <code>QualityStatusFk</code> attribute.
1858
	 * @see MethodMapper
1859
	 */
1860
	private static Integer getQualityStatusFk(TaxonNameBase taxonName) {
1861
		BitSet sources = getSources(taxonName);
1862
		return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1863
	}
1864

    
1865
	
1866
	/**
1867
	 * Returns the <code>QualityStatusCache</code> attribute.
1868
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1869
	 * @return The <code>QualityStatusCache</code> attribute.
1870
	 * @throws UndefinedTransformerMethodException 
1871
	 * @see MethodMapper
1872
	 */
1873
	@SuppressWarnings("unused")
1874
	private static String getQualityStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1875
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1876
	}
1877

    
1878
	
1879
	/**
1880
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1881
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1882
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1883
	 * @see MethodMapper
1884
	 */
1885
	@SuppressWarnings("unused")
1886
	private static Integer getTypeDesignationStatusFk(TaxonNameBase<?,?> taxonName) {
1887
		Integer result = null;
1888
		
1889
		try {
1890
		if (taxonName != null) {
1891
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1892
			if (typeDesignations.size() == 1) {
1893
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1894
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1895
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1896
			} else if (typeDesignations.size() > 1) {
1897
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1898
			}
1899
		}
1900
		
1901
		} catch (Exception e) {
1902
			e.printStackTrace();
1903
		}
1904
		return result;
1905
	}
1906

    
1907
	/**
1908
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1909
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1910
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1911
	 * @see MethodMapper
1912
	 */
1913
	@SuppressWarnings("unused")
1914
	private static String getTypeDesignationStatusCache(TaxonNameBase<?,?> taxonName) {
1915
		String result = null;
1916
		
1917
		try {
1918
		if (taxonName != null) {
1919
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1920
			if (typeDesignations.size() == 1) {
1921
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1922
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1923
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
1924
			} else if (typeDesignations.size() > 1) {
1925
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1926
			}
1927
		}
1928
		
1929
		} catch (Exception e) {
1930
			e.printStackTrace();
1931
		}
1932
		return result;
1933
	}
1934
	
1935
	/**
1936
	 * Returns the <code>FossilStatusFk</code> attribute.
1937
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1938
	 * @return The <code>FossilStatusFk</code> attribute.
1939
	 * @see MethodMapper
1940
	 */
1941
	@SuppressWarnings("unused")
1942
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
1943
		Integer result = null;
1944
		
1945
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
1946
		if (fossilStatuus.size() == 0){
1947
			return null;
1948
		}else if (fossilStatuus.size() > 1){
1949
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
1950
		}
1951
		String fossilStatus = fossilStatuus.iterator().next();
1952
		
1953
		int statusFk = state.getTransformer().FossilStatusCache2FossilStatusFk(fossilStatus);
1954
		return statusFk;
1955
	}
1956
	
1957
	/**
1958
	 * Returns the <code>FossilStatusCache</code> attribute.
1959
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1960
	 * @return The <code>FossilStatusCache</code> attribute.
1961
	 * @see MethodMapper
1962
	 */
1963
	@SuppressWarnings("unused")
1964
	private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
1965
		String result = null;
1966
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
1967
		if (fossilStatuus.size() == 0){
1968
			return null;
1969
		}
1970
		for (String strFossilStatus : fossilStatuus){
1971
			result = CdmUtils.concat(";", result, strFossilStatus);
1972
		}
1973
		return result;
1974
	}
1975
	
1976
	/**
1977
	 * Returns the <code>IdInSource</code> attribute.
1978
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1979
	 * @return The <code>IdInSource</code> attribute.
1980
	 * @see MethodMapper
1981
	 */
1982
	@SuppressWarnings("unused")
1983
	private static String getIdInSource(IdentifiableEntity taxonName) {
1984
		String result = null;
1985
		
1986
		try {
1987
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
1988
			if (sources.size() > 1){
1989
				logger.warn("There is > 1 Pesi source. This is not yet handled.");
1990
			}
1991
			if (sources.size() == 0){
1992
				logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1993
			}
1994
			for (IdentifiableSource source : sources) {
1995
				Reference<?> ref = source.getCitation();
1996
				UUID refUuid = ref.getUuid();
1997
				String idInSource = source.getIdInSource();
1998
				if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
1999
					result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
2000
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2001
					result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
2002
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2003
					result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
2004
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
2005
					result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
2006
				}else{
2007
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2008
				}
2009
				
2010
				String sourceIdNameSpace = source.getIdNamespace();
2011
				if (sourceIdNameSpace != null) {
2012
					if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
2013
						result =  idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
2014
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
2015
						result =  idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
2016
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
2017
						result =  idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
2018
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
2019
						result =  idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
2020
					} 
2021
				}
2022
				if (result == null) {
2023
					logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
2024
				}
2025
			}
2026
		} catch (Exception e) {
2027
			e.printStackTrace();
2028
			logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
2029
		}
2030

    
2031
		if (result == null) {
2032
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2033
		}
2034
		return result;
2035
	}
2036
	
2037
	/**
2038
	 * Returns the idInSource for a given TaxonName only.
2039
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2040
	 * @return The idInSource.
2041
	 */
2042
	private static String getIdInSourceOnly(IdentifiableEntity identEntity) {
2043
		String result = null;
2044
		
2045
		// Get the sources first
2046
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2047

    
2048
		// Determine the idInSource
2049
		if (sources.size() == 1) {
2050
			IdentifiableSource source = sources.iterator().next();
2051
			if (source != null) {
2052
				result = source.getIdInSource();
2053
			}
2054
		} else if (sources.size() > 1) {
2055
			int count = 1;
2056
			result = "";
2057
			for (IdentifiableSource source : sources) {
2058
				result += source.getIdInSource();
2059
				if (count < sources.size()) {
2060
					result += "; ";
2061
				}
2062
				count++;
2063
			}
2064

    
2065
		}
2066
		
2067
		return result;
2068
	}
2069
	
2070
	/**
2071
	 * Returns the Sources for a given TaxonName only.
2072
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2073
	 * @return The Sources.
2074
	 */
2075
	private static Set<IdentifiableSource> getPesiSources(IdentifiableEntity identEntity) {
2076
		Set<IdentifiableSource> sources = new java.util.HashSet<IdentifiableSource>();
2077

    
2078
		//Taxon Names
2079
		if (identEntity.isInstanceOf(TaxonNameBase.class)){
2080
			// Sources from TaxonName
2081
			TaxonNameBase taxonName = CdmBase.deproxy(identEntity, TaxonNameBase.class);
2082
			Set<IdentifiableSource> testSources = identEntity.getSources();
2083
			sources = filterPesiSources(identEntity.getSources());
2084
			
2085
			if (sources.size() == 0 && testSources.size()>0){
2086
				IdentifiableSource source = testSources.iterator().next();
2087
				logger.warn("There are sources, but they are no pesi sources!!!" + source.getIdInSource() + " - " + source.getIdNamespace() + " - " + source.getCitation().getTitleCache());
2088
			}
2089
			if (sources.size() > 1) {
2090
				logger.warn("This TaxonName has more than one Source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() + ")");
2091
			}
2092
			
2093
			// name has no PESI source, take sources from TaxonBase
2094
			if (sources == null || sources.isEmpty()) {
2095
				Set<TaxonBase> taxa = taxonName.getTaxonBases();
2096
				for (TaxonBase taxonBase: taxa){
2097
					sources.addAll(filterPesiSources(taxonBase.getSources()));
2098
				}
2099
			}
2100

    
2101
		//for TaxonBases
2102
		}else if (identEntity.isInstanceOf(TaxonBase.class)){
2103
			sources = filterPesiSources(identEntity.getSources());	
2104
		}
2105

    
2106
		/*TODO: deleted only for testing the inferred synonyms 
2107
		if (sources == null || sources.isEmpty()) {
2108
			logger.warn("This TaxonName has no PESI Sources: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2109
		}else if (sources.size() > 1){
2110
			logger.warn("This Taxon(Name) has more than 1 PESI source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2111
		}
2112
		*/
2113
		return sources;
2114
	}
2115
	
2116
	// return all sources with a PESI reference	
2117
	private static Set<IdentifiableSource> filterPesiSources(Set<? extends IdentifiableSource> sources) {
2118
		Set<IdentifiableSource> result = new HashSet<IdentifiableSource>();
2119
		for (IdentifiableSource source : sources){
2120
			Reference ref = source.getCitation();
2121
			UUID refUuid = ref.getUuid();
2122
			if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed) || 
2123
				refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)||
2124
				refUuid.equals(PesiTransformer.uuidSourceRefErms)||
2125
				refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum) ||
2126
				refUuid.equals(PesiTransformer.uuidSourceRefAuct)){
2127
				result.add(source);
2128
			}
2129
		}
2130
		return result;
2131
	}
2132

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

    
2520
	
2521
	/**
2522
	 * Returns the CDM to PESI specific export mappings.
2523
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2524
	 */
2525
	private PesiExportMapping getMapping() {
2526
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2527
		
2528
		//mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2529
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2530
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2531
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2532
		
2533
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2534
		
2535
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2536
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2537
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misallied Names are handled differently
2538
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2539
		
2540
		// DisplayName
2541
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2542

    
2543
		// FossilStatus (Fk, Cache)
2544
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2545
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2546
		
2547
		//handled by name mapping
2548
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2549
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2550
		
2551
		//experts
2552
		ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
2553
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2554
		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
2555
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2556
		
2557
//		mapping.addMapper(MethodMapper.NewInstance("ParentTaxonFk", this, TaxonBase.class, PesiExportState.class));  //by AM, doesn't work, FK exception
2558
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonNameBase.class, "Name"));
2559
		
2560
		addNameMappers(mapping);
2561

    
2562
		return mapping;
2563
	}
2564
	
2565
	/**
2566
	 * Returns the CDM to PESI specific export mappings.
2567
	 * @param state 
2568
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2569
	 * @throws UndefinedTransformerMethodException 
2570
	 */
2571
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2572
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2573
		
2574
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2575

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

    
2578
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonNameBase.class));
2579
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonNameBase.class));
2580
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonNameBase.class, PesiExportState.class));
2581
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
2582
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2583
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));  
2584
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonNameBase.class));
2585
		
2586
		// DisplayName
2587
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonNameBase.class));
2588
		
2589
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2590
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2591
		
2592
		addNameMappers(mapping);
2593
		//TODO add author mapper, TypeNameFk
2594

    
2595
		return mapping;
2596
	}
2597

    
2598
	private void addNameMappers(PesiExportMapping mapping) {
2599
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2600
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2601
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2602
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2603
		
2604
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2605
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonNameBase.class));
2606
		
2607
//		mapping.addMapper(DbStringMapper.NewInstance("TitleCache", "FullName"));    //does not work as we need other cache strategy
2608
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonNameBase.class));
2609
		
2610
		
2611
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonNameBase.class));
2612
		
2613
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonNameBase.class));
2614
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2615
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonNameBase.class));
2616
		//TODO TypeNameFk
2617
		
2618
		//quality status
2619
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonNameBase.class));
2620
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2621
		
2622
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2623
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2624

    
2625
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2626

    
2627
	}
2628
	
2629
	private PesiExportMapping getSynRelMapping() {
2630
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2631
		
2632
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getTaxonFk1", RelationshipBase.class, PesiExportState.class));
2633
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk2", this.getClass(), "getTaxonFk2", RelationshipBase.class, PesiExportState.class));
2634
		mapping.addMapper(MethodMapper.NewInstance("RelTaxonQualifierFk", this,  RelationshipBase.class));
2635
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this, RelationshipBase.class, PesiExportState.class));
2636
		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2637

    
2638
		return mapping;
2639
	}
2640

    
2641
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state)  throws UndefinedTransformerMethodException{
2642
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2643
		
2644
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2645
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonNameBase.class, "Name"));
2646
		
2647
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2648
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2649

    
2650
		//we have only nomenclatural references here
2651
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2652
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2653
		
2654
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2655
		
2656
		return mapping;
2657
	}
2658

    
2659
}
(11-11/12)