Project

General

Profile

Download (104 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
		list = null;
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
			rs = null;
514
		} catch (SQLException e) {
515
			logger.warn ("Biota could not be requested or inserted");
516
		}
517
		
518
	}
519
	
520
	// 4th round: Add TreeIndex to each taxon
521
	private boolean doPhase04(PesiExportState state) {
522
		boolean success = true;
523
		
524
		logger.info("PHASE 4: Make TreeIndex ... ");
525
	
526
		//TODO test if possible to move to phase 02 
527
		String sql = " UPDATE Taxon SET ParentTaxonFk = (Select TaxonId from Taxon where RankFk = 0) " +
528
				" WHERE (RankFk = 10) and TaxonStatusFk = 1 ";
529
		state.getConfig().getDestination().update(sql);
530
		
531
		state.getConfig().getDestination().update("EXEC dbo.recalculateallstoredpaths");
532
		
533
		return success;
534
		
535
	}
536
	
537
	
538
	// 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
539
	private boolean doPhase02_OLD(PesiExportState state) {
540
		boolean success = true;
541
		if (! state.getConfig().isDoTreeIndex()){
542
			logger.info ("Ignore PHASE 2: ParentTaxonFk and TreeIndex");
543
			return success;
544
		}
545
		
546
		List<Classification> classificationList = null;
547
		logger.info("PHASE 2: Add ParenTaxonFk and TreeIndex...");
548
		
549
		// Specify starting ranks for tree traversing
550
		rankList.add(Rank.KINGDOM());
551
		rankList.add(Rank.GENUS());
552

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

    
566
		logger.info("Fetched " + classificationList.size() + " classification(s).");
567

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

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

    
579
				commitTransaction(txStatus);
580
				logger.debug("Committed transaction.");
581

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

    
591
					TaxonNode newNode = getTaxonNodeService().load(rootNode.getUuid());
592

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1158
		} else {
1159
			logger.error("Taxon is NULL for TaxonNode: " + childNode.getUuid());
1160
		}
1161
	}
1162

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

    
1182
			invokeParentTaxonFkAndTreeIndex(state.getDbId(parentTaxon), currentTaxonFk,	treeIndex);
1183
		}
1184
		
1185
	}
1186

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

    
1202
			if (treeIndex != null) {
1203
				parentTaxonFk_TreeIndex_KingdomFkStmt.setString(2, treeIndex.toString());
1204
			} else {
1205
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(2, null);
1206
			}
1207

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

    
1236

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

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

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

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

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

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

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

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

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

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

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

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

    
1635
 	
1636
	/**
1637
	 * Returns the <code>WebSearchName</code> attribute.
1638
	 * @param taxonName The {@link NonViralName NonViralName}.
1639
	 * @return The <code>WebSearchName</code> attribute.
1640
	 * @see MethodMapper
1641
	 */
1642
	@SuppressWarnings("unused")
1643
	private static String getWebSearchName(TaxonNameBase<?,?> taxonName) {
1644
		//TODO extensions?
1645
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1646
		NonViralNameDefaultCacheStrategy<NonViralName<?>> strategy = getCacheStrategy(nvn);
1647
		String result = strategy.getNameCache(nvn);
1648
		strategy = null;
1649
		nvn = null;
1650
		return result;
1651
	}
1652

    
1653

    
1654
	/**
1655
	 * Returns the <code>FullName</code> attribute.
1656
	 * @param taxonName The {@link NonViralName NonViralName}.
1657
	 * @return The <code>FullName</code> attribute.
1658
	 * @see MethodMapper
1659
	 */
1660
	@SuppressWarnings("unused")
1661
	private static String getFullName(TaxonNameBase taxonName) {
1662
		//TODO extensions?
1663
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1664
		String result = getCacheStrategy(nvn).getTitleCache(nvn);
1665
		Iterator<TaxonBase> taxa = taxonName.getTaxa().iterator();
1666
		if (taxonName.getTaxa().size() >0){
1667
			if (taxonName.getTaxa().size() == 1){
1668
				TaxonBase taxon = taxa.next();
1669
				if (isMisappliedName(taxon)){
1670
					result = result + " " + getAuthorString(taxon);
1671
				}
1672
				taxon = null;
1673
			}
1674
		}
1675
		taxa = null;
1676
		nvn = null;
1677
		return result;
1678
	}
