Project

General

Profile

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

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

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

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

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

    
97
	private static int modCount = 1000;
98
	private static final String dbTableName = "Taxon";
99
	private static final String dbTableNameSynRel = "RelTaxon";
100
	private static final String dbTableAdditionalSourceRel = "AdditionalTaxonSource";
101
	private static final String pluralString = "Taxa";
102
	private static final String parentPluralString = "Taxa";
103
	private PreparedStatement parentTaxonFk_TreeIndex_KingdomFkStmt;
104
	private PreparedStatement parentTaxonFkStmt;
105
	private PreparedStatement rankTypeExpertsUpdateStmt;
106
	private PreparedStatement rankUpdateStmt;
107
	private NomenclaturalCode nomenclaturalCode;
108
	private Integer kingdomFk;
109
	private final HashMap<Rank, Rank> rank2endRankMap = new HashMap<Rank, Rank>();
110
	private final List<Rank> rankList = new ArrayList<Rank>();
111
	private static final UUID uuidTreeIndex = UUID.fromString("28f4e205-1d02-4d3a-8288-775ea8413009");
112
	private AnnotationType treeIndexAnnotationType;
113
	private static ExtensionType lastActionExtensionType;
114
	private static ExtensionType lastActionDateExtensionType;
115
	private static ExtensionType expertNameExtensionType;
116
	private static ExtensionType speciesExpertNameExtensionType;
117
	private static ExtensionType cacheCitationExtensionType;
118
	public static NonViralNameDefaultCacheStrategy<?> zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
119
	public static NonViralNameDefaultCacheStrategy<?> botanicalNameStrategy = BotanicNameDefaultCacheStrategy.NewInstance();
120
	public static NonViralNameDefaultCacheStrategy<?> nonViralNameStrategy = NonViralNameDefaultCacheStrategy.NewInstance();
121
	public static NonViralNameDefaultCacheStrategy<?> bacterialNameStrategy = BacterialNameDefaultCacheStrategy.NewInstance();
122
	private static int currentTaxonId;
123

    
124

    
125
	/**
126
	 * @return the treeIndexAnnotationType
127
	 */
128
	protected AnnotationType getTreeIndexAnnotationType() {
129
		return treeIndexAnnotationType;
130
	}
131

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

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

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

    
151
	@Override
152
	public Class<? extends CdmBase> getStandardMethodParameter() {
153
		return standardMethodParameter;
154
	}
155

    
156
	@Override
157
	protected boolean doCheck(PesiExportState state) {
158
		boolean result = true;
159
		return result;
160
	}
161

    
162

    
163
	@Override
164
	protected void doInvoke(PesiExportState state) {
165
		try {
166
			logger.info("*** Started Making " + pluralString + " ...");
167

    
168
			initPreparedStatements(state);
169

    
170
			// Stores whether this invoke was successful or not.
171
			boolean success = true;
172

    
173
			// PESI: Clear the database table Taxon.
174
//			doDelete(state);
175

    
176
			// Get specific mappings: (CDM) Taxon -> (PESI) Taxon
177
			PesiExportMapping mapping = getMapping();
178
			PesiExportMapping synonymRelMapping = getSynRelMapping();
179
			PesiExportMapping additionalSourceMapping = getAdditionalSourceMapping(state);
180

    
181
			// Initialize the db mapper
182
			mapping.initialize(state);
183
			synonymRelMapping.initialize(state);
184
			additionalSourceMapping.initialize(state);
185

    
186
			// Find extensionTypes
187
			lastActionExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionUuid);
188
			lastActionDateExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionDateUuid);
189
			expertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
190
			speciesExpertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
191
			cacheCitationExtensionType = (ExtensionType)getTermService().find(PesiTransformer.cacheCitationUuid);
192

    
193
			//Export Taxa..
194
			success &= doPhase01(state, mapping, additionalSourceMapping);
195

    
196
			//"PHASE 1b: Handle names without taxa ...
197
			success &= doNames(state, additionalSourceMapping);
198

    
199

    
200
			// 2nd Round: Add ParentTaxonFk to each taxon
201
			success &= doPhase02(state);
202

    
203
			//PHASE 3: Add Rank data, KingdomFk, TypeNameFk ...
204
			success &= doPhase03(state);
205

    
206
			// 4nd Round: Add TreeIndex to each taxon
207
			success &= doPhase04(state);
208

    
209

    
210
			//"PHASE 4: Creating Inferred Synonyms...
211
			success &= doPhase05(state, mapping, synonymRelMapping);
212

    
213
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
214

    
215
			if (!success){
216
				state.getResult().addError("An error occurred in PesiTaxonExport.doInvoke");
217
			}
218
			return;
219
		} catch (Exception e) {
220
			e.printStackTrace();
221
			logger.error(e.getMessage());
222
			state.getResult().addException(e);
223
		}
224
	}
225

    
226

    
227
	private void initPreparedStatements(PesiExportState state) throws SQLException {
228
		initTreeIndexStatement(state);
229
		initRankExpertsUpdateStmt(state);
230
		initRankUpdateStatement(state);
231

    
232
		initParentFkStatement(state);
233
	}
234

    
235
	// Prepare TreeIndex-And-KingdomFk-Statement
236
	private void initTreeIndexStatement(PesiExportState state) throws SQLException {
237
		Connection connection = state.getConfig().getDestination().getConnection();
238
		String parentTaxonFk_TreeIndex_KingdomFkSql = "UPDATE Taxon SET ParentTaxonFk = ?, TreeIndex = ? WHERE TaxonId = ?";
239
		parentTaxonFk_TreeIndex_KingdomFkStmt = connection.prepareStatement(parentTaxonFk_TreeIndex_KingdomFkSql);
240
	}
241

    
242
	// Prepare TreeIndex-And-KingdomFk-Statement
243
	private void initParentFkStatement(PesiExportState state) throws SQLException {
244
		Connection connection = state.getConfig().getDestination().getConnection();
245
		String parentTaxonFkSql = "UPDATE Taxon SET ParentTaxonFk = ? WHERE TaxonId = ?";
246
		parentTaxonFkStmt = connection.prepareStatement(parentTaxonFkSql);
247
	}
248

    
249
	private void initRankUpdateStatement(PesiExportState state) throws SQLException {
250
		Connection connection = state.getConfig().getDestination().getConnection();
251
		String rankSql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, KingdomFk = ? WHERE TaxonId = ?";
252
		rankUpdateStmt = connection.prepareStatement(rankSql);
253
	}
254

    
255
	private void initRankExpertsUpdateStmt(PesiExportState state) throws SQLException {
256
//		String sql_old = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ?, " +
257
//				"ExpertFk = ?, SpeciesExpertFk = ? WHERE TaxonId = ?";
258
		//TODO handle experts GUIDs
259
		Connection connection = state.getConfig().getDestination().getConnection();
260

    
261
		String sql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ? " +
262
				" WHERE TaxonId = ?";
263
		rankTypeExpertsUpdateStmt = connection.prepareStatement(sql);
264
	}
265

    
266
	private boolean doPhase01(PesiExportState state, PesiExportMapping mapping, PesiExportMapping additionalSourceMapping) throws SQLException {
267
		int count = 0;
268
		int pastCount = 0;
269
		List<TaxonBase> list;
270
		boolean success = true;
271
		// Get the limit for objects to save within a single transaction.
272
		int limit = state.getConfig().getLimitSave();
273

    
274

    
275
		logger.info("PHASE 1: Export Taxa...limit is " + limit);
276
		// Start transaction
277
		TransactionStatus txStatus = startTransaction(true);
278
		logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
279

    
280

    
281

    
282
		int partitionCount = 0;
283

    
284
		logger.info("Taking snapshot at the beginning of phase 1 of taxonExport");
285
		//ProfilerController.memorySnapshot();
286
		while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)) != null   ) {
287

    
288
			logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
289

    
290
			for (TaxonBase<?> taxon : list) {
291
				doCount(count++, modCount, pluralString);
292
				TaxonNameBase<?,?> taxonName = taxon.getName();
293

    
294
				NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
295
				System.err.println(nvn.getTitleCache());
296
				if (! nvn.isProtectedTitleCache()){
297
					nvn.setTitleCache(null, false);
298
				}
299
				if (! nvn.isProtectedNameCache()){
300
					nvn.setNameCache(null, false);
301
				}
302
				if (! nvn.isProtectedFullTitleCache()){
303
					nvn.setFullTitleCache(null, false);
304
				}
305
				if (! nvn.isProtectedAuthorshipCache()){
306
					nvn.setAuthorshipCache(null, false);
307
				}
308
				try{
309
				if (nvn.getRank().equals(Rank.KINGDOM())){
310
				    String treeIndex = ((Taxon)taxon).getTaxonNodes().iterator().next().treeIndex();
311
				    Integer kingdomId = PesiTransformer.pesiKingdomMap.get(nvn.getGenusOrUninomial());
312
				    state.getTreeIndexKingdomMap().put(treeIndex, kingdomId);
313
				}}catch(NullPointerException e){
314
				    logger.error(nvn.getTitleCache() + "has no Rank!");
315
				    System.err.println(nvn.getTitleCache() + "has no Rank!");
316
				}
317
				//core mapping
318
				success &= mapping.invoke(taxon);
319
				//additional source
320
				if (nvn.getNomenclaturalReference() != null || StringUtils.isNotBlank(nvn.getNomenclaturalMicroReference() )){
321
					additionalSourceMapping.invoke(taxon);
322
				}
323

    
324
				validatePhaseOne(taxon, nvn);
325
				taxon = null;
326
				nvn = null;
327
				taxonName = null;
328

    
329

    
330

    
331
			}
332

    
333

    
334
			// Commit transaction
335
			commitTransaction(txStatus);
336
			logger.debug("Committed transaction.");
337
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
338
			pastCount = count;
339
			/*logger.warn("Taking snapshot at the end of the loop of phase 1 of taxonExport");
340
			//ProfilerController.memorySnapshot();
341
			*/
342
			// Start transaction
343
			txStatus = startTransaction(true);
344
			logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
345

    
346
		}
347
		if (list == null ) {
348
			logger.info("No " + pluralString + " left to fetch.");
349
		}
350

    
351

    
352

    
353
		// Commit transaction
354
		commitTransaction(txStatus);
355
		txStatus = null;
356
		logger.debug("Committed transaction.");
357
		list = null;
358
		if (logger.isDebugEnabled()){
359
			logger.debug("Taking snapshot at the end of phase 1 of taxonExport");
360
			ProfilerController.memorySnapshot();
361
		}
362
		return success;
363
	}
