Project

General

Profile

Download (96.1 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.name.TaxonName;
66
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
67
import eu.etaxonomy.cdm.model.reference.Reference;
68
import eu.etaxonomy.cdm.model.taxon.Classification;
69
import eu.etaxonomy.cdm.model.taxon.Synonym;
70
import eu.etaxonomy.cdm.model.taxon.SynonymType;
71
import eu.etaxonomy.cdm.model.taxon.Taxon;
72
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
73
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
74
import eu.etaxonomy.cdm.profiler.ProfilerController;
75
import eu.etaxonomy.cdm.strategy.cache.HTMLTagRules;
76
import eu.etaxonomy.cdm.strategy.cache.TagEnum;
77
import eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy;
78
import eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy;
79
import eu.etaxonomy.cdm.strategy.cache.name.ZooNameNoMarkerCacheStrategy;
80

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

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

    
123

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

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

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

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

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

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

    
161

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

    
167
			initPreparedStatements(state);
168

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

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

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

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

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

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

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

    
198

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

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

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

    
208

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

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

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

    
225

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

    
231
		initParentFkStatement(state);
232
	}
233

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

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

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

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

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

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

    
273

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

    
279

    
280

    
281
		int partitionCount = 0;
282

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

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

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

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

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

    
328

    
329

    
330
			}
331

    
332

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

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

    
350

    
351

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

    
364

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

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

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

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

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

    
424

    
425

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

    
440
		List<Taxon> list;
441

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

    
445
		insertBiota(state);
446

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

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

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

    
467
			}
468

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

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

    
485
		return success;
486

    
487
	}
488

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

    
508
	}
509

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

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

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

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

    
523
		return success;
524

    
525
	}
526

    
527

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

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

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

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

    
547
		StringBuffer treeIndex = new StringBuffer();
548

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

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

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

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

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

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

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

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

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

    
623
					newNode = null;
624

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

    
633
				}
634
				rankSpecificRootNodes = null;
635
			}
636

    
637
		}
638

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

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

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

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

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

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

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

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

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

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

    
704
		list = null;
705

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

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

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

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

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

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

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

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

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

    
771

    
772

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
861
		HashMap<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonName>();
862

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

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

    
869
				if (taxonName.isZoological()) {
870
					nomenclaturalCode  = taxonName.getNameType();
871
					kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
872

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

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

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

    
891
						}
892
					}
893

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

    
909

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

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

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

    
962

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

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

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

    
987

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1194
			}
1195

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

    
1199
	}
1200

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

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

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

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

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

    
1250

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

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

    
1276
			if (kingdomFk != null) {
1277

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1395

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

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

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

    
1443

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

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

    
1483

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

    
1500
	}
1501

    
1502

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

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

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

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

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

    
1573
	}
1574

    
1575

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

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

    
1601
	@SuppressWarnings("unused")
1602
	private static String getGUID(TaxonName taxonName) {
1603
		UUID uuid = taxonName.getUuid();
1604
		String result = "NameUUID:" + uuid.toString();
1605
		return result;
1606
	}
1607

    
1608

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

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

    
1638
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1639
			String result = cacheStrategy.getTitleCache(taxonName, tagRules);
1640
			return result;
1641
		}
1642
	}
1643

    
1644

    
1645
	/**
1646
	 * Returns the <code>WebSearchName</code> attribute.
1647
	 * @param taxonName The {@link NonViralName NonViralName}.
1648
	 * @return The <code>WebSearchName</code> attribute.
1649
	 * @see MethodMapper
1650
	 */
1651
	@SuppressWarnings("unused")
1652
	private static String getWebSearchName(TaxonName taxonName) {
1653
		//TODO extensions?
1654
		NonViralNameDefaultCacheStrategy strategy = getCacheStrategy(taxonName);
1655
		String result = strategy.getNameCache(taxonName);
1656
		return result;
1657
	}
1658

    
1659

    
1660
	/**
1661
	 * Returns the <code>FullName</code> attribute.
1662
	 * @param taxonName The {@link NonViralName NonViralName}.
1663
	 * @return The <code>FullName</code> attribute.
1664
	 * @see MethodMapper
1665
	 */