1679
	
1680
	/**
1681
	 * Returns the SourceNameCache for the AdditionalSource table
1682
	 * @param taxonName
1683
	 * @return
1684
	 */
1685
	@SuppressWarnings("unused")
1686
	private static String getSourceNameCache(TaxonNameBase taxonName) {
1687
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1688
		if (nvn != null){
1689
			Reference<?> nomRef = (Reference)nvn.getNomenclaturalReference();
1690
			if (nomRef != null){
1691
				return nomRef.getAbbrevTitleCache();
1692
			}
1693
			
1694
		}
1695
		return null;
1696
	}
1697
	
1698
	
1699
	
1700
	/**
1701
	 * Returns the <code>FullName</code> attribute.
1702
	 * @param taxon The {@link TaxonBase taxon}.
1703
	 * @return The <code>FullName</code> attribute.
1704
	 * @see MethodMapper
1705
	 */
1706
	/*@SuppressWarnings("unused")
1707
	private static String getFullName(TaxonBase taxon) {
1708
		//TODO extensions?
1709
		TaxonNameBase name = taxon.getName();
1710
		String result = getFullName(name);
1711
		if (isMisappliedName(taxon)){
1712
			result = result + " " + getAuthorString(taxon);
1713
		}
1714
		
1715
		return result;
1716
	}
1717
*/
1718
	
1719
	/**
1720
	 * Returns the nomenclatural reference which is the reference
1721
	 * including the detail (microreference).
1722
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1723
	 * @return The <code>AuthorString</code> attribute.
1724
	 * @see MethodMapper
1725
	 */
1726
	@SuppressWarnings("unused")
1727
	private static String getNomRefString(TaxonNameBase<?,?> taxonName) {
1728
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1729
		if (ref == null){
1730
			return null;
1731
		}
1732
		if (! ref.isProtectedAbbrevTitleCache()){
1733
			ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1734
		}
1735
		return ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1736
	}
1737
	
1738

    
1739
	/**
1740
	 * Returns the <code>NameStatusFk</code> attribute.
1741
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1742
	 * @return The <code>NameStatusFk</code> attribute.
1743
	 * @see MethodMapper
1744
	 */
1745
	@SuppressWarnings("unused")
1746
	private static Integer getNameStatusFk(TaxonNameBase<?,?> taxonName) {
1747
		Integer result = null;
1748

    
1749
		NomenclaturalStatus state = getNameStatus(taxonName);
1750
		if (state != null) {
1751
			result = PesiTransformer.nomStatus2nomStatusFk(state.getType());
1752
		}
1753
		return result;
1754
	}
1755
	
1756
	/**
1757
	 * Returns the <code>NameStatusCache</code> attribute.
1758
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1759
	 * @return The <code>NameStatusCache</code> attribute.
1760
	 * @throws UndefinedTransformerMethodException 
1761
	 * @see MethodMapper
1762
	 */
1763
	@SuppressWarnings("unused")
1764
	private static String getNameStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1765
		String result = null;
1766
		NomenclaturalStatus status = getNameStatus(taxonName);
1767
		if (status != null) {
1768
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1769
		}
1770
		return result;
1771
	}
1772
	
1773
	
1774
	private static NomenclaturalStatus getNameStatus(TaxonNameBase<?,?> taxonName) {
1775
		try {
1776
			if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1777
				NonViralName<?> nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1778
				Set<NomenclaturalStatus> states = nonViralName.getStatus();
1779
				if (states.size() == 1) {
1780
					NomenclaturalStatus status = states.iterator().next();
1781
					return status;
1782
				} else if (states.size() > 1) {
1783
					logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1784
				}
1785
			}
1786
		
1787
		} catch (Exception e) {
1788
			e.printStackTrace();
1789
		}