364

    
365

    
366
	private void validatePhaseOne(TaxonBase<?> taxon, NonViralName taxonName) {
367
		// Check whether some rules are violated
368
		nomenclaturalCode = taxonName.getNomenclaturalCode();
369
		String genusOrUninomial = taxonName.getGenusOrUninomial();
370
		String specificEpithet = taxonName.getSpecificEpithet();
371
		String infraSpecificEpithet = taxonName.getInfraSpecificEpithet();
372
		String infraGenericEpithet = taxonName.getInfraGenericEpithet();
373
		Integer rank = getRankFk(taxonName, nomenclaturalCode);
374

    
375
		if (rank == null) {
376
			logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
377
		} else {
378

    
379
			// Check whether infraGenericEpithet is set correctly
380
			// 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
381
			// 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
382

    
383
			int ancestorLevel = 0;
384
			if (taxonName.getRank().equals(Rank.SUBSPECIES())) {
385
				// The accepted taxon two rank levels above should be of rank subgenus
386
				ancestorLevel  = 2;
387
			}
388
			if (taxonName.getRank().equals(Rank.SPECIES())) {
389
				// The accepted taxon one rank level above should be of rank subgenus
390
				ancestorLevel = 1;
391
			}
392
			if (ancestorLevel > 0) {
393
				if (validateAncestorOfSpecificRank(taxon, ancestorLevel, Rank.SUBGENUS())) {
394
					// The child (species or subspecies) of this parent (subgenus) has to have an infraGenericEpithet
395
					if (infraGenericEpithet == null) {
396
						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() + ")");
397
						// maybe the taxon could be named here
398
					}
399
				}
400
			}
401

    
402
			if (infraGenericEpithet == null && rank.intValue() == 190) {
403
				logger.warn("InfraGenericEpithet was not determined although it should exist for rank 190: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
404
			}
405
			if (specificEpithet != null && rank.intValue() < 216) {
406
				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() + ")");
407
			}
408
			if (infraSpecificEpithet != null && rank.intValue() < 225) {
409
				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() + ")";
410
				if (StringUtils.isNotBlank(infraSpecificEpithet)){
411
					logger.warn(message);
412
				}else{
413
					logger.warn(message);
414
				}
415
			}
416
		}
417
		if (infraSpecificEpithet != null && specificEpithet == null) {
418
			logger.warn("An infraSpecificEpithet was determined, but a specificEpithet was not determined: "  + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
419
		}
420
		if (genusOrUninomial == null) {
421
			logger.warn("GenusOrUninomial was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
422
		}
423
	}
424

    
425

    
426

    
427
	/**
428
	 * 2nd Round: Add ParentTaxonFk to each taxon and add Biota if not exists
429
	 * @param state
430
	 * @return
431
	 */
432
	private boolean doPhase02(PesiExportState state) {
433
		int count = 0;
434
		int pastCount = 0;
435
		boolean success = true;
436
		if (! state.getConfig().isDoParentAndBiota()){
437
			logger.info ("Ignore PHASE 2: Make ParentFk and Biota...");
438
			return success;
439
		}
440

    
441
		List<Taxon> list;
442

    
443
		// Get the limit for objects to save within a single transaction.
444
		int limit = state.getConfig().getLimitSave();
445

    
446
		insertBiota(state);
447

    
448
		logger.info("PHASE 2: Make ParentFk and Biota ... limit is " + limit);
449
		// Start transaction
450
		TransactionStatus txStatus = startTransaction(true);
451
		int partitionCount = 0;
452

    
453
//		ProfilerController.memorySnapshot();
454
		while ((list = getNextTaxonPartition(Taxon.class, limit, partitionCount++, null)) != null   ) {
455

    
456
			logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
457
			for (Taxon taxon : list) {
458
				for (TaxonNode node : taxon.getTaxonNodes()){
459
					doCount(count++, modCount, pluralString);
460
					TaxonNode parentNode = node.getParent();
461
					if (parentNode != null && parentNode.getTaxon() != null){  //new root node handling requires has root taxon with taxon == null
462
						int childId = state.getDbId( taxon);
463
						int parentId = state.getDbId(parentNode.getTaxon());
464
						success &= invokeParentTaxonFk(parentId, childId);
465
					}
466
				}
467

    
468
			}
469

    
470
			// Commit transaction
471
			commitTransaction(txStatus);
472
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
473
			pastCount = count;
474
			// Start transaction
475
			txStatus = startTransaction(true);
476
			logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
477

    
478
		}
479
		if (list == null ) {
480
			logger.info("No " + pluralString + " left to fetch.");
481
		}
482
		list = null;
483
		// Commit transaction
484
		commitTransaction(txStatus);
485

    
486
		return success;
487

    
488
	}
489

    
490
	/**
491
	 * Inserts the Biota Taxon if not yet exists.
492
	 * @param state
493
	 * @throws SQLException
494
	 */
495
	private void insertBiota(PesiExportState state) {
496
		try {
497
			ResultSet rs = state.getConfig().getDestination().getResultSet("SELECT * FROM Taxon WHERE GenusOrUninomial = 'Biota' ");
498
			if (rs.next() == false){
499
				int biotaId = state.getConfig().getNameIdStart() -1 ;
500
				String sqlInsertBiota = "INSERT INTO Taxon (TaxonId, KingdomFk, RankFk, RankCache, GenusOrUninomial, WebSearchName, WebShowName, FullName, DisplayName, TaxonStatusFk, TaxonStatusCache) " +
501
									       " VALUES (" + biotaId + ",    0,    0,   'Superdomain',   'Biota',          'Biota',  '<i>Biota</i>',   'Biota', '<i>Biota</i>',  1 ,      'accepted')";
502
				state.getConfig().getDestination().update(sqlInsertBiota);
503
			}
504
			rs = null;
505
		} catch (SQLException e) {
506
			logger.warn ("Biota could not be requested or inserted");
507
		}
508

    
509
	}
510

    
511
	// 4th round: Add TreeIndex to each taxon
512
	private boolean doPhase04(PesiExportState state) {
513
		boolean success = true;
514

    
515
		logger.info("PHASE 4: Make TreeIndex ... ");
516

    
517
		//TODO test if possible to move to phase 02
518
		String sql = " UPDATE Taxon SET ParentTaxonFk = (Select TaxonId from Taxon where RankFk = 0) " +
519
				" WHERE (RankFk = 10) and TaxonStatusFk = 1 ";
520
		state.getConfig().getDestination().update(sql);
521

    
522
		state.getConfig().getDestination().update("EXEC dbo.recalculateallstoredpaths");
523

    
524
		return success;
525

    
526
	}
527

    
528

    
529
	// 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
530
	private boolean doPhase02_OLD(PesiExportState state) {
531
		boolean success = true;
532
		if (! state.getConfig().isDoTreeIndex()){
533
			logger.info ("Ignore PHASE 2: ParentTaxonFk and TreeIndex");
534
			return success;
535
		}
536

    
537
		List<Classification> classificationList = null;
538
		logger.info("PHASE 2: Add ParenTaxonFk and TreeIndex...");
539

    
540
		// Specify starting ranks for tree traversing
541
		rankList.add(Rank.KINGDOM());
542
		rankList.add(Rank.GENUS());
543

    
544
		// Specify where to stop traversing (value) when starting at a specific Rank (key)
545
		rank2endRankMap.put(Rank.GENUS(), null); // Since NULL does not match an existing Rank, traverse all the way down to the leaves
546
		rank2endRankMap.put(Rank.KINGDOM(), Rank.GENUS()); // excludes rank genus
547

    
548
		StringBuffer treeIndex = new StringBuffer();
549

    
550
		// Retrieve list of classifications
551
		TransactionStatus txStatus = startTransaction(true);
552
		logger.info("Started transaction for parentFk and treeIndex. Fetching all classifications...");
553
		classificationList = getClassificationService().listClassifications(null, 0, null, null);
554
		commitTransaction(txStatus);
555
		logger.debug("Committed transaction.");
556

    
557
		logger.info("Fetched " + classificationList.size() + " classification(s).");
558

    
559
		setTreeIndexAnnotationType(getAnnotationType(uuidTreeIndex, "TreeIndex", "TreeIndex", "TI"));
560
		List<TaxonNode> rankSpecificRootNodes;
561
		for (Classification classification : classificationList) {
562
			for (Rank rank : rankList) {
563

    
564
				txStatus = startTransaction(true);
565
				logger.info("Started transaction to fetch all rootNodes specific to Rank " + rank.getLabel() + " ...");
566

    
567
				rankSpecificRootNodes = getClassificationService().listRankSpecificRootNodes(classification, rank, null, null, null);
568
				logger.info("Fetched " + rankSpecificRootNodes.size() + " RootNodes for Rank " + rank.getLabel());
569

    
570
				commitTransaction(txStatus);
571
				logger.debug("Committed transaction.");
572

    
573
				for (TaxonNode rootNode : rankSpecificRootNodes) {
574
					txStatus = startTransaction(false);
575
					Rank endRank = rank2endRankMap.get(rank);
576
					if (endRank != null) {
577
						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till Rank " + endRank.getLabel() + " ...");
578
					} else {
579
						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till leaves are reached ...");
580
					}
581

    
582
					TaxonNode newNode = getTaxonNodeService().load(rootNode.getUuid());
583

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

    
624
					newNode = null;
625

    
626
					try {
627
						commitTransaction(txStatus);
628
						logger.debug("Committed transaction.");
629
					} catch (Exception e) {
630
						logger.error(e.getMessage());
631
						e.printStackTrace();
632
					}
633

    
634
				}
635
				rankSpecificRootNodes = null;
636
			}
637

    
638
		}
639

    
640
		logger.warn("Taking snapshot at the end of phase 2 of taxonExport");
641
		//ProfilerController.memorySnapshot();
642
		return success;
643
	}
644

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

    
657
		List<TaxonBase> list;
658
		logger.info("PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
659
		// Be sure to add rank information, KingdomFk, TypeNameFk, expertFk and speciesExpertFk to every taxonName
660

    
661
		// Start transaction
662
		TransactionStatus txStatus = startTransaction(true);
663
		logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
664
		int partitionCount = 0;
665
		while ((list = getNextTaxonPartition(TaxonBase.class, limit, partitionCount++, null)) != null) {
666

    
667
			logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
668
			for (TaxonBase<?> taxon : list) {
669
				TaxonNameBase<?,?> taxonName = taxon.getName();
670
				// Determine expertFk
671
//				Integer expertFk = makeExpertFk(state, taxonName);
672
//
673
//				// Determine speciesExpertFk
674
//				Integer speciesExpertFk = makeSpeciesExpertFk(state, taxonName);
675

    
676
				doCount(count++, modCount, pluralString);
677
				Integer typeNameFk = getTypeNameFk(taxonName, state);
678
				kingdomFk = findKingdomIdFromTreeIndex(taxon, state);
679
				 //       PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
680

    
681
				//TODO why are expertFks needed? (Andreas M.)
682
//				if (expertFk != null || speciesExpertFk != null) {
683
					invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, nomenclaturalCode, state.getDbId(taxon),
684
							typeNameFk, kingdomFk, state);
685
//				}
686

    
687
					taxon = null;
688
					taxonName = null;
689
			}
690

    
691
			// Commit transaction
692
			commitTransaction(txStatus);
693
			logger.debug("Committed transaction.");
694
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
695
			pastCount = count;
696

    
697
			// Start transaction
698
			txStatus = startTransaction(true);
699
			logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
700
		}
701
		if (list == null) {
702
			logger.info("No " + pluralString + " left to fetch.");
703
		}
704

    
705
		list = null;
706

    
707
		// Commit transaction
708
		commitTransaction(txStatus);
709

    
710
		logger.debug("Committed transaction.");
711
		logger.debug("Try to take snapshot at the end of phase 3 of taxonExport, number of partitions: " + partitionCount);
712
		//ProfilerController.memorySnapshot();
713
		return success;
714
	}
715

    
716
	/**
717
     * @param taxon
718
     * @return
719
     */