1666
	@SuppressWarnings("unused")
1667
	private static String getFullName(TaxonName taxonName) {
1668
		//TODO extensions?
1669
		String result = getCacheStrategy(taxonName).getTitleCache(taxonName);
1670
		Iterator<Taxon> taxa = taxonName.getTaxa().iterator();
1671
		if (taxonName.getTaxa().size() >0){
1672
			if (taxonName.getTaxa().size() == 1){
1673
				Taxon taxon = taxa.next();
1674
				if (isMisappliedName(taxon)){
1675
					result = result + " " + getAuthorString(taxon);
1676
				}
1677
				taxon = null;
1678
			}
1679
		}
1680
		return result;
1681
	}
1682

    
1683
	/**
1684
	 * Returns the SourceNameCache for the AdditionalSource table
1685
	 * @param taxonName
1686
	 * @return
1687
	 */
1688
	@SuppressWarnings("unused")
1689
	private static String getSourceNameCache(TaxonName taxonName) {
1690
		if (taxonName != null){
1691
			Reference nomRef = (Reference)taxonName.getNomenclaturalReference();
1692
			if (nomRef != null){
1693
			    logger.warn("Semantics of getAbbrevTitleCache has changed. Please check if output is still correct. See #5388");
1694
				return nomRef.getAbbrevTitleCache();
1695
			}
1696
		}
1697
		return null;
1698
	}
1699

    
1700

    
1701

    
1702
	/**
1703
	 * Returns the <code>FullName</code> attribute.
1704
	 * @param taxon The {@link TaxonBase taxon}.
1705
	 * @return The <code>FullName</code> attribute.
1706
	 * @see MethodMapper
1707
	 */
1708
	/*@SuppressWarnings("unused")
1709
	private static String getFullName(TaxonBase taxon) {
1710
		//TODO extensions?
1711
		TaxonNameBase name = taxon.getName();
1712
		String result = getFullName(name);
1713
		if (isMisappliedName(taxon)){
1714
			result = result + " " + getAuthorString(taxon);
1715
		}
1716

    
1717
		return result;
1718
	}
1719
*/
1720

    
1721
	/**
1722
	 * Returns the nomenclatural reference which is the reference
1723
	 * including the detail (microreference).
1724
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1725
	 * @return The <code>AuthorString</code> attribute.
1726
	 * @see MethodMapper
1727
	 */
1728
	@SuppressWarnings("unused")
1729
	private static String getNomRefString(TaxonName taxonName) {
1730
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1731
		if (ref == null){
1732
			return null;
1733
		}
1734
		if (! ref.isProtectedAbbrevTitleCache()){
1735
			ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1736
		}
1737
		return ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1738
	}
1739

    
1740

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

    
1751
		NomenclaturalStatus state = getNameStatus(taxonName);
1752
		if (state != null) {
1753
			result = PesiTransformer.nomStatus2nomStatusFk(state.getType());
1754
		}
1755
		return result;
1756
	}
1757

    
1758
	/**
1759
	 * Returns the <code>NameStatusCache</code> attribute.
1760
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1761
	 * @return The <code>NameStatusCache</code> attribute.
1762
	 * @throws UndefinedTransformerMethodException
1763
	 * @see MethodMapper
1764
	 */
1765
	@SuppressWarnings("unused")
1766
	private static String getNameStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1767
		String result = null;
1768
		NomenclaturalStatus status = getNameStatus(taxonName);
1769
		if (status != null) {
1770
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1771
		}
1772
		return result;
1773
	}
1774

    
1775

    
1776
	private static NomenclaturalStatus getNameStatus(TaxonName taxonName) {
1777
		try {
1778
			if (taxonName != null) {
1779
			    Set<NomenclaturalStatus> states = taxonName.getStatus();
1780
				if (states.size() == 1) {
1781
					NomenclaturalStatus status = states.iterator().next();
1782
					return status;
1783
				} else if (states.size() > 1) {
1784
					logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1785
				}
1786
			}
1787

    
1788
		} catch (Exception e) {
1789
			e.printStackTrace();
1790
		}
1791
		return null;
1792
	}
