Project

General

Profile

Download (96.2 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.TaxonNameDefaultCacheStrategy;
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
 * @since 23.02.2010
91
 *
92
 */
93
@Component
94
public class PesiTaxonExport extends PesiExportBase {
95
    private static final long serialVersionUID = -3412722058790200078L;
96
    private static final Logger logger = Logger.getLogger(PesiTaxonExport.class);
97

    
98
	private static final Class<? extends CdmBase> standardMethodParameter = TaxonBase.class;
99

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

    
125

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

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

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

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

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

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

    
163

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

    
169
			initPreparedStatements(state);
170

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

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

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

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

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

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

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

    
200

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

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

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

    
210

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

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

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

    
227

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

    
233
		initParentFkStatement(state);
234
	}
235

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

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

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

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

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

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

    
275

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

    
281

    
282

    
283
		int partitionCount = 0;
284

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

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

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

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

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

    
330

    
331

    
332
			}
333

    
334

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

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

    
352

    
353

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

    
366

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

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

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

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

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

    
426

    
427

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

    
442
		List<Taxon> list;
443

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

    
447
		insertBiota(state);
448

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

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

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

    
469
			}
470

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

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

    
487
		return success;
488

    
489
	}
490

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

    
510
	}
511

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

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

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

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

    
525
		return success;
526

    
527
	}
528

    
529

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

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

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

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

    
550
		StringBuffer treeIndex = new StringBuffer();
551

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

    
559
		logger.info("Fetched " + classificationList.size() + " classification(s).");
560

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

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

    
569
				rankSpecificRootNodes = getClassificationService().listRankSpecificRootNodes(classification, rank,
570
				        includeUnpublished, null, null, null);
571
				logger.info("Fetched " + rankSpecificRootNodes.size() + " RootNodes for Rank " + rank.getLabel());
572

    
573
				commitTransaction(txStatus);
574
				logger.debug("Committed transaction.");
575

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

    
585
					TaxonNode newNode = getTaxonNodeService().load(rootNode.getUuid());
586

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

    
627
					newNode = null;
628

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

    
637
				}
638
				rankSpecificRootNodes = null;
639
			}
640

    
641
		}
642

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

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

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

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

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

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

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

    
690
					taxon = null;
691
					taxonName = null;
692
			}
693

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

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

    
708
		list = null;
709

    
710
		// Commit transaction
711
		commitTransaction(txStatus);
712

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

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

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

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

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

    
760
		// Determine the count of elements in datawarehouse database table Taxon
761
		currentTaxonId = determineTaxonCount(state);
762
		currentTaxonId++;
763

    
764
		count = 0;
765
		pastCount = 0;
766
		int pageSize = limit/10;
767
		int pageNumber = 1;
768
		String inferredSynonymPluralString = "Inferred Synonyms";
769

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

    
775

    
776

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

    
780
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
781
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
782
					synRelMapping, taxonList));
783

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

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

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

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

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

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

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

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

    
835
		taxonList = null;
836
//		logger.warn("Taking snapshot at the end of phase 4 of taxonExport");
837
//		ProfilerController.memorySnapshot();
838

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

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

    
860
		Taxon acceptedTaxon;
861
		Classification classification = null;
862
		List<Synonym> inferredSynonyms = null;
863
		boolean localSuccess = true;
864

    
865
		HashMap<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonName>();
866

    
867
		for (TaxonBase<?> taxonBase : taxonList) {
868

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

    
873
				if (taxonName.isZoological()) {
874
					nomenclaturalCode  = taxonName.getNameType();
875
					kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
876

    
877
					Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
878
					TaxonNode singleNode = null;
879

    
880
					if (taxonNodes.size() > 0) {
881
						// Determine the classification of the current TaxonNode
882

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

    
895
						}
896
					}
897

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

    
913

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

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

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

    
966

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

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

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

    
991

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

    
997
			int partitionCount = 0;
998
			while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null   ) {
999

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

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

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

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

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

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

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

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

    
1146
							localTreeIndex.append(taxonId + "#");
1147

    
1148
							saveData(childNode, parentNode, localTreeIndex, state, taxonId);
1149

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

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

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

    
1176
		} else {
1177
			logger.error("Taxon is NULL for TaxonNode: " + childNode.getUuid());
1178
		}