720
    private Integer findKingdomIdFromTreeIndex(TaxonBase<?> taxon,PesiExportState state) {
721
        if (taxon instanceof Taxon){
722
            Set<TaxonNode> nodes = ((Taxon)taxon).getTaxonNodes();
723
            if (!nodes.isEmpty()){
724
                String treeIndex = nodes.iterator().next().treeIndex();
725

    
726
                Pattern pattern = Pattern.compile("#t[0-9]+#[0-9]+#[0-9]+#");
727
                Matcher matcher = pattern.matcher(treeIndex);
728
                Integer kingdomID = null;
729
                if(matcher.find()) {
730
                    String treeIndexKingdom = matcher.group(0);
731
                    kingdomID = state.getTreeIndexKingdomMap().get(treeIndexKingdom);
732
                }
733
                return kingdomID;
734
            } else {
735
                logger.debug("The taxon has no nodes: " + taxon.getTitleCache() + " the kingdom is taken from the nomenclatural code: " + PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
736
                return PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
737
        }} else{
738
           return PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
739
        }
740
    }
741

    
742
    //	"PHASE 5: Creating Inferred Synonyms..."
743
	private boolean doPhase05(PesiExportState state, PesiExportMapping mapping, PesiExportMapping synRelMapping) throws SQLException {
744
		int count;
745
		int pastCount;
746
		boolean success = true;
747
		// Get the limit for objects to save within a single transaction.
748
		if (! state.getConfig().isDoInferredSynonyms()){
749
			logger.info ("Ignore PHASE 5: Creating Inferred Synonyms...");
750
			return success;
751
		}
752

    
753
		int limit = state.getConfig().getLimitSave();
754
		// Create inferred synonyms for accepted taxa
755
		logger.info("PHASE 4: Creating Inferred Synonyms...");
756

    
757
		// Determine the count of elements in datawarehouse database table Taxon
758
		currentTaxonId = determineTaxonCount(state);
759
		currentTaxonId++;
760

    
761
		count = 0;
762
		pastCount = 0;
763
		int pageSize = limit/10;
764
		int pageNumber = 1;
765
		String inferredSynonymPluralString = "Inferred Synonyms";
766

    
767
		// Start transaction
768
		TransactionStatus txStatus = startTransaction(true);
769
		logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
770
		List<TaxonBase> taxonList = null;
771

    
772

    
773

    
774
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SPECIES(), pageSize, pageNumber)).size() > 0) {
775
			HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
776

    
777
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
778
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
779
					synRelMapping, taxonList));
780

    
781
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
782
			// Commit transaction
783
			commitTransaction(txStatus);
784
			logger.debug("Committed transaction.");
785
			logger.info("Exported " + (taxonList.size()) + " " + inferredSynonymPluralString + ". Total: " + count);
786
			//pastCount = count;
787

    
788
			// Save Rank Data and KingdomFk for inferred synonyms
789
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
790
				invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomenclaturalCode, taxonFk, kingdomFk, state);
791
			}
792

    
793
			// Start transaction
794
			txStatus = startTransaction(true);
795
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
796

    
797
			// Increment pageNumber
798
			pageNumber++;
799
		}
800
		taxonList = null;
801
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SUBSPECIES(), pageSize, pageNumber)).size() > 0) {
802
			HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
803

    
804
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
805
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
806
					synRelMapping, taxonList));
807
			;
808
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
809
			// Commit transaction
810
			commitTransaction(txStatus);
811
			logger.debug("Committed transaction.");
812
			logger.info("Exported " + taxonList.size()+ " " + inferredSynonymPluralString + ". Total: " + count);
813
			//pastCount = count;
814

    
815
			// Save Rank Data and KingdomFk for inferred synonyms
816
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
817
				invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomenclaturalCode, taxonFk, kingdomFk, state);
818
			}
819

    
820
			// Start transaction
821
			txStatus = startTransaction(true);
822
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
823

    
824
			// Increment pageNumber
825
			pageNumber++;
826
			inferredSynonymsDataToBeSaved = null;
827
		}
828
		if (taxonList.size() == 0) {
829
			logger.info("No " + parentPluralString + " left to fetch.");
830
		}
831

    
832
		taxonList = null;
833
//		logger.warn("Taking snapshot at the end of phase 4 of taxonExport");
834
//		ProfilerController.memorySnapshot();
835

    
836
		// Commit transaction
837
		commitTransaction(txStatus);
838
		System.gc();
839
		logger.debug("Taking snapshot at the end of phase 4 after gc() of taxonExport");
840
		//ProfilerController.memorySnapshot();
841
		logger.debug("Committed transaction.");
842
		return success;
843
	}
844

    
845
	/**
846
	 * @param state
847
	 * @param mapping
848
	 * @param synRelMapping
849
	 * @param currentTaxonId
850
	 * @param taxonList
851
	 * @param inferredSynonymsDataToBeSaved
852
	 * @return
853
	 */
854
	private HashMap<Integer, TaxonNameBase<?, ?>> createInferredSynonymsForTaxonList(PesiExportState state,
855
			PesiExportMapping mapping, PesiExportMapping synRelMapping,	 List<TaxonBase> taxonList) {
856

    
857
		Taxon acceptedTaxon;
858
		Classification classification = null;
859
		List<Synonym> inferredSynonyms = null;
860
		boolean localSuccess = true;
861

    
862
		HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
863

    
864
		for (TaxonBase<?> taxonBase : taxonList) {
865

    
866
			if (taxonBase.isInstanceOf(Taxon.class)) { // this should always be the case since we should have fetched accepted taxon only, but you never know...
867
				acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
868
				TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();
869

    
870
				if (taxonName.isInstanceOf(ZoologicalName.class)) {
871
					nomenclaturalCode  = taxonName.getNomenclaturalCode();
872
					kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
873

    
874
					Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
875
					TaxonNode singleNode = null;
876

    
877
					if (taxonNodes.size() > 0) {
878
						// Determine the classification of the current TaxonNode
879

    
880
						singleNode = taxonNodes.iterator().next();
881
						if (singleNode != null) {
882
							classification = singleNode.getClassification();
883
						} else {
884
							logger.error("A TaxonNode belonging to this accepted Taxon is NULL: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() +")");
885
						}
886
					} else {
887
						// Classification could not be determined directly from this TaxonNode
888
						// The stored classification from another TaxonNode is used. It's a simple, but not a failsafe fallback solution.
889
						if (taxonNodes.size() == 0) {
890
							//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");
891

    
892
						}
893
					}
894

    
895
					if (classification != null) {
896
						try{
897
							TaxonNameBase name = acceptedTaxon.getName();
898
							//if (name.isSpecies() || name.isInfraSpecific()){
899
								inferredSynonyms  = getTaxonService().createAllInferredSynonyms(acceptedTaxon, classification, true);
900
							//}
901
//								inferredSynonyms = getTaxonService().createInferredSynonyms(classification, acceptedTaxon, SynonymType.INFERRED_GENUS_OF());
902
							if (inferredSynonyms != null) {
903
								for (Synonym synonym : inferredSynonyms) {
904
//									TaxonNameBase<?,?> synonymName = synonym.getName();
905
									MarkerType markerType =getUuidMarkerType(PesiTransformer.uuidMarkerGuidIsMissing, state);
906
									synonym.addMarker(Marker.NewInstance(markerType, true));
907
									// Both Synonym and its TaxonName have no valid Id yet
908
									synonym.setId(currentTaxonId++);
909

    
910

    
911
									localSuccess &= mapping.invoke(synonym);
912
									//get SynonymRelationship and export
913
									if (synonym.getAcceptedTaxon() == null ){
914
										IdentifiableSource source = synonym.getSources().iterator().next();
915
										if (source.getIdNamespace().contains("Potential combination")){
916
											acceptedTaxon.addSynonym(synonym, SynonymType.POTENTIAL_COMBINATION_OF());
917
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to potential combination");
918
										} else if (source.getIdNamespace().contains("Inferred Genus")){
919
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_GENUS_OF());
920
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred genus");
921
										} else if (source.getIdNamespace().contains("Inferred Epithet")){
922
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_EPITHET_OF());
923
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred epithet");
924
										} else{
925
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_SYNONYM_OF());
926
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred synonym");
927
										}
928

    
929
										localSuccess &= synRelMapping.invoke(synonym);
930
										if (!localSuccess) {
931
											logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
932
										}
933
									} else {
934
										localSuccess &= synRelMapping.invoke(synonym);
935
										if (!localSuccess) {
936
											logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
937
										} else {
938
											logger.info("Synonym relationship successfully exported: " + synonym.getTitleCache() + "  " +acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
939
										}
940
									}
941

    
942
									inferredSynonymsDataToBeSaved.put(synonym.getId(), synonym.getName());
943
								}
944
							}
945
						}catch(Exception e){
946
							logger.error(e.getMessage());
947
							e.printStackTrace();
948
						}
949
					} else {
950
						logger.error("Classification is NULL. Inferred Synonyms could not be created for this Taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() + ")");
951
					}
952
				} else {
953
//							logger.error("TaxonName is not a ZoologicalName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
954
				}
955
			} else {
956
				logger.error("This TaxonBase is not a Taxon even though it should be: " + taxonBase.getUuid() + " (" + taxonBase.getTitleCache() + ")");
957
			}
958
		}
959
		taxonList = null;
960
		return inferredSynonymsDataToBeSaved;
961
	}
962

    
963

    
964
	/**
965
	 * Handles names that do not appear in taxa
966
	 * @param state
967
	 * @param mapping
968
	 * @return
969
	 */
970
	private boolean doNames(PesiExportState state, PesiExportMapping additionalSourceMapping)  throws SQLException {
971

    
972
		boolean success = true;
973
		if (! state.getConfig().isDoPureNames()){
974
			logger.info ("Ignore PHASE 1b: PureNames");
975
			return success;
976
		}
977

    
978
		try {
979
			PesiExportMapping mapping = getPureNameMapping(state);
980
			mapping.initialize(state);
981
			int count = 0;
982
			int pastCount = 0;
983
			List<NonViralName<?>> list;
984
			success = true;
985
			// Get the limit for objects to save within a single transaction.
986
			int limit = state.getConfig().getLimitSave();
987

    
988

    
989
			logger.info("PHASE 1b: Export Pure Names ...");
990
			// Start transaction
991
			TransactionStatus txStatus = startTransaction(true);
992
			logger.info("Started new transaction for Pure Names. Fetching some " + pluralString + " (max: " + limit + ") ...");
993

    
994
			int partitionCount = 0;
995
			while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null   ) {
996

    
997
				logger.info("Fetched " + list.size() + " names without taxa. Exporting...");
998
				for (TaxonNameBase<?,?> taxonName : list) {
999
					doCount(count++, modCount, pluralString);
1000
					success &= mapping.invoke(taxonName);
1001
					//additional source
1002
					if (taxonName.getNomenclaturalReference() != null || StringUtils.isNotBlank(taxonName.getNomenclaturalMicroReference() )){
1003
						additionalSourceMapping.invoke(taxonName);
1004
					}
1005
				}
1006

    
1007
				// Commit transaction
1008
				commitTransaction(txStatus);
1009
				logger.debug("Committed transaction.");
1010
				logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
1011
				pastCount = count;
1012

    
1013
				// Start transaction
1014
				txStatus = startTransaction(true);
1015
				logger.info("Started new transaction for PureNames. Fetching some " + pluralString + " (max: " + limit + ") ...");
1016
			}
1017
			if (list == null) {
1018
				logger.info("No " + pluralString + " left to fetch.");
1019
			}
1020
			list = null;
1021
			// Commit transaction
1022
			commitTransaction(txStatus);
1023
			logger.debug("Committed transaction.");
1024
		} catch (Exception e) {
1025
			logger.error("Error occurred in pure name export");
1026
			e.printStackTrace();
1027
			success = false;
1028
		}