1793
	/**
1794
	 * Returns the <code>TaxonStatusFk</code> attribute.
1795
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1796
	 * @param state The {@link PesiExportState PesiExportState}.
1797
	 * @return The <code>TaxonStatusFk</code> attribute.
1798
	 * @see MethodMapper
1799
	 */
1800
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1801
		Integer result = null;
1802

    
1803
		try {
1804
			if (isMisappliedName(taxon)) {
1805
				Synonym synonym = Synonym.NewInstance(null, null);
1806

    
1807
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1808
				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1809
			} else {
1810
				result = PesiTransformer.taxonBase2statusFk(taxon);
1811
			}
1812

    
1813
		} catch (Exception e) {
1814
			e.printStackTrace();
1815
		}
1816
		return result;
1817
	}
1818

    
1819
	/**
1820
	 * Returns the <code>TaxonStatusCache</code> attribute.
1821
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1822
	 * @param state The {@link PesiExportState PesiExportState}.
1823
	 * @return The <code>TaxonStatusCache</code> attribute.
1824
	 * @throws UndefinedTransformerMethodException
1825
	 * @see MethodMapper
1826
	 */
1827
	@SuppressWarnings("unused")
1828
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1829
		return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1830
	}
1831

    
1832
	/**
1833
	 * Returns the <code>TypeNameFk</code> attribute.
1834
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1835
	 * @param state The {@link PesiExportState PesiExportState}.
1836
	 * @return The <code>TypeNameFk</code> attribute.
1837
	 * @see MethodMapper
1838
	 */
1839
	private static Integer getTypeNameFk(TaxonName taxonNameBase, PesiExportState state) {
1840
		Integer result = null;
1841
		if (taxonNameBase != null) {
1842
			Set<NameTypeDesignation> nameTypeDesignations = taxonNameBase.getNameTypeDesignations();
1843
			if (nameTypeDesignations.size() == 1) {
1844
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1845
				if (nameTypeDesignation != null) {
1846
					TaxonName typeName = nameTypeDesignation.getTypeName();
1847
					if (typeName != null) {
1848
						result = state.getDbId(typeName);
1849
					}
1850
				}
1851
			} else if (nameTypeDesignations.size() > 1) {
1852
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonNameBase.getUuid() + " (" + taxonNameBase.getTitleCache() + ")");
1853
			}
1854
		}
1855
		return result;
1856
	}
1857

    
1858
	/**
1859
	 * Returns the <code>TypeFullnameCache</code> attribute.
1860
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1861
	 * @return The <code>TypeFullnameCache</code> attribute.
1862
	 * @see MethodMapper
1863
	 */
1864
	@SuppressWarnings("unused")
1865
	private static String getTypeFullnameCache(TaxonName taxonName) {
1866
		String result = null;
1867

    
1868
		try {
1869
		if (taxonName != null) {
1870
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1871
			if (nameTypeDesignations.size() == 1) {
1872
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1873
				if (nameTypeDesignation != null) {
1874
					TaxonName typeName = nameTypeDesignation.getTypeName();
1875
					if (typeName != null) {
1876
						result = typeName.getTitleCache();
1877
					}
1878
				}
1879
			} else if (nameTypeDesignations.size() > 1) {
1880
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1881
			}
1882
		}
1883

    
1884
		} catch (Exception e) {
1885
			e.printStackTrace();
1886
		}
1887
		return result;
1888
	}
1889

    
1890

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

    
1902

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

    
1915

    
1916
	/**
1917
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1918
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1919
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1920
	 * @see MethodMapper
1921
	 */
1922
	@SuppressWarnings("unused")
1923
	private static Integer getTypeDesignationStatusFk(TaxonName taxonName) {
1924
		Integer result = null;
1925

    
1926
		try {
1927
		if (taxonName != null) {
1928
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1929
			if (typeDesignations.size() == 1) {
1930
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1931
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1932
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1933
			} else if (typeDesignations.size() > 1) {
1934
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1935
			}
1936
		}
1937

    
1938
		} catch (Exception e) {
1939
			e.printStackTrace();
1940
		}
1941
		return result;
1942
	}