1179
	}
1180

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

    
1198
			}
1199

    
1200
			invokeParentTaxonFkAndTreeIndex(state.getDbId(parentTaxon), currentTaxonFk,	treeIndex);
1201
		}
1202

    
1203
	}
1204

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

    
1220
			if (treeIndex != null) {
1221
				parentTaxonFk_TreeIndex_KingdomFkStmt.setString(2, treeIndex.toString());
1222
			} else {
1223
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(2, null);
1224
			}
1225

    
1226
			if (currentTaxonFk != null) {
1227
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(3, currentTaxonFk);
1228
			} else {
1229
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(3, null);
1230
			}
1231

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

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

    
1254

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

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

    
1280
			if (kingdomFk != null) {
1281

    
1282
				rankUpdateStmt.setInt(3, kingdomFk);
1283
			} else {
1284
				rankUpdateStmt.setObject(3, null);
1285
			}
1286

    
1287
			if (taxonFk != null) {
1288
				rankUpdateStmt.setInt(4, taxonFk);
1289
			} else {
1290
				rankUpdateStmt.setObject(4, null);
1291
			}
1292

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

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

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

    
1332
			if (typeNameFk != null) {
1333
				rankTypeExpertsUpdateStmt.setInt(index++, typeNameFk);
1334
			} else {
1335
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1336
			}
1337

    
1338
			if (kingdomFkk != null) {
1339
				rankTypeExpertsUpdateStmt.setInt(index++, kingdomFkk);
1340
			} else {
1341
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1342
			}
1343

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

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

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

    
1384
		String sql;
1385
		Source destination =  pesiConfig.getDestination();
1386

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

    
1394
	@Override
1395
	protected boolean isIgnore(PesiExportState state) {
1396
		return ! state.getConfig().isDoTaxa();
1397
	}
1398

    
1399

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

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

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

    
1447

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

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

    
1487

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

    
1504
	}
1505

    
1506

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

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

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

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

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

    
1577
	}
1578

    
1579

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

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

    
1605
	@SuppressWarnings("unused")
1606
	private static String getGUID(TaxonName taxonName) {
1607
		UUID uuid = taxonName.getUuid();
1608
		String result = "NameUUID:" + uuid.toString();
1609
		return result;
1610
	}
1611

    
1612

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

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

    
1642
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1643
			String result = cacheStrategy.getTitleCache(taxonName, tagRules);
1644
			return result;
1645
		}
1646
	}
1647

    
1648

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

    
1663

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

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

    
1704

    
1705

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

    
1721
		return result;
1722
	}
1723
*/
1724

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

    
1744

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

    
1755
		NomenclaturalStatus state = getNameStatus(taxonName);
1756
		if (state != null) {
1757
			result = PesiTransformer.nomStatus2nomStatusFk(state.getType());
1758
		}
1759
		return result;
1760
	}
1761

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

    
1779

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

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

    
1807
		try {
1808
			if (isMisappliedName(taxon)) {
1809
				Synonym synonym = Synonym.NewInstance(null, null);
1810

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

    
1817
		} catch (Exception e) {
1818
			e.printStackTrace();
1819
		}
1820
		return result;
1821
	}
1822

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

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

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

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

    
1888
		} catch (Exception e) {
1889
			e.printStackTrace();
1890
		}
1891
		return result;
1892
	}
1893

    
1894

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

    
1906

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

    
1919

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

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

    
1942
		} catch (Exception e) {
1943
			e.printStackTrace();
1944
		}
1945
		return result;
1946
	}
1947

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

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

    
1970
		} catch (Exception e) {
1971
			e.printStackTrace();
1972
		}
1973
		return result;
1974
	}