1029
		return success;
1030
	}
1031

    
1032
	/**
1033
	 * Determines the current number of entries in the DataWarehouse database table <code>Taxon</code>.
1034
	 * @param state The {@link PesiExportState PesiExportState}.
1035
	 * @return The count.
1036
	 */
1037
	private Integer determineTaxonCount(PesiExportState state) {
1038
		Integer result = null;
1039
		PesiExportConfigurator pesiConfig = state.getConfig();
1040

    
1041
		String sql;
1042
		Source destination =  pesiConfig.getDestination();
1043
		sql = "SELECT max(taxonId) FROM Taxon";
1044
		destination.setQuery(sql);
1045
		ResultSet resultSet = destination.getResultSet();
1046
		try {
1047
			resultSet.next();
1048
			result = resultSet.getInt(1);
1049
		} catch (SQLException e) {
1050
			logger.error("TaxonCount could not be determined: " + e.getMessage());
1051
			e.printStackTrace();
1052
		}
1053
		resultSet = null;
1054
		return result;
1055
	}
1056

    
1057
	/**
1058
	 * Checks whether a parent at specific level has a specific Rank.
1059
	 * @param taxonName A {@link TaxonNameBase TaxonName}.
1060
	 * @param level The ancestor level.
1061
	 * @param ancestorRank The ancestor rank.
1062
	 * @return Whether a parent at a specific level has a specific Rank.
1063
	 */
1064
	private boolean validateAncestorOfSpecificRank(TaxonBase<?> taxonBase, int level, Rank ancestorRank) {
1065
		boolean result = false;
1066
		TaxonNode parentNode = null;
1067
		if (taxonBase.isInstanceOf(Taxon.class)){
1068
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1069
			// Get ancestor Taxon via TaxonNode
1070
			Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();
1071
			if (taxonNodes.size() == 1) {
1072
				TaxonNode taxonNode = taxonNodes.iterator().next();
1073
				if (taxonNode != null) {
1074
					for (int i = 0; i < level; i++) {
1075
						if (taxonNode != null) {
1076
							taxonNode  = taxonNode.getParent();
1077
						}
1078
					}
1079
					parentNode = taxonNode;
1080
				}
1081
			} else if (taxonNodes.size() > 1) {
1082
				logger.error("This taxon has " + taxonNodes.size() + " taxonNodes: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1083
			}
1084
		}
1085
		//compare
1086
		if (parentNode != null) {
1087
			TaxonNode node = CdmBase.deproxy(parentNode, TaxonNode.class);
1088
			Taxon parentTaxon = node.getTaxon();
1089
			if (parentTaxon != null) {
1090
				TaxonNameBase<?,?> parentTaxonName = parentTaxon.getName();
1091
				if (parentTaxonName != null && parentTaxonName.getRank().equals(ancestorRank)) {
1092
					result = true;
1093
				}
1094
			} else if (parentNode.treeIndex().matches("#t\\d+#\\d+#")) {
1095
				//do nothing (is root node)
1096
			} else {
1097
				logger.error("This TaxonNode has no Taxon: " + node.getUuid());
1098
			}
1099
		}
1100
		return result;
1101
	}
1102

    
1103
	/**
1104
	 * Returns the AnnotationType for a given UUID.
1105
	 * @param uuid The Annotation UUID.
1106
	 * @param label The Annotation label.
1107
	 * @param text The Annotation text.
1108
	 * @param labelAbbrev The Annotation label abbreviation.
1109
	 * @return The AnnotationType.
1110
	 */
1111
	protected AnnotationType getAnnotationType(UUID uuid, String label, String text, String labelAbbrev){
1112
		AnnotationType annotationType = (AnnotationType)getTermService().find(uuid);
1113
		if (annotationType == null) {
1114
			annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
1115
			annotationType.setUuid(uuid);
1116
//			annotationType.setVocabulary(AnnotationType.EDITORIAL().getVocabulary());
1117
			getTermService().save(annotationType);
1118
		}
1119
		return annotationType;
1120
	}
1121

    
1122
	/**
1123
	 * Traverses the classification recursively and stores determined values for every Taxon.
1124
	 * @param childNode The {@link TaxonNode TaxonNode} to process.
1125
	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1126
	 * @param treeIndex The TreeIndex at the current level.
1127
	 * @param fetchLevel Rank to stop fetching at.
1128
	 * @param state The {@link PesiExportState PesiExportState}.
1129
	 */
1130
	private void traverseTree(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, Rank fetchLevel, PesiExportState state) {
1131
		// Traverse all branches from this childNode until specified fetchLevel is reached.
1132
		StringBuffer localTreeIndex = new StringBuffer(treeIndex);
1133
		Taxon childTaxon = childNode.getTaxon();
1134
		if (childTaxon != null) {
1135
			if (isPesiTaxon(childTaxon)){
1136
				Integer taxonId = state.getDbId(childTaxon);
1137
				TaxonNameBase<?,?> childName = childTaxon.getName();
1138
				if (taxonId != null) {
1139
					Rank childRank = childName.getRank();
1140
					if (childRank != null) {
1141
						if (! childRank.equals(fetchLevel)) {
1142

    
1143
							localTreeIndex.append(taxonId + "#");
1144

    
1145
							saveData(childNode, parentNode, localTreeIndex, state, taxonId);
1146

    
1147
							// Store treeIndex as annotation for further use
1148
							Annotation annotation = Annotation.NewInstance(localTreeIndex.toString(), getTreeIndexAnnotationType(), Language.DEFAULT());
1149
							childNode.addAnnotation(annotation);
1150

    
1151
							for (TaxonNode newNode : childNode.getChildNodes()) {
1152
								if (newNode.getTaxon() != null && isPesiTaxon(newNode.getTaxon())){
1153
									traverseTree(newNode, childNode, localTreeIndex, fetchLevel, state);
1154
								}
1155
							}
1156

    
1157
						} else {
1158
	//						logger.debug("Target Rank " + fetchLevel.getLabel() + " reached");
1159
							return;
1160
						}
1161
					} else {
1162
						logger.error("Rank is NULL. FetchLevel can not be checked: " + childName.getUuid() + " (" + childName.getTitleCache() + ")");
1163
					}
1164
				} else {
1165
					logger.error("Taxon can not be found in state: " + childTaxon.getUuid() + " (" + childTaxon.getTitleCache() + ")");
1166
				}
1167
			}else{
1168
				if (logger.isDebugEnabled()){
1169
					logger.debug("Taxon is not a PESI taxon: " + childTaxon.getUuid());
1170
				}
1171
			}
1172

    
1173
		} else {
1174
			logger.error("Taxon is NULL for TaxonNode: " + childNode.getUuid());
1175
		}
1176
	}
1177

    
1178
	/**
1179
	 * Stores values in database for every recursive round.
1180
	 * @param childNode The {@link TaxonNode TaxonNode} to process.
1181
	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1182
	 * @param treeIndex The TreeIndex at the current level.
1183
	 * @param state The {@link PesiExportState PesiExportState}.
1184
	 * @param currentTaxonFk The TaxonFk to store the values for.
1185
	 */
1186
	private void saveData(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, PesiExportState state, Integer currentTaxonFk) {
1187
		// We are differentiating kingdoms by the nomenclatural code for now.
1188
		// This needs to be handled in a better way as soon as we know how to differentiate between more kingdoms.
1189
		Taxon childTaxon = childNode.getTaxon();
1190
		if (isPesiTaxon(childTaxon)) {
1191
			TaxonBase<?> parentTaxon = null;
1192
			if (parentNode != null) {
1193
				parentTaxon = parentNode.getTaxon();
1194

    
1195
			}
1196

    
1197
			invokeParentTaxonFkAndTreeIndex(state.getDbId(parentTaxon), currentTaxonFk,	treeIndex);
1198
		}
1199

    
1200
	}
1201

    
1202
	/**
1203
	 * Inserts values into the Taxon database table.
1204
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1205
	 * @param state The {@link PesiExportState PesiExportState}.
1206
	 * @param stmt The prepared statement.
1207
	 * @return Whether save was successful or not.
1208
	 */
1209
	protected boolean invokeParentTaxonFkAndTreeIndex(Integer parentTaxonFk, Integer currentTaxonFk, StringBuffer treeIndex) {
1210
		try {
1211
			if (parentTaxonFk != null) {
1212
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(1, parentTaxonFk);
1213
			} else {
1214
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(1, null);
1215
			}
1216

    
1217
			if (treeIndex != null) {
1218
				parentTaxonFk_TreeIndex_KingdomFkStmt.setString(2, treeIndex.toString());
1219
			} else {
1220
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(2, null);
1221
			}
1222

    
1223
			if (currentTaxonFk != null) {
1224
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(3, currentTaxonFk);
1225
			} else {
1226
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(3, null);
1227
			}
1228

    
1229
			parentTaxonFk_TreeIndex_KingdomFkStmt.executeUpdate();
1230
			return true;
1231
		} catch (SQLException e) {
1232
			logger.error("ParentTaxonFk (" + parentTaxonFk ==null? "-":parentTaxonFk + ") and TreeIndex could not be inserted into database for taxon "+ (currentTaxonFk == null? "-" :currentTaxonFk) + ": " + e.getMessage());
1233
			e.printStackTrace();
1234
			return false;
1235
		}
1236
	}
1237

    
1238
	protected boolean invokeParentTaxonFk(Integer parentId, Integer childId) {
1239
		try {
1240
			parentTaxonFkStmt.setInt(1, parentId);
1241
			parentTaxonFkStmt.setInt(2, childId);
1242
			parentTaxonFkStmt.executeUpdate();
1243
			return true;
1244
		} catch (SQLException e) {
1245
			logger.warn("ParentTaxonFk (" + parentId ==null? "-":parentId + ") could not be inserted into database for taxon "+ (childId == null? "-" :childId) + ": " + e.getMessage());
1246
			e.printStackTrace();
1247
			return false;
1248
		}
1249
	}
1250

    
1251

    
1252
	/**
1253
	 * Inserts Rank data and KingdomFk into the Taxon database table.
1254
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1255
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1256
	 * @param taxonFk The TaxonFk to store the values for.
1257
	 * @param state
1258
	 * @param kindomFk The KingdomFk.
1259
	 * @return Whether save was successful or not.
1260
	 */
1261
	private boolean invokeRankDataAndKingdomFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, Integer taxonFk, Integer kingdomFk, PesiExportState state) {
1262
		try {
1263
			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1264
			if (rankFk != null) {
1265
				rankUpdateStmt.setInt(1, rankFk);
1266
			} else {
1267
				rankUpdateStmt.setObject(1, null);
1268
			}
1269

    
1270
			String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
1271
			if (rankCache != null) {
1272
				rankUpdateStmt.setString(2, rankCache);
1273
			} else {
1274
				rankUpdateStmt.setObject(2, null);
1275
			}
1276

    
1277
			if (kingdomFk != null) {
1278

    
1279
				rankUpdateStmt.setInt(3, kingdomFk);
1280
			} else {
1281
				rankUpdateStmt.setObject(3, null);
1282
			}
1283

    
1284
			if (taxonFk != null) {
1285
				rankUpdateStmt.setInt(4, taxonFk);
1286
			} else {
1287
				rankUpdateStmt.setObject(4, null);
1288
			}
1289

    
1290
			rankUpdateStmt.executeUpdate();
1291
			return true;
1292
		} catch (SQLException e) {
1293
			logger.error("Data (RankFk, RankCache, KingdomFk) could not be inserted into database: " + e.getMessage());
1294
			e.printStackTrace();
1295
			return false;
1296
		}
1297
	}