1943

    
1944
	/**
1945
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1946
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1947
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1948
	 * @see MethodMapper
1949
	 */
1950
	@SuppressWarnings("unused")
1951
	private static String getTypeDesignationStatusCache(TaxonName taxonName) {
1952
		String result = null;
1953

    
1954
		try {
1955
		if (taxonName != null) {
1956
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1957
			if (typeDesignations.size() == 1) {
1958
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1959
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1960
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
1961
			} else if (typeDesignations.size() > 1) {
1962
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1963
			}
1964
		}
1965

    
1966
		} catch (Exception e) {
1967
			e.printStackTrace();
1968
		}
1969
		return result;
1970
	}
1971

    
1972
	/**
1973
	 * Returns the <code>FossilStatusFk</code> attribute.
1974
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1975
	 * @return The <code>FossilStatusFk</code> attribute.
1976
	 * @see MethodMapper
1977
	 */
1978
	@SuppressWarnings("unused")
1979
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
1980
		Integer result = null;
1981

    
1982
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
1983
		if (fossilStatuus.size() == 0){
1984
			return null;
1985
		}else if (fossilStatuus.size() > 1){
1986
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
1987
		}
1988
		String fossilStatus = fossilStatuus.iterator().next();
1989

    
1990
		int statusFk = state.getTransformer().FossilStatusCache2FossilStatusFk(fossilStatus);
1991
		return statusFk;
1992
	}
1993

    
1994
	/**
1995
	 * Returns the <code>FossilStatusCache</code> attribute.
1996
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1997
	 * @return The <code>FossilStatusCache</code> attribute.
1998
	 * @see MethodMapper
1999
	 */
2000
	@SuppressWarnings("unused")
2001
	private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
2002
		String result = null;
2003
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
2004
		if (fossilStatuus.size() == 0){
2005
			return null;
2006
		}
2007
		for (String strFossilStatus : fossilStatuus){
2008
			result = CdmUtils.concat(";", result, strFossilStatus);
2009
		}
2010
		return result;
2011
	}
2012

    
2013
	/**
2014
	 * Returns the <code>IdInSource</code> attribute.
2015
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2016
	 * @return The <code>IdInSource</code> attribute.
2017
	 * @see MethodMapper
2018
	 */
2019
	@SuppressWarnings("unused")
2020
	private static String getIdInSource(IdentifiableEntity taxonName) {
2021
		String result = null;
2022

    
2023
		try {
2024
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
2025
			if (sources.size() > 1){
2026
				logger.warn("There is > 1 Pesi source. This is not yet handled.");
2027
			}
2028
			if (sources.size() == 0){
2029
				logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2030
			}
2031
			for (IdentifiableSource source : sources) {
2032
				Reference ref = source.getCitation();
2033
				UUID refUuid = ref.getUuid();
2034
				String idInSource = source.getIdInSource();
2035
				if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
2036
					result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
2037
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2038
					result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
2039
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2040
					result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
2041
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
2042
					result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
2043
				}else{
2044
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2045
				}
2046

    
2047
				String sourceIdNameSpace = source.getIdNamespace();
2048
				if (sourceIdNameSpace != null) {
2049
					if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
2050
						result =  idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
2051
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
2052
						result =  idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
2053
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
2054
						result =  idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
2055
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
2056
						result =  idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
2057
					}
2058
				}
2059
				if (result == null) {
2060
					logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
2061
				}
2062
			}
2063
		} catch (Exception e) {
2064
			e.printStackTrace();
2065
			logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
2066
		}
2067

    
2068
		if (result == null) {
2069
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2070
		}
2071
		return result;
2072
	}
2073

    
2074
	/**
2075
	 * Returns the idInSource for a given TaxonName only.
2076
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2077
	 * @return The idInSource.
2078
	 */
2079
	private static String getIdInSourceOnly(IdentifiableEntity identEntity) {
2080
		String result = null;
2081

    
2082
		// Get the sources first
2083
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2084

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

    
2102
		}
2103

    
2104
		return result;
2105
	}
2106

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

    
2115
		//Taxon Names