1790
		return null;
1791
	}
1792
	/**
1793
	 * Returns the <code>TaxonStatusFk</code> attribute.
1794
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1795
	 * @param state The {@link PesiExportState PesiExportState}.
1796
	 * @return The <code>TaxonStatusFk</code> attribute.
1797
	 * @see MethodMapper
1798
	 */
1799
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1800
		Integer result = null;
1801
		
1802
		try {
1803
			if (isMisappliedName(taxon)) {
1804
				Synonym synonym = Synonym.NewInstance(null, null);
1805
				
1806
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1807
				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1808
			} else {
1809
				result = PesiTransformer.taxonBase2statusFk(taxon);
1810
			}
1811
		
1812
		} catch (Exception e) {
1813
			e.printStackTrace();
1814
		}
1815
		return result;
1816
	}
1817
	
1818
	/**
1819
	 * Returns the <code>TaxonStatusCache</code> attribute.
1820
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1821
	 * @param state The {@link PesiExportState PesiExportState}.
1822
	 * @return The <code>TaxonStatusCache</code> attribute.
1823
	 * @throws UndefinedTransformerMethodException 
1824
	 * @see MethodMapper
1825
	 */
1826
	@SuppressWarnings("unused")
1827
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1828
		return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1829
	}
1830
	
1831
	/**
1832
	 * Returns the <code>TypeNameFk</code> attribute.
1833
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1834
	 * @param state The {@link PesiExportState PesiExportState}.
1835
	 * @return The <code>TypeNameFk</code> attribute.
1836
	 * @see MethodMapper
1837
	 */
1838
	private static Integer getTypeNameFk(TaxonNameBase<?,?> taxonNameBase, PesiExportState state) {
1839
		Integer result = null;
1840
		if (taxonNameBase != null) {
1841
			Set<NameTypeDesignation> nameTypeDesignations = taxonNameBase.getNameTypeDesignations();
1842
			if (nameTypeDesignations.size() == 1) {
1843
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1844
				if (nameTypeDesignation != null) {
1845
					TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1846
					if (typeName != null) {
1847
						result = state.getDbId(typeName);
1848
					}
1849
				}
1850
			} else if (nameTypeDesignations.size() > 1) {
1851
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonNameBase.getUuid() + " (" + taxonNameBase.getTitleCache() + ")");
1852
			}
1853
		}
1854
		return result;
1855
	}
1856
	
1857
	/**
1858
	 * Returns the <code>TypeFullnameCache</code> attribute.
1859
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1860
	 * @return The <code>TypeFullnameCache</code> attribute.
1861
	 * @see MethodMapper
1862
	 */
1863
	@SuppressWarnings("unused")
1864
	private static String getTypeFullnameCache(TaxonNameBase<?,?> taxonName) {
1865
		String result = null;
1866
		
1867
		try {
1868
		if (taxonName != null) {
1869
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1870
			if (nameTypeDesignations.size() == 1) {
1871
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1872
				if (nameTypeDesignation != null) {
1873
					TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1874
					if (typeName != null) {
1875
						result = typeName.getTitleCache();
1876
					}
1877
				}
1878
			} else if (nameTypeDesignations.size() > 1) {
1879
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1880
			}
1881
		}
1882
		
1883
		} catch (Exception e) {
1884
			e.printStackTrace();
1885
		}
1886
		return result;
1887
	}
1888

    
1889
	
1890
	/**
1891
	 * Returns the <code>QualityStatusFk</code> attribute.
1892
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1893
	 * @return The <code>QualityStatusFk</code> attribute.
1894
	 * @see MethodMapper
1895
	 */
1896
	private static Integer getQualityStatusFk(TaxonNameBase taxonName) {
1897
		BitSet sources = getSources(taxonName);
1898
		return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1899
	}
1900

    
1901
	
