Project

General

Profile

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
338
				
339
				
340
			}
341
			
342

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

    
374

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1232

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1642

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2594
		return mapping;
2595
	}
2596

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

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

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

    
2637
		return mapping;
2638
	}
2639

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

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

    
2658
}
(11-11/12)