1975

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

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

    
1994
		int statusFk = state.getTransformer().fossilStatusCache2FossilStatusFk(fossilStatus);
1995
		return statusFk;
1996
	}
1997

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

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

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

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

    
2072
		if (result == null) {
2073
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2074
		}
2075
		return result;
2076
	}
2077

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

    
2086
		// Get the sources first
2087
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2088

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

    
2106
		}
2107

    
2108
		return result;
2109
	}
2110

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

    
2119
		//Taxon Names
2120
		if (identEntity.isInstanceOf(TaxonName.class)){
2121
			// Sources from TaxonName
2122
		    TaxonName taxonName = CdmBase.deproxy(identEntity, TaxonName.class);
2123
			Set<IdentifiableSource> testSources = identEntity.getSources();
2124
			sources = filterPesiSources(identEntity.getSources());
2125

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

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

    
2142
		//for TaxonBases
2143
		}else if (identEntity.isInstanceOf(TaxonBase.class)){
2144
			sources = filterPesiSources(identEntity.getSources());
2145
		}
2146

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

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

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

    
2190

    
2191

    
2192

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

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

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

    
2241
				// idInSource only
2242
				String idInSource = getIdInSourceOnly(taxonName);
2243

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

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

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

    
2272
		if (StringUtils.isBlank(result)) {
2273
			return null;
2274
		} else {
2275
			return result;
2276
		}
2277
	}
2278

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

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

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

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

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

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

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

    
2407

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

    
2434
	}
2435

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

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

    
2476

    
2477
	/**
2478
	 * Returns the CDM to PESI specific export mappings.
2479
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2480
	 */
2481
	private PesiExportMapping getMapping() {
2482
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2483

    
2484
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2485
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2486
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2487
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2488

    
2489
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2490

    
2491
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2492
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2493
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misallied Names are handled differently
2494
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2495

    
2496
		// DisplayName
2497
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2498

    
2499
		// FossilStatus (Fk, Cache)
2500
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2501
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2502

    
2503
		//handled by name mapping
2504
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2505
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2506

    
2507
		//experts
2508
		ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
2509
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2510
		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
2511
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2512

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

    
2516
		addNameMappers(mapping);
2517

    
2518
		return mapping;
2519
	}
2520

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

    
2530
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2531

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

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

    
2543

    
2544

    
2545
		// DisplayName
2546
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonName.class));
2547

    
2548
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2549
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2550

    
2551
		addNameMappers(mapping);
2552
		//TODO add author mapper, TypeNameFk
2553

    
2554
		return mapping;
2555
	}
2556

    
2557
	private void addNameMappers(PesiExportMapping mapping) {
2558
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2559
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2560
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2561
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2562

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

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

    
2569

    
2570
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonName.class));
2571

    
2572
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonName.class));
2573
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonName.class, PesiExportState.class));
2574
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
2575
		//TODO TypeNameFk
2576

    
2577
		//quality status
2578
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonName.class));
2579
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonName.class, PesiExportState.class));
2580

    
2581
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2582
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2583

    
2584
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2585

    
2586
	}
2587

    
2588
	private PesiExportMapping getSynRelMapping() {
2589
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2590

    
2591
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getTaxonFk1", RelationshipBase.class, PesiExportState.class));
2592
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk2", this.getClass(), "getTaxonFk2", RelationshipBase.class, PesiExportState.class));
2593
		mapping.addMapper(MethodMapper.NewInstance("RelTaxonQualifierFk", this,  RelationshipBase.class));
2594
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this, RelationshipBase.class, PesiExportState.class));
2595
		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2596

    
2597
		return mapping;
2598
	}
2599

    
2600
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state)  throws UndefinedTransformerMethodException{
2601
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2602

    
2603
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2604
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2605

    
2606
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2607
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2608
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonName.class));
2609

    
2610

    
2611
		//we have only nomenclatural references here
2612
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2613
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2614

    
2615
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2616

    
2617
		return mapping;
2618
	}
2619

    
2620
}
(11-11/12)