2116
		if (identEntity.isInstanceOf(TaxonName.class)){
2117
			// Sources from TaxonName
2118
		    TaxonName taxonName = CdmBase.deproxy(identEntity, TaxonName.class);
2119
			Set<IdentifiableSource> testSources = identEntity.getSources();
2120
			sources = filterPesiSources(identEntity.getSources());
2121

    
2122
			if (sources.size() == 0 && testSources.size()>0){
2123
				IdentifiableSource source = testSources.iterator().next();
2124
				logger.warn("There are sources, but they are no pesi sources!!!" + source.getIdInSource() + " - " + source.getIdNamespace() + " - " + source.getCitation().getTitleCache());
2125
			}
2126
			if (sources.size() > 1) {
2127
				logger.warn("This TaxonName has more than one Source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() + ")");
2128
			}
2129

    
2130
			// name has no PESI source, take sources from TaxonBase
2131
			if (sources == null || sources.isEmpty()) {
2132
				Set<TaxonBase> taxa = taxonName.getTaxonBases();
2133
				for (TaxonBase taxonBase: taxa){
2134
					sources.addAll(filterPesiSources(taxonBase.getSources()));
2135
				}
2136
			}
2137

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

    
2143
		/*TODO: deleted only for testing the inferred synonyms
2144
		if (sources == null || sources.isEmpty()) {
2145
			logger.warn("This TaxonName has no PESI Sources: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2146
		}else if (sources.size() > 1){
2147
			logger.warn("This Taxon(Name) has more than 1 PESI source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2148
		}
2149
		*/
2150
		return sources;
2151
	}
2152

    
2153
	// return all sources with a PESI reference
2154
	private static Set<IdentifiableSource> filterPesiSources(Set<? extends IdentifiableSource> sources) {
2155
		Set<IdentifiableSource> result = new HashSet<IdentifiableSource>();
2156
		for (IdentifiableSource source : sources){
2157
			Reference ref = source.getCitation();
2158
			UUID refUuid = ref.getUuid();
2159
			if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed) ||
2160
				refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)||
2161
				refUuid.equals(PesiTransformer.uuidSourceRefErms)||
2162
				refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum) ||
2163
				refUuid.equals(PesiTransformer.uuidSourceRefAuct)){
2164
				result.add(source);
2165
			}
2166
		}
2167
		return result;
2168
	}
2169

    
2170
	/**
2171
	 * Returns the <code>GUID</code> attribute.
2172
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2173
	 * @return The <code>GUID</code> attribute.
2174
	 * @see MethodMapper
2175
	 */
2176
	private static String getGUID(TaxonBase<?> taxon) {
2177
		if (taxon.getLsid() != null ){
2178
			return taxon.getLsid().getLsid();
2179
		}else if (taxon.hasMarker(PesiTransformer.uuidMarkerGuidIsMissing, true)){
2180
			return null;
2181
		}else{
2182
			return taxon.getUuid().toString();
2183
		}
2184
	}
2185

    
2186

    
2187

    
2188

    
2189
	/**
2190
	 * Returns the <code>DerivedFromGuid</code> attribute.
2191
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2192
	 * @return The <code>DerivedFromGuid</code> attribute.
2193
	 * @see MethodMapper
2194
	 */
2195
	@SuppressWarnings("unused")
2196
	private static String getDerivedFromGuid(TaxonBase<?> taxon) {
2197
		String result = null;
2198
		try {
2199
		// The same as GUID for now
2200
		result = getGUID(taxon);
2201
		} catch (Exception e) {
2202
			e.printStackTrace();
2203
		}
2204
		return result;
2205
	}
2206

    
2207
	/**
2208
	 * Returns the <code>CacheCitation</code> attribute.
2209
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2210
	 * @return The CacheCitation.
2211
	 * @see MethodMapper
2212
	 */
2213
	@SuppressWarnings("unused")