1298

    
1299
	/**
1300
	 * Inserts Rank data, TypeNameFk, KingdomFk, expertFk and speciesExpertFk into the Taxon database table.
1301
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1302
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1303
	 * @param taxonFk The TaxonFk to store the values for.
1304
	 * @param typeNameFk The TypeNameFk.
1305
	 * @param state
1306
	 * @param kindomFk The KingdomFk.
1307
	 * @param expertFk The ExpertFk.
1308
	 * @param speciesExpertFk The SpeciesExpertFk.
1309
	 * @return Whether save was successful or not.
1310
	 */
1311
	private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode,
1312
			Integer taxonFk, Integer typeNameFk, Integer kingdomFkk, PesiExportState state) {
1313
		try {
1314
			int index = 1;
1315
			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1316
			if (rankFk != null) {
1317
				rankTypeExpertsUpdateStmt.setInt(index++, rankFk);
1318
			} else {
1319
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1320
			}
1321

    
1322
			String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
1323
			if (rankCache != null) {
1324
				rankTypeExpertsUpdateStmt.setString(index++, rankCache);
1325
			} else {
1326
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1327
			}
1328

    
1329
			if (typeNameFk != null) {
1330
				rankTypeExpertsUpdateStmt.setInt(index++, typeNameFk);
1331
			} else {
1332
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1333
			}
1334

    
1335
			if (kingdomFkk != null) {
1336
				rankTypeExpertsUpdateStmt.setInt(index++, kingdomFkk);
1337
			} else {
1338
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1339
			}
1340

    
1341
//			if (expertFk != null) {
1342
//				rankTypeExpertsUpdateStmt.setInt(5, expertFk);
1343
//			} else {
1344
//				rankTypeExpertsUpdateStmt.setObject(5, null);
1345
//			}
1346
//
1347
//			//TODO handle experts GUIDS
1348
//			if (speciesExpertFk != null) {
1349
//				rankTypeExpertsUpdateStmt.setInt(6, speciesExpertFk);
1350
//			} else {
1351
//				rankTypeExpertsUpdateStmt.setObject(6, null);
1352
//			}
1353
//
1354
			if (taxonFk != null) {
1355
				rankTypeExpertsUpdateStmt.setInt(index++, taxonFk);
1356
			} else {
1357
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1358
			}
1359

    
1360
			rankTypeExpertsUpdateStmt.executeUpdate();
1361
			return true;
1362
		} catch (SQLException e) {
1363
			logger.error("Data could not be inserted into database: " + e.getMessage());
1364
			e.printStackTrace();
1365
			return false;
1366
		} catch (Exception e) {
1367
			logger.error("Some exception occurred: " + e.getMessage());
1368
			e.printStackTrace();
1369
			return false;
1370
		}
1371
	}
1372

    
1373
	/**
1374
	 * Deletes all entries of database tables related to <code>Taxon</code>.
1375
	 * @param state The {@link PesiExportState PesiExportState}.
1376
	 * @return Whether the delete operation was successful or not.
1377
	 */
1378
	protected boolean doDelete(PesiExportState state) {
1379
		PesiExportConfigurator pesiConfig = state.getConfig();
1380

    
1381
		String sql;
1382
		Source destination =  pesiConfig.getDestination();
1383

    
1384
		// Clear Taxon
1385
		sql = "DELETE FROM " + dbTableName;
1386
		destination.setQuery(sql);
1387
		destination.update(sql);
1388
		return true;
1389
	}
1390

    
1391
	@Override
1392
	protected boolean isIgnore(PesiExportState state) {
1393
		return ! state.getConfig().isDoTaxa();
1394
	}
1395

    
1396

    
1397
	/**
1398
	 * Creates the kingdom fk.
1399
	 * @param taxonName
1400
	 * @return
1401
	 */
1402
	@SuppressWarnings("unused")  //used by mapper
1403
	private static Integer getKingdomFk(TaxonNameBase taxonName){
1404
		return PesiTransformer.nomenClaturalCode2Kingdom(taxonName.getNomenclaturalCode());
1405
	}
1406

    
1407
	/**
1408
	 * Creates the parent fk.
1409
	 * @param taxonName
1410
	 * @return
1411
	 */
1412
	@SuppressWarnings("unused")  //used by mapper
1413
	private static Integer getParentTaxonFk(TaxonBase<?> taxonBase, PesiExportState state){
1414
		if (taxonBase.isInstanceOf(Taxon.class)){
1415
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1416
			if (! isMisappliedName(taxon)){
1417
				Set<TaxonNode> nodes = taxon.getTaxonNodes();
1418
				if (nodes.size() == 0){
1419
					if (taxon.getName().getRank().isLower(Rank.KINGDOM())){
1420
						logger.warn("Accepted taxon has no parent. " + taxon.getTitleCache() + ", " +  taxon.getUuid());
1421
					}
1422
				}else if (nodes.size() > 1){
1423
					logger.warn("Taxon has more than 1 node attached. This is not supported by PESI export." +  taxon.getTitleCache() + ", " +  taxon.getUuid());
1424
				}else{
1425
					Taxon parent =nodes.iterator().next().getParent().getTaxon();
1426
					return state.getDbId(parent);
1427
				}
1428
			}
1429
		}
1430
		return null;
1431
	}
1432

    
1433
	/**
1434
	 * Returns the rankFk for the taxon name based on the names nomenclatural code.
1435
	 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1436
	 * @param taxonName
1437
	 * @return
1438
	 */
1439
	@SuppressWarnings("unused")  //used by mapper
1440
	private static Integer getRankFk(TaxonNameBase<?,?> taxonName) {
1441
		return getRankFk(taxonName, taxonName.getNomenclaturalCode());
1442
	}
1443

    
1444

    
1445
	/**
1446
	 * Returns the <code>RankFk</code> attribute.
1447
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1448
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1449
	 * @return The <code>RankFk</code> attribute.
1450
	 * @see MethodMapper
1451
	 */