1902
	/**
1903
	 * Returns the <code>QualityStatusCache</code> attribute.
1904
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1905
	 * @return The <code>QualityStatusCache</code> attribute.
1906
	 * @throws UndefinedTransformerMethodException 
1907
	 * @see MethodMapper
1908
	 */
1909
	@SuppressWarnings("unused")
1910
	private static String getQualityStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1911
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1912
	}
1913

    
1914
	
1915
	/**
1916
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1917
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1918
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1919
	 * @see MethodMapper
1920
	 */
1921
	@SuppressWarnings("unused")
1922
	private static Integer getTypeDesignationStatusFk(TaxonNameBase<?,?> taxonName) {
1923
		Integer result = null;
1924
		
1925
		try {
1926
		if (taxonName != null) {
1927
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1928
			if (typeDesignations.size() == 1) {
1929
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1930
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1931
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1932
			} else if (typeDesignations.size() > 1) {
1933
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1934
			}
1935
		}
1936
		
1937
		} catch (Exception e) {
1938
			e.printStackTrace();
1939
		}
1940
		return result;
1941
	}
1942

    
1943
	/**
1944
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1945
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1946
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1947
	 * @see MethodMapper
1948
	 */
1949
	@SuppressWarnings("unused")
1950
	private static String getTypeDesignationStatusCache(TaxonNameBase<?,?> taxonName) {
1951
		String result = null;
1952
		
1953
		try {
1954
		if (taxonName != null) {
1955
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1956
			if (typeDesignations.size() == 1) {
1957
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1958
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1959
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
1960
			} else if (typeDesignations.size() > 1) {
1961
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1962
			}
1963
		}
1964
		
1965
		} catch (Exception e) {
1966
			e.printStackTrace();
1967
		}
1968
		return result;
1969
	}
1970
	
1971
	/**
1972
	 * Returns the <code>FossilStatusFk</code> attribute.
1973
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1974
	 * @return The <code>FossilStatusFk</code> attribute.
1975
	 * @see MethodMapper
1976
	 */
1977
	@SuppressWarnings("unused")
1978
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
1979
		Integer result = null;
1980
		
1981
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
1982
		if (fossilStatuus.size() == 0){
1983
			return null;
1984
		}else if (fossilStatuus.size() > 1){
1985
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
1986
		}
1987
		String fossilStatus = fossilStatuus.iterator().next();
1988
		
1989
		int statusFk = state.getTransformer().FossilStatusCache2FossilStatusFk(fossilStatus);
1990
		return statusFk;
1991
	}
1992
	
1993
	/**
1994
	 * Returns the <code>FossilStatusCache</code> attribute.
1995
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1996
	 * @return The <code>FossilStatusCache</code> attribute.
1997
	 * @see MethodMapper
1998
	 */
1999
	@SuppressWarnings("unused")
2000
	private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
2001
		String result = null;
2002
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
2003
		if (fossilStatuus.size() == 0){
2004
			return null;
2005
		}
2006
		for (String strFossilStatus : fossilStatuus){
2007
			result = CdmUtils.concat(";", result, strFossilStatus);
2008
		}
2009
		return result;
2010
	}
2011
	
2012
	/**
2013
	 * Returns the <code>IdInSource</code> attribute.
2014
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2015
	 * @return The <code>IdInSource</code> attribute.
2016
	 * @see MethodMapper
2017
	 */
2018
	@SuppressWarnings("unused")
2019
	private static String getIdInSource(IdentifiableEntity taxonName) {
2020
		String result = null;
2021
		
2022
		try {
2023
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
2024
			if (sources.size() > 1){
2025
				logger.warn("There is > 1 Pesi source. This is not yet handled.");
2026
			}
2027
			if (sources.size() == 0){
2028
				logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2029
			}
2030
			for (IdentifiableSource source : sources) {
2031
				Reference<?> ref = source.getCitation();
2032
				UUID refUuid = ref.getUuid();
2033
				String idInSource = source.getIdInSource();
2034
				if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
2035
					result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
2036
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2037
					result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
2038
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2039
					result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
2040
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
2041
					result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
2042
				}else{
2043
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2044
				}
2045
				
2046
				String sourceIdNameSpace = source.getIdNamespace();
2047
				if (sourceIdNameSpace != null) {
2048
					if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
2049
						result =  idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
2050
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
2051
						result =  idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
2052
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
2053
						result =  idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
2054
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
2055
						result =  idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
2056
					} 
2057
				}
2058
				if (result == null) {
2059
					logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
2060
				}
2061
			}