2214
	private static String getCacheCitation(TaxonBase taxon) {
2215
		// !!! See also doPhaseUpdates
2216

    
2217
		TaxonName taxonName = taxon.getName();
2218
		String result = "";
2219
		//TODO implement anew for taxa
2220
		try {
2221
			BitSet sources = getSources(taxonName);
2222
			if (sources.isEmpty()) {
2223
//				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2224
			} else if (sources.get(PesiTransformer.SOURCE_ERMS)) {
2225
				// 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...
2226
				// 		 So the following code is some kind of harmless assumption.
2227
				Set<Extension> extensions = taxonName.getExtensions();
2228
				for (Extension extension : extensions) {
2229
					if (extension.getType().equals(cacheCitationExtensionType)) {
2230
						result = extension.getValue();
2231
					}
2232
				}
2233
			} else {
2234
				String expertName = getExpertName(taxon);
2235
				String webShowName = getWebShowName(taxonName);
2236

    
2237
				// idInSource only
2238
				String idInSource = getIdInSourceOnly(taxonName);
2239

    
2240
				// build the cacheCitation
2241
				if (expertName != null) {
2242
					result += expertName + ". ";
2243
				} else {
2244
					if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
2245
				}
2246
				if (webShowName != null) {
2247
					result += webShowName + ". ";
2248
				} else {
2249
					logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2250
				}
2251

    
2252
				if (getOriginalDB(taxonName).equals("FaEu")) {
2253
					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
2254
				} else if (getOriginalDB(taxonName).equals("EM")) {
2255
					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
2256
				}
2257

    
2258
				if (idInSource != null) {
2259
					result += idInSource;
2260
				} else {
2261
					logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2262
				}
2263
			}
2264
		} catch (Exception e) {
2265
			e.printStackTrace();
2266
		}
2267

    
2268
		if (StringUtils.isBlank(result)) {
2269
			return null;
2270
		} else {
2271
			return result;
2272
		}
2273
	}
2274

    
2275
	/**
2276
	 * Returns the <code>OriginalDB</code> attribute.
2277
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2278
	 * @return The <code>OriginalDB</code> attribute.
2279
	 * @see MethodMapper
2280
	 */
2281
	private static String getOriginalDB(IdentifiableEntity identEntity) {
2282
		// Sources from TaxonName
2283
		BitSet sources  = getSources(identEntity);
2284
		return PesiTransformer.getOriginalDbBySources(sources);
2285
	}
2286

    
2287
	/**
2288
	 * Returns the <code>LastAction</code> attribute.
2289
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2290
	 * @return The <code>LastAction</code> attribute.
2291
	 * @see MethodMapper
2292
	 */
2293
	@SuppressWarnings("unused")
2294
	private static String getLastAction(IdentifiableEntity<?> identEntity) {
2295
		String result = null;
2296
		try {
2297
		Set<Extension> extensions = identEntity.getExtensions();
2298
		for (Extension extension : extensions) {
2299
			if (extension.getType().equals(lastActionExtensionType)) {
2300
				result = extension.getValue();
2301
			}
2302
		}
2303
		} catch (Exception e) {
2304
			e.printStackTrace();
2305
		}
2306
		return result;
2307
	}
2308

    
2309
	/**
2310
	 * Returns the <code>LastActionDate</code> attribute.
2311
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2312
	 * @return The <code>LastActionDate</code> attribute.
2313
	 * @see MethodMapper
2314
	 */
2315
	@SuppressWarnings({ "unused" })
2316
	private static DateTime getLastActionDate(IdentifiableEntity identEntity) {
2317
		DateTime result = null;
2318
		try {
2319
			Set<Extension> extensions = identEntity.getExtensions();
2320
			for (Extension extension : extensions) {
2321
				if (extension.getType().equals(lastActionDateExtensionType)) {
2322
					String dateTime = extension.getValue();
2323
					if (dateTime != null) {
2324
						DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
2325
						result = formatter.parseDateTime(dateTime);
2326
					}
2327
				}
2328
			}
2329
		} catch (Exception e) {
2330
			e.printStackTrace();
2331
		}
2332
		return result;
2333
	}
2334

    
2335
	/**
2336
	 * Returns the <code>ExpertName</code> attribute.
2337
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2338
	 * @return The <code>ExpertName</code> attribute.
2339
	 * @see MethodMapper
2340
	 */
2341
	@SuppressWarnings("unused")