1452
	private static Integer getRankFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode) {
1453
		Integer result = null;
1454
		try {
1455
			if (nomenclaturalCode != null) {
1456
				if (taxonName != null) {
1457
					if (taxonName.getRank() == null) {
1458
						logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1459
					} else {
1460
						result = PesiTransformer.rank2RankId(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1461
					}
1462
					if (result == null) {
1463
						logger.warn("Rank could not be determined for PESI-Kingdom-Id " + PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode) + " and TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1464
					}
1465
				}
1466
			}
1467
		} catch (Exception e) {
1468
			e.printStackTrace();
1469
		}
1470
		return result;
1471
	}
1472

    
1473
	/**
1474
	 * Returns the rank cache for the taxon name based on the names nomenclatural code.
1475
	 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1476
	 * @param taxonName
1477
	 * @return
1478
	 */
1479
	@SuppressWarnings("unused")  //used by mapper
1480
	private static String getRankCache(TaxonNameBase<?,?> taxonName, PesiExportState state) {
1481
		return getRankCache(taxonName, taxonName.getNomenclaturalCode(), state);
1482
	}
1483

    
1484

    
1485
	/**
1486
	 * Returns the <code>RankCache</code> attribute.
1487
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1488
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1489
	 * @param state
1490
	 * @return The <code>RankCache</code> attribute.
1491
	 * @see MethodMapper
1492
	 */
1493
	private static String getRankCache(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, PesiExportState state) {
1494
		if (nomenclaturalCode != null) {
1495
			return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1496
		}else{
1497
			logger.warn("No nomenclatural code defined for name " + taxonName.getUuid());
1498
			return null;
1499
		}
1500

    
1501
	}
1502

    
1503

    
1504
	/**
1505
	 * Returns the <code>DisplayName</code> attribute.
1506
	 * @param taxon The {@link TaxonBase Taxon}.
1507
	 * @return The <code>DisplayName</code> attribute.
1508
	 * @see MethodMapper
1509
	 */
1510
	@SuppressWarnings("unused")  //used by Mapper
1511
	private static String getDisplayName(TaxonBase<?> taxon) {
1512
		TaxonNameBase<?,?> taxonName = taxon.getName();
1513
		String result = getDisplayName(taxonName);
1514
		if (isMisappliedName(taxon)){
1515
			result = result + " " + getAuthorString(taxon);
1516
		}
1517
		return result;
1518
	}
1519

    
1520
	/**
1521
	 * Returns the <code>AuthorString</code> attribute.
1522
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1523
	 * @return The <code>AuthorString</code> attribute.
1524
	 * @see MethodMapper
1525
	 */
1526
	@SuppressWarnings("unused") //used by mapper
1527
	protected static String getAuthorString(TaxonBase<?> taxon) {
1528
		try {
1529
			String result = null;
1530
			boolean isNonViralName = false;
1531
			String authorshipCache = null;
1532
			TaxonNameBase<?,?> taxonName = taxon.getName();
1533
			if (taxonName != null && taxonName.isInstanceOf(NonViralName.class)){
1534
				authorshipCache = CdmBase.deproxy(taxonName, NonViralName.class).getAuthorshipCache();
1535
				isNonViralName = true;
1536
			}
1537
			result = authorshipCache;
1538

    
1539
			// For a misapplied names there are special rules
1540
			if (isMisappliedName(taxon)){
1541
				if (taxon.getSec() != null){
1542
					String secTitle = taxon.getSec().getTitleCache();
1543
					if (! secTitle.startsWith("auct")){
1544
						secTitle = "sensu " + secTitle;
1545
					}else if (secTitle.equals("auct")){  //may be removed once the title cache is generated correctly for references with title auct. #
1546
						secTitle = "auct.";
1547
					}
1548
					return secTitle;
1549
				}else if (StringUtils.isBlank(authorshipCache)) {
1550
					// Set authorshipCache to "auct."
1551
					result = PesiTransformer.AUCT_STRING;
1552
				}else{
1553
					result = PesiTransformer.AUCT_STRING;
1554
//					result = authorshipCache;
1555
				}
1556
			}
1557

    
1558
			if (taxonName == null){
1559
				logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1560
			}else if (! isNonViralName){
1561
				logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1562
			}
1563

    
1564
			if (StringUtils.isBlank(result)) {
1565
				return null;
1566
			} else {
1567
				return result;
1568
			}
1569
		} catch (Exception e) {
1570
			e.printStackTrace();
1571
			return null;
1572
		}
1573

    
1574
	}
1575

    
1576

    
1577
	/**
1578
	 * Returns the <code>DisplayName</code> attribute.
1579
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1580
	 * @return The <code>DisplayName</code> attribute.
1581
	 * @see MethodMapper
1582
	 */
1583
	@SuppressWarnings("unused")  //used by Mapper
1584
	private static String getDisplayName(TaxonNameBase<?,?> taxonName) {
1585
		// TODO: extension?
1586
		if (taxonName == null) {
1587
			return null;
1588
		}else{
1589
			INonViralNameCacheStrategy<NonViralName<?>> cacheStrategy = getCacheStrategy(taxonName);
1590
			HTMLTagRules tagRules = new HTMLTagRules().
1591
					addRule(TagEnum.name, "i").
1592
					addRule(TagEnum.nomStatus, "@status@");
1593

    
1594
			NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1595
			String result = cacheStrategy.getFullTitleCache(nvn, tagRules);
1596
			cacheStrategy = null;
1597
			nvn = null;
1598
			return result.replaceAll(",?\\<@status@\\>.*\\</@status@\\>", "");
1599
		}
1600
	}
1601

    
1602
	@SuppressWarnings("unused")
1603
	private static String getGUID(TaxonNameBase<?,?> taxonName) {
1604
		UUID uuid = taxonName.getUuid();
1605
		String result = "NameUUID:" + uuid.toString();
1606
		return result;
1607
	}
1608

    
1609

    
1610
	/**
1611
	 * Returns the <code>WebShowName</code> attribute for a taxon.
1612
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1613
	 * @return The <code>WebShowName</code> attribute.
1614
	 * @see MethodMapper
1615
	*/
1616
	@SuppressWarnings("unused")
1617
	private static String getWebShowName(TaxonBase<?> taxon) {
1618
		TaxonNameBase<?,?> taxonName = taxon.getName();
1619
		String result = getWebShowName(taxonName);
1620
		if (isMisappliedName(taxon)){
1621
			result = result + " " + getAuthorString(taxon);
1622
		}
1623
		return result;
1624
	}
1625

    
1626
	/**
1627
	 * Returns the <code>WebShowName</code> attribute.
1628
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1629
	 * @return The <code>WebShowName</code> attribute.
1630
	 * @see MethodMapper
1631
	 */
1632
	private static String getWebShowName(TaxonNameBase<?,?> taxonName) {
1633
		//TODO extensions?
1634
		if (taxonName == null) {
1635
			return null;
1636
		}else{
1637
			INonViralNameCacheStrategy<NonViralName<?>> cacheStrategy = getCacheStrategy(taxonName);
1638

    
1639
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1640
			NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1641
			String result = cacheStrategy.getTitleCache(nvn, tagRules);
1642
			cacheStrategy = null;
1643
			nvn = null;
1644
			return result;
1645
		}
1646
	}
1647

    
1648

    
1649
	/**
1650
	 * Returns the <code>WebSearchName</code> attribute.
1651
	 * @param taxonName The {@link NonViralName NonViralName}.
1652
	 * @return The <code>WebSearchName</code> attribute.
1653
	 * @see MethodMapper
1654
	 */
1655
	@SuppressWarnings("unused")
1656
	private static String getWebSearchName(TaxonNameBase<?,?> taxonName) {
1657
		//TODO extensions?
1658
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1659
		NonViralNameDefaultCacheStrategy<NonViralName<?>> strategy = getCacheStrategy(nvn);
1660
		String result = strategy.getNameCache(nvn);
1661
		strategy = null;
1662
		nvn = null;
1663
		return result;
1664
	}
1665

    
1666

    
1667
	/**
1668
	 * Returns the <code>FullName</code> attribute.
1669
	 * @param taxonName The {@link NonViralName NonViralName}.
1670
	 * @return The <code>FullName</code> attribute.
1671
	 * @see MethodMapper
1672
	 */
1673
	@SuppressWarnings("unused")
1674
	private static String getFullName(TaxonNameBase taxonName) {
1675
		//TODO extensions?
1676
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1677
		String result = getCacheStrategy(nvn).getTitleCache(nvn);
1678
		Iterator<TaxonBase> taxa = taxonName.getTaxa().iterator();
1679
		if (taxonName.getTaxa().size() >0){
1680
			if (taxonName.getTaxa().size() == 1){
1681
				TaxonBase taxon = taxa.next();
1682
				if (isMisappliedName(taxon)){
1683
					result = result + " " + getAuthorString(taxon);
1684
				}
1685
				taxon = null;
1686
			}
1687
		}
1688
		taxa = null;
1689
		nvn = null;
1690
		return result;
1691
	}
1692

    
1693
	/**
1694
	 * Returns the SourceNameCache for the AdditionalSource table
1695
	 * @param taxonName
1696
	 * @return
1697
	 */
1698
	@SuppressWarnings("unused")
1699
	private static String getSourceNameCache(TaxonNameBase taxonName) {
1700
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1701
		if (nvn != null){
1702
			Reference nomRef = (Reference)nvn.getNomenclaturalReference();
1703
			if (nomRef != null){
1704
			    logger.warn("Semantics of getAbbrevTitleCache has changed. Please check if output is still correct. See #5388");
1705
				return nomRef.getAbbrevTitleCache();
1706
			}
1707
		}
1708
		return null;
1709
	}
1710

    
1711

    
1712

    
1713
	/**
1714
	 * Returns the <code>FullName</code> attribute.
1715
	 * @param taxon The {@link TaxonBase taxon}.
1716
	 * @return The <code>FullName</code> attribute.
1717
	 * @see MethodMapper
1718
	 */
1719
	/*@SuppressWarnings("unused")
1720
	private static String getFullName(TaxonBase taxon) {
1721
		//TODO extensions?
1722
		TaxonNameBase name = taxon.getName();
1723
		String result = getFullName(name);
1724
		if (isMisappliedName(taxon)){
1725
			result = result + " " + getAuthorString(taxon);
1726
		}
1727

    
1728
		return result;
1729
	}
1730
*/
1731

    
1732
	/**
1733
	 * Returns the nomenclatural reference which is the reference
1734
	 * including the detail (microreference).
1735
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1736
	 * @return The <code>AuthorString</code> attribute.
1737
	 * @see MethodMapper
1738
	 */
1739
	@SuppressWarnings("unused")
1740
	private static String getNomRefString(TaxonNameBase<?,?> taxonName) {
1741
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1742
		if (ref == null){
1743
			return null;
1744
		}
1745
		if (! ref.isProtectedAbbrevTitleCache()){
1746
			ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1747
		}
1748
		return ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1749
	}
1750

    
1751

    
1752
	/**
1753
	 * Returns the <code>NameStatusFk</code> attribute.
1754
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1755
	 * @return The <code>NameStatusFk</code> attribute.
1756
	 * @see MethodMapper
1757
	 */
1758
	@SuppressWarnings("unused")
1759
	private static Integer getNameStatusFk(TaxonNameBase<?,?> taxonName) {
1760
		Integer result = null;
1761

    
1762
		NomenclaturalStatus state = getNameStatus(taxonName);
1763
		if (state != null) {
1764
			result = PesiTransformer.nomStatus2nomStatusFk(state.getType());
1765
		}
1766
		return result;
1767
	}
1768

    
1769
	/**
1770
	 * Returns the <code>NameStatusCache</code> attribute.
1771
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1772
	 * @return The <code>NameStatusCache</code> attribute.
1773
	 * @throws UndefinedTransformerMethodException
1774
	 * @see MethodMapper
1775
	 */
1776
	@SuppressWarnings("unused")
1777
	private static String getNameStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1778
		String result = null;
1779
		NomenclaturalStatus status = getNameStatus(taxonName);
1780
		if (status != null) {
1781
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1782
		}
1783
		return result;
1784
	}
1785

    
1786

    
1787
	private static NomenclaturalStatus getNameStatus(TaxonNameBase<?,?> taxonName) {
1788
		try {
1789
			if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1790
				NonViralName<?> nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1791
				Set<NomenclaturalStatus> states = nonViralName.getStatus();
1792
				if (states.size() == 1) {
1793
					NomenclaturalStatus status = states.iterator().next();
1794
					return status;
1795
				} else if (states.size() > 1) {
1796
					logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1797
				}
1798
			}
1799

    
1800
		} catch (Exception e) {
1801
			e.printStackTrace();
1802
		}
1803
		return null;
1804
	}
1805
	/**
1806
	 * Returns the <code>TaxonStatusFk</code> attribute.
1807
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1808
	 * @param state The {@link PesiExportState PesiExportState}.
1809
	 * @return The <code>TaxonStatusFk</code> attribute.
1810
	 * @see MethodMapper
1811
	 */
1812
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1813
		Integer result = null;
1814

    
1815
		try {
1816
			if (isMisappliedName(taxon)) {
1817
				Synonym synonym = Synonym.NewInstance(null, null);
1818

    
1819
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1820
				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1821
			} else {
1822
				result = PesiTransformer.taxonBase2statusFk(taxon);
1823
			}
1824

    
1825
		} catch (Exception e) {
1826
			e.printStackTrace();
1827
		}
1828
		return result;
1829
	}
1830

    
1831
	/**
1832
	 * Returns the <code>TaxonStatusCache</code> attribute.
1833
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1834
	 * @param state The {@link PesiExportState PesiExportState}.
1835
	 * @return The <code>TaxonStatusCache</code> attribute.
1836
	 * @throws UndefinedTransformerMethodException
1837
	 * @see MethodMapper
1838
	 */
1839
	@SuppressWarnings("unused")
1840
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1841
		return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1842
	}
1843

    
1844
	/**
1845
	 * Returns the <code>TypeNameFk</code> attribute.
1846
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1847
	 * @param state The {@link PesiExportState PesiExportState}.
1848
	 * @return The <code>TypeNameFk</code> attribute.
1849
	 * @see MethodMapper
1850
	 */
1851
	private static Integer getTypeNameFk(TaxonNameBase<?,?> taxonNameBase, PesiExportState state) {
1852
		Integer result = null;
1853
		if (taxonNameBase != null) {
1854
			Set<NameTypeDesignation> nameTypeDesignations = taxonNameBase.getNameTypeDesignations();
1855
			if (nameTypeDesignations.size() == 1) {
1856
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1857
				if (nameTypeDesignation != null) {
1858
					TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1859
					if (typeName != null) {
1860
						result = state.getDbId(typeName);
1861
					}
1862
				}
1863
			} else if (nameTypeDesignations.size() > 1) {
1864
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonNameBase.getUuid() + " (" + taxonNameBase.getTitleCache() + ")");
1865
			}
1866
		}
1867
		return result;
1868
	}
1869

    
1870
	/**
1871
	 * Returns the <code>TypeFullnameCache</code> attribute.
1872
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1873
	 * @return The <code>TypeFullnameCache</code> attribute.
1874
	 * @see MethodMapper
1875
	 */
1876
	@SuppressWarnings("unused")
1877
	private static String getTypeFullnameCache(TaxonNameBase<?,?> taxonName) {
1878
		String result = null;
1879

    
1880
		try {
1881
		if (taxonName != null) {
1882
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1883
			if (nameTypeDesignations.size() == 1) {
1884
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1885
				if (nameTypeDesignation != null) {
1886
					TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1887
					if (typeName != null) {
1888
						result = typeName.getTitleCache();
1889
					}
1890
				}
1891
			} else if (nameTypeDesignations.size() > 1) {
1892
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1893
			}
1894
		}
1895

    
1896
		} catch (Exception e) {
1897
			e.printStackTrace();
1898
		}
1899
		return result;
1900
	}
1901

    
1902

    
1903
	/**
1904
	 * Returns the <code>QualityStatusFk</code> attribute.
1905
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1906
	 * @return The <code>QualityStatusFk</code> attribute.
1907
	 * @see MethodMapper
1908
	 */
1909
	private static Integer getQualityStatusFk(TaxonNameBase taxonName) {
1910
		BitSet sources = getSources(taxonName);
1911
		return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1912
	}