2062
		} catch (Exception e) {
2063
			e.printStackTrace();
2064
			logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
2065
		}
2066

    
2067
		if (result == null) {
2068
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2069
		}
2070
		return result;
2071
	}
2072
	
2073
	/**
2074
	 * Returns the idInSource for a given TaxonName only.
2075
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2076
	 * @return The idInSource.
2077
	 */
2078
	private static String getIdInSourceOnly(IdentifiableEntity identEntity) {
2079
		String result = null;
2080
		
2081
		// Get the sources first
2082
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2083

    
2084
		// Determine the idInSource
2085
		if (sources.size() == 1) {
2086
			IdentifiableSource source = sources.iterator().next();
2087
			if (source != null) {
2088
				result = source.getIdInSource();
2089
			}
2090
		} else if (sources.size() > 1) {
2091
			int count = 1;
2092
			result = "";
2093
			for (IdentifiableSource source : sources) {
2094
				result += source.getIdInSource();
2095
				if (count < sources.size()) {
2096
					result += "; ";
2097
				}
2098
				count++;
2099
			}
2100

    
2101
		}
2102
		
2103
		return result;
2104
	}
2105
	
2106
	/**
2107
	 * Returns the Sources for a given TaxonName only.
2108
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2109
	 * @return The Sources.
2110
	 */
2111
	private static Set<IdentifiableSource> getPesiSources(IdentifiableEntity identEntity) {
2112
		Set<IdentifiableSource> sources = new java.util.HashSet<IdentifiableSource>();
2113

    
2114
		//Taxon Names
2115
		if (identEntity.isInstanceOf(TaxonNameBase.class)){
2116
			// Sources from TaxonName
2117
			TaxonNameBase taxonName = CdmBase.deproxy(identEntity, TaxonNameBase.class);
2118
			Set<IdentifiableSource> testSources = identEntity.getSources();
2119
			sources = filterPesiSources(identEntity.getSources());
2120
			
2121
			if (sources.size() == 0 && testSources.size()>0){
2122
				IdentifiableSource source = testSources.iterator().next();
2123
				logger.warn("There are sources, but they are no pesi sources!!!" + source.getIdInSource() + " - " + source.getIdNamespace() + " - " + source.getCitation().getTitleCache());
2124
			}
2125
			if (sources.size() > 1) {
2126
				logger.warn("This TaxonName has more than one Source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() + ")");
2127
			}
2128
			
2129
			// name has no PESI source, take sources from TaxonBase
2130
			if (sources == null || sources.isEmpty()) {
2131
				Set<TaxonBase> taxa = taxonName.getTaxonBases();
2132
				for (TaxonBase taxonBase: taxa){
2133
					sources.addAll(filterPesiSources(taxonBase.getSources()));
2134
				}
2135
			}
2136

    
2137
		//for TaxonBases
2138
		}else if (identEntity.isInstanceOf(TaxonBase.class)){
2139
			sources = filterPesiSources(identEntity.getSources());	
2140
		}
2141

    
2142
		/*TODO: deleted only for testing the inferred synonyms 
2143
		if (sources == null || sources.isEmpty()) {
2144
			logger.warn("This TaxonName has no PESI Sources: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2145
		}else if (sources.size() > 1){
2146
			logger.warn("This Taxon(Name) has more than 1 PESI source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2147
		}
2148
		*/
2149
		return sources;
2150
	}
2151
	
2152
	// return all sources with a PESI reference	