2342
	private static String getExpertName(TaxonBase<?> taxonName) {
2343
		String result = null;
2344
		try {
2345
		Set<Extension> extensions = taxonName.getExtensions();
2346
		for (Extension extension : extensions) {
2347
			if (extension.getType().equals(expertNameExtensionType)) {
2348
				result = extension.getValue();
2349
			}
2350
		}
2351
		} catch (Exception e) {
2352
			e.printStackTrace();
2353
		}
2354
		return result;
2355
	}
2356

    
2357
	/**
2358
	 * Returns the <code>ExpertFk</code> attribute.
2359
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2360
	 * @param state The {@link PesiExportState PesiExportState}.
2361
	 * @return The <code>ExpertFk</code> attribute.
2362
	 * @see MethodMapper
2363
	 */
2364
	private static Integer getExpertFk(Reference reference, PesiExportState state) {
2365
		Integer result = state.getDbId(reference);
2366
		return result;
2367
	}
2368

    
2369
	/**
2370
	 * Returns the <code>SpeciesExpertName</code> attribute.
2371
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2372
	 * @return The <code>SpeciesExpertName</code> attribute.
2373
	 * @see MethodMapper
2374
	 */
2375
	@SuppressWarnings("unused")
2376
	private static String getSpeciesExpertName(TaxonBase<?> taxonName) {
2377
		String result = null;
2378
		try {
2379
		Set<Extension> extensions = taxonName.getExtensions();
2380
		for (Extension extension : extensions) {
2381
			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2382
				result = extension.getValue();
2383
			}
2384
		}
2385
		} catch (Exception e) {
2386
			e.printStackTrace();
2387
		}
2388
		return result;
2389
	}
2390

    
2391
	/**
2392
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2393
	 * @param reference The {@link Reference Reference}.
2394
	 * @param state The {@link PesiExportState PesiExportState}.
2395
	 * @return The <code>SpeciesExpertFk</code> attribute.
2396
	 * @see MethodMapper
2397
	 */
2398
	private static Integer getSpeciesExpertFk(Reference reference, PesiExportState state) {
2399
		Integer result = state.getDbId(reference);
2400
		return result;
2401
	}
2402

    
2403

    
2404
	/**
2405
	 * Returns the source (E+M, Fauna Europaea, Index Fungorum, ERMS) of a given
2406
	 * Identifiable Entity as a BitSet
2407
	 * @param identEntity
2408
	 * @return
2409
	 */
2410
	private static BitSet getSources(IdentifiableEntity<?> identEntity){
2411
		BitSet bitSet = new BitSet();
2412
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2413
		for (IdentifiableSource source : sources) {
2414
			Reference ref = source.getCitation();
2415
			UUID refUuid = ref.getUuid();
2416
			if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
2417
				bitSet.set(PesiTransformer.SOURCE_EM);
2418
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2419
				bitSet.set(PesiTransformer.SOURCE_FE);
2420
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2421
				bitSet.set(PesiTransformer.SOURCE_ERMS);
2422
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){
2423
				bitSet.set(PesiTransformer.SOURCE_IF);
2424
			}else{
2425
				if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2426
			}
2427
		}
2428
		return bitSet;
2429

    
2430
	}
2431

    
2432
	protected static NonViralNameDefaultCacheStrategy getCacheStrategy(TaxonName taxonName) {
2433
		taxonName = CdmBase.deproxy(taxonName);
2434
		NonViralNameDefaultCacheStrategy cacheStrategy;
2435
		if (taxonName.isZoological()){
2436
			cacheStrategy = zooNameStrategy;
2437
		}else if (taxonName.isBotanical()) {
2438
			cacheStrategy = nonViralNameStrategy;
2439
		}else if (taxonName.isNonViral()) {
2440
			cacheStrategy = nonViralNameStrategy;
2441
		}else if (taxonName.isBacterial()) {
2442
			cacheStrategy = nonViralNameStrategy;
2443
		}else{
2444
			logger.error("Unhandled taxon name type. Can't define strategy class");
2445
			cacheStrategy = nonViralNameStrategy;
2446
		}
2447
		return cacheStrategy;
2448
	}