1913

    
1914

    
1915
	/**
1916
	 * Returns the <code>QualityStatusCache</code> attribute.
1917
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1918
	 * @return The <code>QualityStatusCache</code> attribute.
1919
	 * @throws UndefinedTransformerMethodException
1920
	 * @see MethodMapper
1921
	 */
1922
	@SuppressWarnings("unused")
1923
	private static String getQualityStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1924
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1925
	}
1926

    
1927

    
1928
	/**
1929
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1930
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1931
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1932
	 * @see MethodMapper
1933
	 */
1934
	@SuppressWarnings("unused")
1935
	private static Integer getTypeDesignationStatusFk(TaxonNameBase<?,?> taxonName) {
1936
		Integer result = null;
1937

    
1938
		try {
1939
		if (taxonName != null) {
1940
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1941
			if (typeDesignations.size() == 1) {
1942
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1943
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1944
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1945
			} else if (typeDesignations.size() > 1) {
1946
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1947
			}
1948
		}
1949

    
1950
		} catch (Exception e) {
1951
			e.printStackTrace();
1952
		}
1953
		return result;
1954
	}
1955

    
1956
	/**
1957
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1958
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1959
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1960
	 * @see MethodMapper
1961
	 */
1962
	@SuppressWarnings("unused")
1963
	private static String getTypeDesignationStatusCache(TaxonNameBase<?,?> taxonName) {
1964
		String result = null;
1965

    
1966
		try {
1967
		if (taxonName != null) {
1968
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1969
			if (typeDesignations.size() == 1) {
1970
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1971
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1972
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
1973
			} else if (typeDesignations.size() > 1) {
1974
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1975
			}
1976
		}
1977

    
1978
		} catch (Exception e) {
1979
			e.printStackTrace();
1980
		}
1981
		return result;
1982
	}
1983

    
1984
	/**
1985
	 * Returns the <code>FossilStatusFk</code> attribute.
1986
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1987
	 * @return The <code>FossilStatusFk</code> attribute.
1988
	 * @see MethodMapper
1989
	 */
1990
	@SuppressWarnings("unused")
1991
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
1992
		Integer result = null;
1993

    
1994
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
1995
		if (fossilStatuus.size() == 0){
1996
			return null;
1997
		}else if (fossilStatuus.size() > 1){
1998
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
1999
		}
2000
		String fossilStatus = fossilStatuus.iterator().next();
2001

    
2002
		int statusFk = state.getTransformer().FossilStatusCache2FossilStatusFk(fossilStatus);
2003
		return statusFk;
2004
	}
2005

    
2006
	/**
2007
	 * Returns the <code>FossilStatusCache</code> attribute.
2008
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2009
	 * @return The <code>FossilStatusCache</code> attribute.
2010
	 * @see MethodMapper
2011
	 */
2012
	@SuppressWarnings("unused")
2013
	private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
2014
		String result = null;
2015
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
2016
		if (fossilStatuus.size() == 0){
2017
			return null;
2018
		}
2019
		for (String strFossilStatus : fossilStatuus){
2020
			result = CdmUtils.concat(";", result, strFossilStatus);
2021
		}
2022
		return result;
2023
	}
2024

    
2025
	/**
2026
	 * Returns the <code>IdInSource</code> attribute.
2027
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2028
	 * @return The <code>IdInSource</code> attribute.
2029
	 * @see MethodMapper
2030
	 */
2031
	@SuppressWarnings("unused")
2032
	private static String getIdInSource(IdentifiableEntity taxonName) {
2033
		String result = null;
2034

    
2035
		try {
2036
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
2037
			if (sources.size() > 1){
2038
				logger.warn("There is > 1 Pesi source. This is not yet handled.");
2039
			}
2040
			if (sources.size() == 0){
2041
				logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2042
			}
2043
			for (IdentifiableSource source : sources) {
2044
				Reference ref = source.getCitation();
2045
				UUID refUuid = ref.getUuid();
2046
				String idInSource = source.getIdInSource();
2047
				if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
2048
					result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
2049
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2050
					result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
2051
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2052
					result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
2053
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
2054
					result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
2055
				}else{
2056
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2057
				}
2058

    
2059
				String sourceIdNameSpace = source.getIdNamespace();
2060
				if (sourceIdNameSpace != null) {
2061
					if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
2062
						result =  idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
2063
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
2064
						result =  idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
2065
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
2066
						result =  idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
2067
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
2068
						result =  idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
2069
					}
2070
				}
2071
				if (result == null) {
2072
					logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
2073
				}
2074
			}
2075
		} catch (Exception e) {
2076
			e.printStackTrace();
2077
			logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
2078
		}
2079

    
2080
		if (result == null) {
2081
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2082
		}
2083
		return result;
2084
	}
2085

    
2086
	/**
2087
	 * Returns the idInSource for a given TaxonName only.
2088
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2089
	 * @return The idInSource.
2090
	 */
2091
	private static String getIdInSourceOnly(IdentifiableEntity identEntity) {
2092
		String result = null;
2093

    
2094
		// Get the sources first
2095
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2096

    
2097
		// Determine the idInSource
2098
		if (sources.size() == 1) {
2099
			IdentifiableSource source = sources.iterator().next();
2100
			if (source != null) {
2101
				result = source.getIdInSource();
2102
			}
2103
		} else if (sources.size() > 1) {
2104
			int count = 1;
2105
			result = "";
2106
			for (IdentifiableSource source : sources) {
2107
				result += source.getIdInSource();
2108
				if (count < sources.size()) {
2109
					result += "; ";
2110
				}
2111
				count++;
2112
			}
2113

    
2114
		}
2115

    
2116
		return result;
2117
	}
2118

    
2119
	/**
2120
	 * Returns the Sources for a given TaxonName only.
2121
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2122
	 * @return The Sources.
2123
	 */
2124
	private static Set<IdentifiableSource> getPesiSources(IdentifiableEntity identEntity) {
2125
		Set<IdentifiableSource> sources = new java.util.HashSet<IdentifiableSource>();
2126

    
2127
		//Taxon Names
2128
		if (identEntity.isInstanceOf(TaxonNameBase.class)){
2129
			// Sources from TaxonName
2130
			TaxonNameBase taxonName = CdmBase.deproxy(identEntity, TaxonNameBase.class);
2131
			Set<IdentifiableSource> testSources = identEntity.getSources();
2132
			sources = filterPesiSources(identEntity.getSources());
2133

    
2134
			if (sources.size() == 0 && testSources.size()>0){
2135
				IdentifiableSource source = testSources.iterator().next();
2136
				logger.warn("There are sources, but they are no pesi sources!!!" + source.getIdInSource() + " - " + source.getIdNamespace() + " - " + source.getCitation().getTitleCache());
2137
			}
2138
			if (sources.size() > 1) {
2139
				logger.warn("This TaxonName has more than one Source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() + ")");
2140
			}
2141

    
2142
			// name has no PESI source, take sources from TaxonBase
2143
			if (sources == null || sources.isEmpty()) {
2144
				Set<TaxonBase> taxa = taxonName.getTaxonBases();
2145
				for (TaxonBase taxonBase: taxa){
2146
					sources.addAll(filterPesiSources(taxonBase.getSources()));
2147
				}
2148
			}
2149

    
2150
		//for TaxonBases
2151
		}else if (identEntity.isInstanceOf(TaxonBase.class)){
2152
			sources = filterPesiSources(identEntity.getSources());
2153
		}
2154

    
2155
		/*TODO: deleted only for testing the inferred synonyms
2156
		if (sources == null || sources.isEmpty()) {
2157
			logger.warn("This TaxonName has no PESI Sources: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2158
		}else if (sources.size() > 1){
2159
			logger.warn("This Taxon(Name) has more than 1 PESI source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2160
		}
2161
		*/
2162
		return sources;
2163
	}
2164

    
2165
	// return all sources with a PESI reference
2166
	private static Set<IdentifiableSource> filterPesiSources(Set<? extends IdentifiableSource> sources) {
2167
		Set<IdentifiableSource> result = new HashSet<IdentifiableSource>();
2168
		for (IdentifiableSource source : sources){
2169
			Reference ref = source.getCitation();
2170
			UUID refUuid = ref.getUuid();
2171
			if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed) ||
2172
				refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)||
2173
				refUuid.equals(PesiTransformer.uuidSourceRefErms)||
2174
				refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum) ||
2175
				refUuid.equals(PesiTransformer.uuidSourceRefAuct)){
2176
				result.add(source);
2177
			}
2178
		}
2179
		return result;
2180
	}
2181

    
2182
	/**
2183
	 * Returns the <code>GUID</code> attribute.
2184
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2185
	 * @return The <code>GUID</code> attribute.
2186
	 * @see MethodMapper
2187
	 */
2188
	private static String getGUID(TaxonBase<?> taxon) {
2189
		if (taxon.getLsid() != null ){
2190
			return taxon.getLsid().getLsid();
2191
		}else if (taxon.hasMarker(PesiTransformer.uuidMarkerGuidIsMissing, true)){
2192
			return null;
2193
		}else{
2194
			return taxon.getUuid().toString();
2195
		}
2196
	}
2197

    
2198

    
2199

    
2200

    
2201
	/**
2202
	 * Returns the <code>DerivedFromGuid</code> attribute.
2203
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2204
	 * @return The <code>DerivedFromGuid</code> attribute.
2205
	 * @see MethodMapper
2206
	 */
2207
	@SuppressWarnings("unused")
2208
	private static String getDerivedFromGuid(TaxonBase<?> taxon) {
2209
		String result = null;
2210
		try {
2211
		// The same as GUID for now
2212
		result = getGUID(taxon);
2213
		} catch (Exception e) {
2214
			e.printStackTrace();
2215
		}
2216
		return result;
2217
	}
2218

    
2219
	/**
2220
	 * Returns the <code>CacheCitation</code> attribute.
2221
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2222
	 * @return The CacheCitation.
2223
	 * @see MethodMapper
2224
	 */
2225
	@SuppressWarnings("unused")
2226
	private static String getCacheCitation(TaxonBase taxon) {
2227
		// !!! See also doPhaseUpdates
2228

    
2229
		TaxonNameBase<?,?> taxonName = taxon.getName();
2230
		String result = "";
2231
		//TODO implement anew for taxa
2232
		try {
2233
			BitSet sources = getSources(taxonName);
2234
			if (sources.isEmpty()) {
2235
//				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2236
			} else if (sources.get(PesiTransformer.SOURCE_ERMS)) {
2237
				// 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...
2238
				// 		 So the following code is some kind of harmless assumption.
2239
				Set<Extension> extensions = taxonName.getExtensions();
2240
				for (Extension extension : extensions) {
2241
					if (extension.getType().equals(cacheCitationExtensionType)) {
2242
						result = extension.getValue();
2243
					}
2244
				}
2245
			} else {
2246
				String expertName = getExpertName(taxon);
2247
				String webShowName = getWebShowName(taxonName);
2248

    
2249
				// idInSource only
2250
				String idInSource = getIdInSourceOnly(taxonName);
2251

    
2252
				// build the cacheCitation
2253
				if (expertName != null) {
2254
					result += expertName + ". ";
2255
				} else {
2256
					if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
2257
				}
2258
				if (webShowName != null) {
2259
					result += webShowName + ". ";
2260
				} else {
2261
					logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2262
				}
2263

    
2264
				if (getOriginalDB(taxonName).equals("FaEu")) {
2265
					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
2266
				} else if (getOriginalDB(taxonName).equals("EM")) {
2267
					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
2268
				}
2269

    
2270
				if (idInSource != null) {
2271
					result += idInSource;
2272
				} else {
2273
					logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2274
				}
2275
			}
2276
		} catch (Exception e) {
2277
			e.printStackTrace();
2278
		}
2279

    
2280
		if (StringUtils.isBlank(result)) {
2281
			return null;
2282
		} else {
2283
			return result;
2284
		}
2285
	}