2153
	private static Set<IdentifiableSource> filterPesiSources(Set<? extends IdentifiableSource> sources) {
2154
		Set<IdentifiableSource> result = new HashSet<IdentifiableSource>();
2155
		for (IdentifiableSource source : sources){
2156
			Reference ref = source.getCitation();
2157
			UUID refUuid = ref.getUuid();
2158
			if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed) || 
2159
				refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)||
2160
				refUuid.equals(PesiTransformer.uuidSourceRefErms)||
2161
				refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum) ||
2162
				refUuid.equals(PesiTransformer.uuidSourceRefAuct)){
2163
				result.add(source);
2164
			}
2165
		}
2166
		return result;
2167
	}
2168

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

    
2556
	
2557
	/**
2558
	 * Returns the CDM to PESI specific export mappings.
2559
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2560
	 */
2561
	private PesiExportMapping getMapping() {
2562
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2563
		
2564
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2565
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2566
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2567
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2568
		
2569
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2570
		
2571
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2572
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2573
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misallied Names are handled differently
2574
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2575
		
2576
		// DisplayName
2577
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2578

    
2579
		// FossilStatus (Fk, Cache)
2580
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2581
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2582
		
2583
		//handled by name mapping
2584
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2585
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2586
		
2587
		//experts
2588
		ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
2589
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2590
		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
2591
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2592
		
2593
//		mapping.addMapper(MethodMapper.NewInstance("ParentTaxonFk", this, TaxonBase.class, PesiExportState.class));  //by AM, doesn't work, FK exception
2594
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonNameBase.class, "Name"));
2595
		
2596
		addNameMappers(mapping);
2597

    
2598
		return mapping;
2599
	}
2600
	
2601
	/**
2602
	 * Returns the CDM to PESI specific export mappings.
2603
	 * @param state 
2604
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2605
	 * @throws UndefinedTransformerMethodException 
2606
	 */
2607
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2608
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2609
		
2610
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2611

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

    
2614
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonNameBase.class));
2615
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonNameBase.class));
2616
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonNameBase.class, PesiExportState.class));
2617
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
2618
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2619
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));  
2620
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonNameBase.class));
2621
		mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonNameBase.class));
2622
		
2623
		
2624
		
2625
		// DisplayName
2626
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonNameBase.class));
2627
		
2628
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2629
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2630
		
2631
		addNameMappers(mapping);
2632
		//TODO add author mapper, TypeNameFk
2633

    
2634
		return mapping;
2635
	}
2636

    
2637
	private void addNameMappers(PesiExportMapping mapping) {
2638
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2639
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2640
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2641
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2642
		
2643
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2644
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonNameBase.class));
2645
		
2646
//		mapping.addMapper(DbStringMapper.NewInstance("TitleCache", "FullName"));    //does not work as we need other cache strategy
2647
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonNameBase.class));
2648
		
2649
		
2650
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonNameBase.class));
2651
		
2652
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonNameBase.class));
2653
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2654
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonNameBase.class));
2655
		//TODO TypeNameFk
2656
		
2657
		//quality status
2658
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonNameBase.class));
2659
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2660
		
2661
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2662
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2663

    
2664
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2665

    
2666
	}
2667
	
2668
	private PesiExportMapping getSynRelMapping() {
2669
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2670
		
2671
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getTaxonFk1", RelationshipBase.class, PesiExportState.class));
2672
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk2", this.getClass(), "getTaxonFk2", RelationshipBase.class, PesiExportState.class));
2673
		mapping.addMapper(MethodMapper.NewInstance("RelTaxonQualifierFk", this,  RelationshipBase.class));
2674
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this, RelationshipBase.class, PesiExportState.class));
2675
		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2676

    
2677
		return mapping;
2678
	}
2679

    
2680
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state)  throws UndefinedTransformerMethodException{
2681
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2682
		
2683
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2684
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonNameBase.class, "Name"));
2685
		
2686
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2687
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2688
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonNameBase.class));
2689
		
2690

    
2691
		//we have only nomenclatural references here
2692
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2693
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2694
		
2695
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2696
		
2697
		return mapping;
2698
	}
2699

    
2700
}
(11-11/12)