2449

    
2450
	/**
2451
	 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2452
	 * @param relationship The {@link RelationshipBase Relationship}.
2453
	 * @return The <code>RelTaxonQualifierFk</code> attribute.
2454
	 * @see MethodMapper
2455
	 */
2456
	@SuppressWarnings("unused")
2457
	private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2458
		return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2459
	}
2460
	/**
2461
	 * Returns the <code>Notes</code> attribute.
2462
	 * @param relationship The {@link RelationshipBase Relationship}.
2463
	 * @return The <code>Notes</code> attribute.
2464
	 * @see MethodMapper
2465
	 */
2466
	@SuppressWarnings("unused")
2467
	private static String getNotes(RelationshipBase<?, ?, ?> relationship) {
2468
		// TODO
2469
		return null;
2470
	}
2471

    
2472

    
2473
	/**
2474
	 * Returns the CDM to PESI specific export mappings.
2475
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2476
	 */
2477
	private PesiExportMapping getMapping() {
2478
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2479

    
2480
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2481
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2482
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2483
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2484

    
2485
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2486

    
2487
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2488
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2489
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misallied Names are handled differently
2490
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2491

    
2492
		// DisplayName
2493
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2494

    
2495
		// FossilStatus (Fk, Cache)
2496
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2497
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2498

    
2499
		//handled by name mapping
2500
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2501
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2502

    
2503
		//experts
2504
		ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
2505
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2506
		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
2507
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2508

    
2509
//		mapping.addMapper(MethodMapper.NewInstance("ParentTaxonFk", this, TaxonBase.class, PesiExportState.class));  //by AM, doesn't work, FK exception
2510
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2511

    
2512
		addNameMappers(mapping);
2513

    
2514
		return mapping;
2515
	}
2516

    
2517
	/**
2518
	 * Returns the CDM to PESI specific export mappings.
2519
	 * @param state
2520
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2521
	 * @throws UndefinedTransformerMethodException
2522
	 */
2523
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2524
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2525

    
2526
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2527

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

    
2530
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonName.class));
2531
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonName.class));
2532
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonName.class, PesiExportState.class));
2533
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
2534
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2535
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
2536
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonName.class));
2537
		mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonName.class));
2538

    
2539

    
2540

    
2541
		// DisplayName
2542
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonName.class));
2543

    
2544
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2545
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2546

    
2547
		addNameMappers(mapping);
2548
		//TODO add author mapper, TypeNameFk
2549

    
2550
		return mapping;
2551
	}
2552

    
2553
	private void addNameMappers(PesiExportMapping mapping) {
2554
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2555
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2556
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2557
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2558

    
2559
//		mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName"));  //does not work as we need other cache strategy
2560
		mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonName.class));
2561

    
2562
//		mapping.addMapper(DbStringMapper.NewInstance("TitleCache", "FullName"));    //does not work as we need other cache strategy
2563
		mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonName.class));
2564

    
2565

    
2566
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonName.class));
2567

    
2568
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonName.class));
2569
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonName.class, PesiExportState.class));
2570
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
2571
		//TODO TypeNameFk
2572

    
2573
		//quality status
2574
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonName.class));
2575
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonName.class, PesiExportState.class));
2576

    
2577
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2578
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2579

    
2580
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2581

    
2582
	}
2583

    
2584
	private PesiExportMapping getSynRelMapping() {
2585
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2586

    
2587
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getTaxonFk1", RelationshipBase.class, PesiExportState.class));
2588
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk2", this.getClass(), "getTaxonFk2", RelationshipBase.class, PesiExportState.class));
2589
		mapping.addMapper(MethodMapper.NewInstance("RelTaxonQualifierFk", this,  RelationshipBase.class));
2590
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this, RelationshipBase.class, PesiExportState.class));
2591
		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2592

    
2593
		return mapping;
2594
	}
2595

    
2596
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state)  throws UndefinedTransformerMethodException{
2597
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2598

    
2599
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2600
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2601

    
2602
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2603
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2604
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonName.class));
2605

    
2606

    
2607
		//we have only nomenclatural references here
2608
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2609
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2610

    
2611
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2612

    
2613
		return mapping;
2614
	}
2615

    
2616
}
(11-11/12)