2286

    
2287
	/**
2288
	 * Returns the <code>OriginalDB</code> attribute.
2289
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2290
	 * @return The <code>OriginalDB</code> attribute.
2291
	 * @see MethodMapper
2292
	 */
2293
	private static String getOriginalDB(IdentifiableEntity identEntity) {
2294
		// Sources from TaxonName
2295
		BitSet sources  = getSources(identEntity);
2296
		return PesiTransformer.getOriginalDbBySources(sources);
2297
	}
2298

    
2299
	/**
2300
	 * Returns the <code>LastAction</code> attribute.
2301
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2302
	 * @return The <code>LastAction</code> attribute.
2303
	 * @see MethodMapper
2304
	 */
2305
	@SuppressWarnings("unused")
2306
	private static String getLastAction(IdentifiableEntity<?> identEntity) {
2307
		String result = null;
2308
		try {
2309
		Set<Extension> extensions = identEntity.getExtensions();
2310
		for (Extension extension : extensions) {
2311
			if (extension.getType().equals(lastActionExtensionType)) {
2312
				result = extension.getValue();
2313
			}
2314
		}
2315
		} catch (Exception e) {
2316
			e.printStackTrace();
2317
		}
2318
		return result;
2319
	}
2320

    
2321
	/**
2322
	 * Returns the <code>LastActionDate</code> attribute.
2323
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2324
	 * @return The <code>LastActionDate</code> attribute.
2325
	 * @see MethodMapper
2326
	 */
2327
	@SuppressWarnings({ "unused" })
2328
	private static DateTime getLastActionDate(IdentifiableEntity identEntity) {
2329
		DateTime result = null;
2330
		try {
2331
			Set<Extension> extensions = identEntity.getExtensions();
2332
			for (Extension extension : extensions) {
2333
				if (extension.getType().equals(lastActionDateExtensionType)) {
2334
					String dateTime = extension.getValue();
2335
					if (dateTime != null) {
2336
						DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
2337
						result = formatter.parseDateTime(dateTime);
2338
					}
2339
				}
2340
			}
2341
		} catch (Exception e) {
2342
			e.printStackTrace();
2343
		}
2344
		return result;
2345
	}
2346

    
2347
	/**
2348
	 * Returns the <code>ExpertName</code> attribute.
2349
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2350
	 * @return The <code>ExpertName</code> attribute.
2351
	 * @see MethodMapper
2352
	 */
2353
	@SuppressWarnings("unused")
2354
	private static String getExpertName(TaxonBase<?> taxonName) {
2355
		String result = null;
2356
		try {
2357
		Set<Extension> extensions = taxonName.getExtensions();
2358
		for (Extension extension : extensions) {
2359
			if (extension.getType().equals(expertNameExtensionType)) {
2360
				result = extension.getValue();
2361
			}
2362
		}
2363
		} catch (Exception e) {
2364
			e.printStackTrace();
2365
		}
2366
		return result;
2367
	}
2368

    
2369
	/**
2370
	 * Returns the <code>ExpertFk</code> attribute.
2371
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2372
	 * @param state The {@link PesiExportState PesiExportState}.
2373
	 * @return The <code>ExpertFk</code> attribute.
2374
	 * @see MethodMapper
2375
	 */
2376
	private static Integer getExpertFk(Reference reference, PesiExportState state) {
2377
		Integer result = state.getDbId(reference);
2378
		return result;
2379
	}
2380

    
2381
	/**
2382
	 * Returns the <code>SpeciesExpertName</code> attribute.
2383
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2384
	 * @return The <code>SpeciesExpertName</code> attribute.
2385
	 * @see MethodMapper
2386
	 */
2387
	@SuppressWarnings("unused")
2388
	private static String getSpeciesExpertName(TaxonBase<?> taxonName) {
2389
		String result = null;
2390
		try {
2391
		Set<Extension> extensions = taxonName.getExtensions();
2392
		for (Extension extension : extensions) {
2393
			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2394
				result = extension.getValue();
2395
			}
2396
		}
2397
		} catch (Exception e) {
2398
			e.printStackTrace();
2399
		}
2400
		return result;
2401
	}
2402

    
2403
	/**
2404
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2405
	 * @param reference The {@link Reference Reference}.
2406
	 * @param state The {@link PesiExportState PesiExportState}.
2407
	 * @return The <code>SpeciesExpertFk</code> attribute.
2408
	 * @see MethodMapper
2409
	 */
2410
	private static Integer getSpeciesExpertFk(Reference reference, PesiExportState state) {
2411
		Integer result = state.getDbId(reference);
2412
		return result;
2413
	}
2414

    
2415

    
2416
	/**
2417
	 * Returns the source (E+M, Fauna Europaea, Index Fungorum, ERMS) of a given
2418
	 * Identifiable Entity as a BitSet
2419
	 * @param identEntity
2420
	 * @return
2421
	 */
2422
	private static BitSet getSources(IdentifiableEntity<?> identEntity){
2423
		BitSet bitSet = new BitSet();
2424
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2425
		for (IdentifiableSource source : sources) {
2426
			Reference ref = source.getCitation();
2427
			UUID refUuid = ref.getUuid();
2428
			if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
2429
				bitSet.set(PesiTransformer.SOURCE_EM);
2430
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2431
				bitSet.set(PesiTransformer.SOURCE_FE);
2432
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2433
				bitSet.set(PesiTransformer.SOURCE_ERMS);
2434
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){
2435
				bitSet.set(PesiTransformer.SOURCE_IF);
2436
			}else{
2437
				if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2438
			}
2439
		}
2440
		return bitSet;
2441

    
2442
	}
2443

    
2444
	protected static NonViralNameDefaultCacheStrategy getCacheStrategy(TaxonNameBase<?, ?> taxonName) {
2445
		taxonName = CdmBase.deproxy(taxonName, TaxonNameBase.class);
2446
		NonViralNameDefaultCacheStrategy<?> cacheStrategy;
2447
		if (taxonName.isInstanceOf(ZoologicalName.class)){
2448
			cacheStrategy = zooNameStrategy;
2449
		}else if (taxonName.isInstanceOf(BotanicalName.class)) {
2450
			cacheStrategy = botanicalNameStrategy;
2451
		}else if (taxonName.getClass().equals(NonViralName.class)) {
2452
			cacheStrategy = nonViralNameStrategy;
2453
		}else if (taxonName.getClass().equals(BacterialName.class)) {
2454
			cacheStrategy = bacterialNameStrategy;
2455
		}else{
2456
			logger.error("Unhandled taxon name type. Can't define strategy class");
2457
			cacheStrategy = botanicalNameStrategy;
2458
		}
2459
		return cacheStrategy;
2460
	}
2461

    
2462
	/**
2463
	 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2464
	 * @param relationship The {@link RelationshipBase Relationship}.
2465
	 * @return The <code>RelTaxonQualifierFk</code> attribute.
2466
	 * @see MethodMapper
2467
	 */
2468
	@SuppressWarnings("unused")
2469
	private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2470
		return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2471
	}
2472
	/**
2473
	 * Returns the <code>Notes</code> attribute.
2474
	 * @param relationship The {@link RelationshipBase Relationship}.
2475
	 * @return The <code>Notes</code> attribute.
2476
	 * @see MethodMapper
2477
	 */
2478
	@SuppressWarnings("unused")
2479
	private static String getNotes(RelationshipBase<?, ?, ?> relationship) {
2480
		// TODO
2481
		return null;
2482
	}
2483

    
2484

    
2485
	/**
2486
	 * Returns the CDM to PESI specific export mappings.
2487
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2488
	 */
2489
	private PesiExportMapping getMapping() {
2490
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2491

    
2492
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2493
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2494
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2495
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2496

    
2497
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2498

    
2499
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2500
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2501
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misallied Names are handled differently
2502
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2503

    
2504
		// DisplayName
2505
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2506

    
2507
		// FossilStatus (Fk, Cache)
2508
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2509
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2510

    
2511
		//handled by name mapping
2512
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2513
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2514

    
2515
		//experts
2516
		ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
2517
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2518
		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
2519
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2520

    
2521
//		mapping.addMapper(MethodMapper.NewInstance("ParentTaxonFk", this, TaxonBase.class, PesiExportState.class));  //by AM, doesn't work, FK exception
2522
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonNameBase.class, "Name"));
2523

    
2524
		addNameMappers(mapping);
2525

    
2526
		return mapping;
2527
	}
2528

    
2529
	/**
2530
	 * Returns the CDM to PESI specific export mappings.
2531
	 * @param state
2532
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2533
	 * @throws UndefinedTransformerMethodException
2534
	 */
2535
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2536
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2537

    
2538
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2539

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

    
2542
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonNameBase.class));
2543
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonNameBase.class));
2544
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonNameBase.class, PesiExportState.class));
2545
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
2546
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2547
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
2548
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonNameBase.class));
2549
		mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonNameBase.class));
2550

    
2551

    
2552

    
2553
		// DisplayName
2554
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonNameBase.class));
2555

    
2556
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2557
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2558

    
2559
		addNameMappers(mapping);
2560
		//TODO add author mapper, TypeNameFk
2561

    
2562
		return mapping;
2563
	}
2564

    
2565
	private void addNameMappers(PesiExportMapping mapping) {
2566
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2567
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2568
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2569
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2570

    
2571
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2572
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonNameBase.class));
2573

    
2574
//		mapping.addMapper(DbStringMapper.NewInstance("TitleCache", "FullName"));    //does not work as we need other cache strategy
2575
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonNameBase.class));
2576

    
2577

    
2578
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonNameBase.class));
2579

    
2580
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonNameBase.class));
2581
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2582
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonNameBase.class));
2583
		//TODO TypeNameFk
2584

    
2585
		//quality status
2586
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonNameBase.class));
2587
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2588

    
2589
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2590
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2591

    
2592
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2593

    
2594
	}
2595

    
2596
	private PesiExportMapping getSynRelMapping() {
2597
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2598

    
2599
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getTaxonFk1", RelationshipBase.class, PesiExportState.class));
2600
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk2", this.getClass(), "getTaxonFk2", RelationshipBase.class, PesiExportState.class));
2601
		mapping.addMapper(MethodMapper.NewInstance("RelTaxonQualifierFk", this,  RelationshipBase.class));
2602
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this, RelationshipBase.class, PesiExportState.class));
2603
		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2604

    
2605
		return mapping;
2606
	}
2607

    
2608
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state)  throws UndefinedTransformerMethodException{
2609
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2610

    
2611
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2612
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonNameBase.class, "Name"));
2613

    
2614
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2615
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2616
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonNameBase.class));
2617

    
2618

    
2619
		//we have only nomenclatural references here
2620
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2621
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2622

    
2623
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2624

    
2625
		return mapping;
2626
	}
2627

    
2628
}
(11-11/12)