Project

General

Profile

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

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

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

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

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

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

    
136

    
137
	/**
138
	 * @return the treeIndexAnnotationType
139
	 */
140
	protected AnnotationType getTreeIndexAnnotationType() {
141
		return treeIndexAnnotationType;
142
	}
143

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

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

    
159
	public PesiTaxonExport() {
160
		super();
161
	}
162

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

    
171
	/* (non-Javadoc)
172
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
173
	 */
174
	@Override
175
	protected boolean doCheck(PesiExportState state) {
176
		boolean result = true;
177
		return result;
178
	}
179

    
180

    
181
	/* (non-Javadoc)
182
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
183
	 */
184
	@Override
185
	protected void doInvoke(PesiExportState state) {
186
		try {
187
			logger.info("*** Started Making " + pluralString + " ...");
188

    
189
			initPreparedStatements(state);
190

    
191
			// Stores whether this invoke was successful or not.
192
			boolean success = true;
193

    
194
			// PESI: Clear the database table Taxon.
195
//			doDelete(state);
196

    
197
			// Get specific mappings: (CDM) Taxon -> (PESI) Taxon
198
			PesiExportMapping mapping = getMapping();
199
			PesiExportMapping synonymRelMapping = getSynRelMapping();
200
			PesiExportMapping additionalSourceMapping = getAdditionalSourceMapping(state);
201

    
202
			// Initialize the db mapper
203
			mapping.initialize(state);
204
			synonymRelMapping.initialize(state);
205
			additionalSourceMapping.initialize(state);
206

    
207
			// Find extensionTypes
208
			lastActionExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionUuid);
209
			lastActionDateExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionDateUuid);
210
			expertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
211
			speciesExpertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
212
			cacheCitationExtensionType = (ExtensionType)getTermService().find(PesiTransformer.cacheCitationUuid);
213

    
214
			//Export Taxa..
215
			success &= doPhase01(state, mapping, additionalSourceMapping);
216

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

    
220

    
221
			// 2nd Round: Add ParentTaxonFk to each taxon
222
			success &= doPhase02(state);
223

    
224
			//PHASE 3: Add Rank data, KingdomFk, TypeNameFk ...
225
			success &= doPhase03(state);
226

    
227
			// 4nd Round: Add TreeIndex to each taxon
228
			success &= doPhase04(state);
229

    
230

    
231
			//"PHASE 4: Creating Inferred Synonyms...
232
			success &= doPhase05(state, mapping, synonymRelMapping);
233

    
234
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
235

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

    
247

    
248
	private void initPreparedStatements(PesiExportState state) throws SQLException {
249
		initTreeIndexStatement(state);
250
		initRankExpertsUpdateStmt(state);
251
		initRankUpdateStatement(state);
252

    
253
		initParentFkStatement(state);
254
	}
255

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

    
263
	// Prepare TreeIndex-And-KingdomFk-Statement
264
	private void initParentFkStatement(PesiExportState state) throws SQLException {
265
		Connection connection = state.getConfig().getDestination().getConnection();
266
		String parentTaxonFkSql = "UPDATE Taxon SET ParentTaxonFk = ? WHERE TaxonId = ?";
267
		parentTaxonFkStmt = connection.prepareStatement(parentTaxonFkSql);
268
	}
269

    
270
	private void initRankUpdateStatement(PesiExportState state) throws SQLException {
271
		Connection connection = state.getConfig().getDestination().getConnection();
272
		String rankSql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, KingdomFk = ? WHERE TaxonId = ?";
273
		rankUpdateStmt = connection.prepareStatement(rankSql);
274
	}
275

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

    
282
		String sql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ? " +
283
				" WHERE TaxonId = ?";
284
		rankTypeExpertsUpdateStmt = connection.prepareStatement(sql);
285
	}
286

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

    
295

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

    
301

    
302

    
303
		int partitionCount = 0;
304

    
305
		logger.info("Taking snapshot at the beginning of phase 1 of taxonExport");
306
		//ProfilerController.memorySnapshot();
307
		while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)) != null   ) {
308

    
309
			logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
310

    
311
			for (TaxonBase<?> taxon : list) {
312
				doCount(count++, modCount, pluralString);
313
				TaxonNameBase<?,?> taxonName = taxon.getName();
314

    
315
				NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
316
				System.err.println(nvn.getTitleCache());
317
				if (! nvn.isProtectedTitleCache()){
318
					nvn.setTitleCache(null, false);
319
				}
320
				if (! nvn.isProtectedNameCache()){
321
					nvn.setNameCache(null, false);
322
				}
323
				if (! nvn.isProtectedFullTitleCache()){
324
					nvn.setFullTitleCache(null, false);
325
				}
326
				if (! nvn.isProtectedAuthorshipCache()){
327
					nvn.setAuthorshipCache(null, false);
328
				}
329
				try{
330
				if (nvn.getRank().equals(Rank.KINGDOM())){
331
				    String treeIndex = ((Taxon)taxon).getTaxonNodes().iterator().next().treeIndex();
332
				    Integer kingdomId = PesiTransformer.pesiKingdomMap.get(nvn.getGenusOrUninomial());
333
				    state.getTreeIndexKingdomMap().put(treeIndex, kingdomId);
334
				}}catch(NullPointerException e){
335
				    logger.error(nvn.getTitleCache() + "has no Rank!");
336
				    System.err.println(nvn.getTitleCache() + "has no Rank!");
337
				}
338
				//core mapping
339
				success &= mapping.invoke(taxon);
340
				//additional source
341
				if (nvn.getNomenclaturalReference() != null || StringUtils.isNotBlank(nvn.getNomenclaturalMicroReference() )){
342
					additionalSourceMapping.invoke(taxon);
343
				}
344

    
345
				validatePhaseOne(taxon, nvn);
346
				taxon = null;
347
				nvn = null;
348
				taxonName = null;
349

    
350

    
351

    
352
			}
353

    
354

    
355
			// Commit transaction
356
			commitTransaction(txStatus);
357
			logger.debug("Committed transaction.");
358
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
359
			pastCount = count;
360
			/*logger.warn("Taking snapshot at the end of the loop of phase 1 of taxonExport");
361
			//ProfilerController.memorySnapshot();
362
			*/
363
			// Start transaction
364
			txStatus = startTransaction(true);
365
			logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
366

    
367
		}
368
		if (list == null ) {
369
			logger.info("No " + pluralString + " left to fetch.");
370
		}
371

    
372

    
373

    
374
		// Commit transaction
375
		commitTransaction(txStatus);
376
		txStatus = null;
377
		logger.debug("Committed transaction.");
378
		list = null;
379
		if (logger.isDebugEnabled()){
380
			logger.debug("Taking snapshot at the end of phase 1 of taxonExport");
381
			ProfilerController.memorySnapshot();
382
		}
383
		return success;
384
	}
385

    
386

    
387
	private void validatePhaseOne(TaxonBase<?> taxon, NonViralName taxonName) {
388
		// Check whether some rules are violated
389
		nomenclaturalCode = taxonName.getNomenclaturalCode();
390
		String genusOrUninomial = taxonName.getGenusOrUninomial();
391
		String specificEpithet = taxonName.getSpecificEpithet();
392
		String infraSpecificEpithet = taxonName.getInfraSpecificEpithet();
393
		String infraGenericEpithet = taxonName.getInfraGenericEpithet();
394
		Integer rank = getRankFk(taxonName, nomenclaturalCode);
395

    
396
		if (rank == null) {
397
			logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
398
		} else {
399

    
400
			// Check whether infraGenericEpithet is set correctly
401
			// 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
402
			// 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
403

    
404
			int ancestorLevel = 0;
405
			if (taxonName.getRank().equals(Rank.SUBSPECIES())) {
406
				// The accepted taxon two rank levels above should be of rank subgenus
407
				ancestorLevel  = 2;
408
			}
409
			if (taxonName.getRank().equals(Rank.SPECIES())) {
410
				// The accepted taxon one rank level above should be of rank subgenus
411
				ancestorLevel = 1;
412
			}
413
			if (ancestorLevel > 0) {
414
				if (validateAncestorOfSpecificRank(taxon, ancestorLevel, Rank.SUBGENUS())) {
415
					// The child (species or subspecies) of this parent (subgenus) has to have an infraGenericEpithet
416
					if (infraGenericEpithet == null) {
417
						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() + ")");
418
						// maybe the taxon could be named here
419
					}
420
				}
421
			}
422

    
423
			if (infraGenericEpithet == null && rank.intValue() == 190) {
424
				logger.warn("InfraGenericEpithet was not determined although it should exist for rank 190: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
425
			}
426
			if (specificEpithet != null && rank.intValue() < 216) {
427
				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() + ")");
428
			}
429
			if (infraSpecificEpithet != null && rank.intValue() < 225) {
430
				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() + ")";
431
				if (StringUtils.isNotBlank(infraSpecificEpithet)){
432
					logger.warn(message);
433
				}else{
434
					logger.warn(message);
435
				}
436
			}
437
		}
438
		if (infraSpecificEpithet != null && specificEpithet == null) {
439
			logger.warn("An infraSpecificEpithet was determined, but a specificEpithet was not determined: "  + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
440
		}
441
		if (genusOrUninomial == null) {
442
			logger.warn("GenusOrUninomial was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
443
		}
444
	}
445

    
446

    
447

    
448
	/**
449
	 * 2nd Round: Add ParentTaxonFk to each taxon and add Biota if not exists
450
	 * @param state
451
	 * @return
452
	 */
453
	private boolean doPhase02(PesiExportState state) {
454
		int count = 0;
455
		int pastCount = 0;
456
		boolean success = true;
457
		if (! state.getConfig().isDoParentAndBiota()){
458
			logger.info ("Ignore PHASE 2: Make ParentFk and Biota...");
459
			return success;
460
		}
461

    
462
		List<Taxon> list;
463

    
464
		// Get the limit for objects to save within a single transaction.
465
		int limit = state.getConfig().getLimitSave();
466

    
467
		insertBiota(state);
468

    
469
		logger.info("PHASE 2: Make ParentFk and Biota ... limit is " + limit);
470
		// Start transaction
471
		TransactionStatus txStatus = startTransaction(true);
472
		int partitionCount = 0;
473

    
474
//		ProfilerController.memorySnapshot();
475
		while ((list = getNextTaxonPartition(Taxon.class, limit, partitionCount++, null)) != null   ) {
476

    
477
			logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
478
			for (Taxon taxon : list) {
479
				for (TaxonNode node : taxon.getTaxonNodes()){
480
					doCount(count++, modCount, pluralString);
481
					TaxonNode parentNode = node.getParent();
482
					if (parentNode != null && parentNode.getTaxon() != null){  //new root node handling requires has root taxon with taxon == null
483
						int childId = state.getDbId( taxon);
484
						int parentId = state.getDbId(parentNode.getTaxon());
485
						success &= invokeParentTaxonFk(parentId, childId);
486
					}
487
				}
488

    
489
			}
490

    
491
			// Commit transaction
492
			commitTransaction(txStatus);
493
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
494
			pastCount = count;
495
			// Start transaction
496
			txStatus = startTransaction(true);
497
			logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
498

    
499
		}
500
		if (list == null ) {
501
			logger.info("No " + pluralString + " left to fetch.");
502
		}
503
		list = null;
504
		// Commit transaction
505
		commitTransaction(txStatus);
506

    
507
		return success;
508

    
509
	}
510

    
511
	/**
512
	 * Inserts the Biota Taxon if not yet exists.
513
	 * @param state
514
	 * @throws SQLException
515
	 */
516
	private void insertBiota(PesiExportState state) {
517
		try {
518
			ResultSet rs = state.getConfig().getDestination().getResultSet("SELECT * FROM Taxon WHERE GenusOrUninomial = 'Biota' ");
519
			if (rs.next() == false){
520
				int biotaId = state.getConfig().getNameIdStart() -1 ;
521
				String sqlInsertBiota = "INSERT INTO Taxon (TaxonId, KingdomFk, RankFk, RankCache, GenusOrUninomial, WebSearchName, WebShowName, FullName, DisplayName, TaxonStatusFk, TaxonStatusCache) " +
522
									       " VALUES (" + biotaId + ",    0,    0,   'Superdomain',   'Biota',          'Biota',  '<i>Biota</i>',   'Biota', '<i>Biota</i>',  1 ,      'accepted')";
523
				state.getConfig().getDestination().update(sqlInsertBiota);
524
			}
525
			rs = null;
526
		} catch (SQLException e) {
527
			logger.warn ("Biota could not be requested or inserted");
528
		}
529

    
530
	}
531

    
532
	// 4th round: Add TreeIndex to each taxon
533
	private boolean doPhase04(PesiExportState state) {
534
		boolean success = true;
535

    
536
		logger.info("PHASE 4: Make TreeIndex ... ");
537

    
538
		//TODO test if possible to move to phase 02
539
		String sql = " UPDATE Taxon SET ParentTaxonFk = (Select TaxonId from Taxon where RankFk = 0) " +
540
				" WHERE (RankFk = 10) and TaxonStatusFk = 1 ";
541
		state.getConfig().getDestination().update(sql);
542

    
543
		state.getConfig().getDestination().update("EXEC dbo.recalculateallstoredpaths");
544

    
545
		return success;
546

    
547
	}
548

    
549

    
550
	// 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
551
	private boolean doPhase02_OLD(PesiExportState state) {
552
		boolean success = true;
553
		if (! state.getConfig().isDoTreeIndex()){
554
			logger.info ("Ignore PHASE 2: ParentTaxonFk and TreeIndex");
555
			return success;
556
		}
557

    
558
		List<Classification> classificationList = null;
559
		logger.info("PHASE 2: Add ParenTaxonFk and TreeIndex...");
560

    
561
		// Specify starting ranks for tree traversing
562
		rankList.add(Rank.KINGDOM());
563
		rankList.add(Rank.GENUS());
564

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

    
569
		StringBuffer treeIndex = new StringBuffer();
570

    
571
		// Retrieve list of classifications
572
		TransactionStatus txStatus = startTransaction(true);
573
		logger.info("Started transaction for parentFk and treeIndex. Fetching all classifications...");
574
		classificationList = getClassificationService().listClassifications(null, 0, null, null);
575
		commitTransaction(txStatus);
576
		logger.debug("Committed transaction.");
577

    
578
		logger.info("Fetched " + classificationList.size() + " classification(s).");
579

    
580
		setTreeIndexAnnotationType(getAnnotationType(uuidTreeIndex, "TreeIndex", "TreeIndex", "TI"));
581
		List<TaxonNode> rankSpecificRootNodes;
582
		for (Classification classification : classificationList) {
583
			for (Rank rank : rankList) {
584

    
585
				txStatus = startTransaction(true);
586
				logger.info("Started transaction to fetch all rootNodes specific to Rank " + rank.getLabel() + " ...");
587

    
588
				rankSpecificRootNodes = getClassificationService().listRankSpecificRootNodes(classification, rank, null, null, null);
589
				logger.info("Fetched " + rankSpecificRootNodes.size() + " RootNodes for Rank " + rank.getLabel());
590

    
591
				commitTransaction(txStatus);
592
				logger.debug("Committed transaction.");
593

    
594
				for (TaxonNode rootNode : rankSpecificRootNodes) {
595
					txStatus = startTransaction(false);
596
					Rank endRank = rank2endRankMap.get(rank);
597
					if (endRank != null) {
598
						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till Rank " + endRank.getLabel() + " ...");
599
					} else {
600
						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till leaves are reached ...");
601
					}
602

    
603
					TaxonNode newNode = getTaxonNodeService().load(rootNode.getUuid());
604

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

    
645
					newNode = null;
646

    
647
					try {
648
						commitTransaction(txStatus);
649
						logger.debug("Committed transaction.");
650
					} catch (Exception e) {
651
						logger.error(e.getMessage());
652
						e.printStackTrace();
653
					}
654

    
655
				}
656
				rankSpecificRootNodes = null;
657
			}
658

    
659
		}
660

    
661
		logger.warn("Taking snapshot at the end of phase 2 of taxonExport");
662
		//ProfilerController.memorySnapshot();
663
		return success;
664
	}
665

    
666
	//PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...
667
	private boolean doPhase03(PesiExportState state) {
668
		int count = 0;
669
		int pastCount = 0;
670
		boolean success = true;
671
		if (! state.getConfig().isDoTreeIndex()){
672
			logger.info ("Ignore PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
673
			return success;
674
		}
675
		// Get the limit for objects to save within a single transaction.
676
		int limit = state.getConfig().getLimitSave();
677

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

    
682
		// Start transaction
683
		TransactionStatus txStatus = startTransaction(true);
684
		logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
685
		int partitionCount = 0;
686
		while ((list = getNextTaxonPartition(TaxonBase.class, limit, partitionCount++, null)) != null) {
687

    
688
			logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
689
			for (TaxonBase<?> taxon : list) {
690
				TaxonNameBase<?,?> taxonName = taxon.getName();
691
				// Determine expertFk
692
//				Integer expertFk = makeExpertFk(state, taxonName);
693
//
694
//				// Determine speciesExpertFk
695
//				Integer speciesExpertFk = makeSpeciesExpertFk(state, taxonName);
696

    
697
				doCount(count++, modCount, pluralString);
698
				Integer typeNameFk = getTypeNameFk(taxonName, state);
699
				kingdomFk = findKingdomIdFromTreeIndex(taxon, state);
700
				 //       PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
701

    
702
				//TODO why are expertFks needed? (Andreas M.)
703
//				if (expertFk != null || speciesExpertFk != null) {
704
					invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, nomenclaturalCode, state.getDbId(taxon),
705
							typeNameFk, kingdomFk, state);
706
//				}
707

    
708
					taxon = null;
709
					taxonName = null;
710
			}
711

    
712
			// Commit transaction
713
			commitTransaction(txStatus);
714
			logger.debug("Committed transaction.");
715
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
716
			pastCount = count;
717

    
718
			// Start transaction
719
			txStatus = startTransaction(true);
720
			logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
721
		}
722
		if (list == null) {
723
			logger.info("No " + pluralString + " left to fetch.");
724
		}
725

    
726
		list = null;
727

    
728
		// Commit transaction
729
		commitTransaction(txStatus);
730

    
731
		logger.debug("Committed transaction.");
732
		logger.debug("Try to take snapshot at the end of phase 3 of taxonExport, number of partitions: " + partitionCount);
733
		//ProfilerController.memorySnapshot();
734
		return success;
735
	}
736

    
737
	/**
738
     * @param taxon
739
     * @return
740
     */
741
    private Integer findKingdomIdFromTreeIndex(TaxonBase<?> taxon,PesiExportState state) {
742
        if (taxon instanceof Taxon){
743
            Set<TaxonNode> nodes = ((Taxon)taxon).getTaxonNodes();
744
            if (!nodes.isEmpty()){
745
                String treeIndex = nodes.iterator().next().treeIndex();
746

    
747
                Pattern pattern = Pattern.compile("#t[0-9]+#[0-9]+#[0-9]+#");
748
                Matcher matcher = pattern.matcher(treeIndex);
749
                Integer kingdomID = null;
750
                if(matcher.find()) {
751
                    String treeIndexKingdom = matcher.group(0);
752
                    kingdomID = state.getTreeIndexKingdomMap().get(treeIndexKingdom);
753
                }
754
                return kingdomID;
755
            } else {
756
                logger.debug("The taxon has no nodes: " + taxon.getTitleCache() + " the kingdom is taken from the nomenclatural code: " + PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
757
                return PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
758
        }} else{
759
           return PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
760
        }
761
    }
762

    
763
    //	"PHASE 5: Creating Inferred Synonyms..."
764
	private boolean doPhase05(PesiExportState state, PesiExportMapping mapping, PesiExportMapping synRelMapping) throws SQLException {
765
		int count;
766
		int pastCount;
767
		boolean success = true;
768
		// Get the limit for objects to save within a single transaction.
769
		if (! state.getConfig().isDoInferredSynonyms()){
770
			logger.info ("Ignore PHASE 5: Creating Inferred Synonyms...");
771
			return success;
772
		}
773

    
774
		int limit = state.getConfig().getLimitSave();
775
		// Create inferred synonyms for accepted taxa
776
		logger.info("PHASE 4: Creating Inferred Synonyms...");
777

    
778
		// Determine the count of elements in datawarehouse database table Taxon
779
		currentTaxonId = determineTaxonCount(state);
780
		currentTaxonId++;
781

    
782
		count = 0;
783
		pastCount = 0;
784
		int pageSize = limit/10;
785
		int pageNumber = 1;
786
		String inferredSynonymPluralString = "Inferred Synonyms";
787

    
788
		// Start transaction
789
		TransactionStatus txStatus = startTransaction(true);
790
		logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
791
		List<TaxonBase> taxonList = null;
792

    
793

    
794

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

    
798
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
799
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
800
					synRelMapping, taxonList));
801

    
802
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
803
			// Commit transaction
804
			commitTransaction(txStatus);
805
			logger.debug("Committed transaction.");
806
			logger.info("Exported " + (taxonList.size()) + " " + inferredSynonymPluralString + ". Total: " + count);
807
			//pastCount = count;
808

    
809
			// Save Rank Data and KingdomFk for inferred synonyms
810
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
811
				invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomenclaturalCode, taxonFk, kingdomFk, state);
812
			}
813

    
814
			// Start transaction
815
			txStatus = startTransaction(true);
816
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
817

    
818
			// Increment pageNumber
819
			pageNumber++;
820
		}
821
		taxonList = null;
822
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", Rank.SUBSPECIES(), pageSize, pageNumber)).size() > 0) {
823
			HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
824

    
825
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
826
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
827
					synRelMapping, taxonList));
828
			;
829
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
830
			// Commit transaction
831
			commitTransaction(txStatus);
832
			logger.debug("Committed transaction.");
833
			logger.info("Exported " + taxonList.size()+ " " + inferredSynonymPluralString + ". Total: " + count);
834
			//pastCount = count;
835

    
836
			// Save Rank Data and KingdomFk for inferred synonyms
837
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
838
				invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomenclaturalCode, taxonFk, kingdomFk, state);
839
			}
840

    
841
			// Start transaction
842
			txStatus = startTransaction(true);
843
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
844

    
845
			// Increment pageNumber
846
			pageNumber++;
847
			inferredSynonymsDataToBeSaved = null;
848
		}
849
		if (taxonList.size() == 0) {
850
			logger.info("No " + parentPluralString + " left to fetch.");
851
		}
852

    
853
		taxonList = null;
854
//		logger.warn("Taking snapshot at the end of phase 4 of taxonExport");
855
//		ProfilerController.memorySnapshot();
856

    
857
		// Commit transaction
858
		commitTransaction(txStatus);
859
		System.gc();
860
		logger.debug("Taking snapshot at the end of phase 4 after gc() of taxonExport");
861
		//ProfilerController.memorySnapshot();
862
		logger.debug("Committed transaction.");
863
		return success;
864
	}
865

    
866
	/**
867
	 * @param state
868
	 * @param mapping
869
	 * @param synRelMapping
870
	 * @param currentTaxonId
871
	 * @param taxonList
872
	 * @param inferredSynonymsDataToBeSaved
873
	 * @return
874
	 */
875
	private HashMap<Integer, TaxonNameBase<?, ?>> createInferredSynonymsForTaxonList(PesiExportState state,
876
			PesiExportMapping mapping, PesiExportMapping synRelMapping,	 List<TaxonBase> taxonList) {
877

    
878
		Taxon acceptedTaxon;
879
		Classification classification = null;
880
		List<Synonym> inferredSynonyms = null;
881
		boolean localSuccess = true;
882

    
883
		HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
884

    
885
		for (TaxonBase<?> taxonBase : taxonList) {
886

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

    
891
				if (taxonName.isInstanceOf(ZoologicalName.class)) {
892
					nomenclaturalCode  = taxonName.getNomenclaturalCode();
893
					kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
894

    
895
					Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
896
					TaxonNode singleNode = null;
897

    
898
					if (taxonNodes.size() > 0) {
899
						// Determine the classification of the current TaxonNode
900

    
901
						singleNode = taxonNodes.iterator().next();
902
						if (singleNode != null) {
903
							classification = singleNode.getClassification();
904
						} else {
905
							logger.error("A TaxonNode belonging to this accepted Taxon is NULL: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() +")");
906
						}
907
					} else {
908
						// Classification could not be determined directly from this TaxonNode
909
						// The stored classification from another TaxonNode is used. It's a simple, but not a failsafe fallback solution.
910
						if (taxonNodes.size() == 0) {
911
							//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");
912

    
913
						}
914
					}
915

    
916
					if (classification != null) {
917
						try{
918
							TaxonNameBase name = acceptedTaxon.getName();
919
							//if (name.isSpecies() || name.isInfraSpecific()){
920
								inferredSynonyms  = getTaxonService().createAllInferredSynonyms(acceptedTaxon, classification, true);
921
							//}
922
//								inferredSynonyms = getTaxonService().createInferredSynonyms(classification, acceptedTaxon, SynonymRelationshipType.INFERRED_GENUS_OF());
923
							if (inferredSynonyms != null) {
924
								for (Synonym synonym : inferredSynonyms) {
925
//									TaxonNameBase<?,?> synonymName = synonym.getName();
926
									MarkerType markerType =getUuidMarkerType(PesiTransformer.uuidMarkerGuidIsMissing, state);
927
									synonym.addMarker(Marker.NewInstance(markerType, true));
928
									// Both Synonym and its TaxonName have no valid Id yet
929
									synonym.setId(currentTaxonId++);
930

    
931

    
932
									localSuccess &= mapping.invoke(synonym);
933
									//get SynonymRelationship and export
934
									if (synonym.getSynonymRelations().isEmpty() ){
935
										SynonymRelationship synRel;
936
										IdentifiableSource source = synonym.getSources().iterator().next();
937
										if (source.getIdNamespace().contains("Potential combination")){
938
											synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
939
											logger.error(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to potential combination");
940
										} else if (source.getIdNamespace().contains("Inferred Genus")){
941
											synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.INFERRED_GENUS_OF());
942
											logger.error(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to inferred genus");
943
										} else if (source.getIdNamespace().contains("Inferred Epithet")){
944
											synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.INFERRED_EPITHET_OF());
945
											logger.error(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to inferred epithet");
946
										} else{
947
											synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.INFERRED_SYNONYM_OF());
948
											logger.error(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to inferred synonym");
949
										}
950

    
951
										localSuccess &= synRelMapping.invoke(synRel);
952
										if (!localSuccess) {
953
											logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
954
										}
955
										synRel = null;
956
									} else {
957
										for (SynonymRelationship synRel: synonym.getSynonymRelations()){
958
											localSuccess &= synRelMapping.invoke(synRel);
959
											if (!localSuccess) {
960
												logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
961
											} else {
962
												logger.info("Synonym relationship successfully exported: " + synonym.getTitleCache() + "  " +acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
963
											}
964
											synRel = null;
965
										}
966
									}
967

    
968
									inferredSynonymsDataToBeSaved.put(synonym.getId(), synonym.getName());
969
								}
970
							}
971
						}catch(Exception e){
972
							logger.error(e.getMessage());
973
							e.printStackTrace();
974
						}
975
					} else {
976
						logger.error("Classification is NULL. Inferred Synonyms could not be created for this Taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() + ")");
977
					}
978
				} else {
979
//							logger.error("TaxonName is not a ZoologicalName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
980
				}
981
			} else {
982
				logger.error("This TaxonBase is not a Taxon even though it should be: " + taxonBase.getUuid() + " (" + taxonBase.getTitleCache() + ")");
983
			}
984
		}
985
		taxonList = null;
986
		return inferredSynonymsDataToBeSaved;
987
	}
988

    
989

    
990
	/**
991
	 * Handles names that do not appear in taxa
992
	 * @param state
993
	 * @param mapping
994
	 * @return
995
	 */
996
	private boolean doNames(PesiExportState state, PesiExportMapping additionalSourceMapping)  throws SQLException {
997

    
998
		boolean success = true;
999
		if (! state.getConfig().isDoPureNames()){
1000
			logger.info ("Ignore PHASE 1b: PureNames");
1001
			return success;
1002
		}
1003

    
1004
		try {
1005
			PesiExportMapping mapping = getPureNameMapping(state);
1006
			mapping.initialize(state);
1007
			int count = 0;
1008
			int pastCount = 0;
1009
			List<NonViralName<?>> list;
1010
			success = true;
1011
			// Get the limit for objects to save within a single transaction.
1012
			int limit = state.getConfig().getLimitSave();
1013

    
1014

    
1015
			logger.info("PHASE 1b: Export Pure Names ...");
1016
			// Start transaction
1017
			TransactionStatus txStatus = startTransaction(true);
1018
			logger.info("Started new transaction for Pure Names. Fetching some " + pluralString + " (max: " + limit + ") ...");
1019

    
1020
			int partitionCount = 0;
1021
			while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null   ) {
1022

    
1023
				logger.info("Fetched " + list.size() + " names without taxa. Exporting...");
1024
				for (TaxonNameBase<?,?> taxonName : list) {
1025
					doCount(count++, modCount, pluralString);
1026
					success &= mapping.invoke(taxonName);
1027
					//additional source
1028
					if (taxonName.getNomenclaturalReference() != null || StringUtils.isNotBlank(taxonName.getNomenclaturalMicroReference() )){
1029
						additionalSourceMapping.invoke(taxonName);
1030
					}
1031
				}
1032

    
1033
				// Commit transaction
1034
				commitTransaction(txStatus);
1035
				logger.debug("Committed transaction.");
1036
				logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
1037
				pastCount = count;
1038

    
1039
				// Start transaction
1040
				txStatus = startTransaction(true);
1041
				logger.info("Started new transaction for PureNames. Fetching some " + pluralString + " (max: " + limit + ") ...");
1042
			}
1043
			if (list == null) {
1044
				logger.info("No " + pluralString + " left to fetch.");
1045
			}
1046
			list = null;
1047
			// Commit transaction
1048
			commitTransaction(txStatus);
1049
			logger.debug("Committed transaction.");
1050
		} catch (Exception e) {
1051
			logger.error("Error occurred in pure name export");
1052
			e.printStackTrace();
1053
			success = false;
1054
		}
1055
		return success;
1056
	}
1057

    
1058
	/**
1059
	 * Determines the current number of entries in the DataWarehouse database table <code>Taxon</code>.
1060
	 * @param state The {@link PesiExportState PesiExportState}.
1061
	 * @return The count.
1062
	 */
1063
	private Integer determineTaxonCount(PesiExportState state) {
1064
		Integer result = null;
1065
		PesiExportConfigurator pesiConfig = state.getConfig();
1066

    
1067
		String sql;
1068
		Source destination =  pesiConfig.getDestination();
1069
		sql = "SELECT max(taxonId) FROM Taxon";
1070
		destination.setQuery(sql);
1071
		ResultSet resultSet = destination.getResultSet();
1072
		try {
1073
			resultSet.next();
1074
			result = resultSet.getInt(1);
1075
		} catch (SQLException e) {
1076
			logger.error("TaxonCount could not be determined: " + e.getMessage());
1077
			e.printStackTrace();
1078
		}
1079
		resultSet = null;
1080
		return result;
1081
	}
1082

    
1083
	/**
1084
	 * Checks whether a parent at specific level has a specific Rank.
1085
	 * @param taxonName A {@link TaxonNameBase TaxonName}.
1086
	 * @param level The ancestor level.
1087
	 * @param ancestorRank The ancestor rank.
1088
	 * @return Whether a parent at a specific level has a specific Rank.
1089
	 */
1090
	private boolean validateAncestorOfSpecificRank(TaxonBase<?> taxonBase, int level, Rank ancestorRank) {
1091
		boolean result = false;
1092
		TaxonNode parentNode = null;
1093
		if (taxonBase.isInstanceOf(Taxon.class)){
1094
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1095
			// Get ancestor Taxon via TaxonNode
1096
			Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();
1097
			if (taxonNodes.size() == 1) {
1098
				TaxonNode taxonNode = taxonNodes.iterator().next();
1099
				if (taxonNode != null) {
1100
					for (int i = 0; i < level; i++) {
1101
						if (taxonNode != null) {
1102
							taxonNode  = taxonNode.getParent();
1103
						}
1104
					}
1105
					parentNode = taxonNode;
1106
				}
1107
			} else if (taxonNodes.size() > 1) {
1108
				logger.error("This taxon has " + taxonNodes.size() + " taxonNodes: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1109
			}
1110
		}
1111
		//compare
1112
		if (parentNode != null) {
1113
			TaxonNode node = CdmBase.deproxy(parentNode, TaxonNode.class);
1114
			Taxon parentTaxon = node.getTaxon();
1115
			if (parentTaxon != null) {
1116
				TaxonNameBase<?,?> parentTaxonName = parentTaxon.getName();
1117
				if (parentTaxonName != null && parentTaxonName.getRank().equals(ancestorRank)) {
1118
					result = true;
1119
				}
1120
			} else if (parentNode.treeIndex().matches("#t\\d+#\\d+#")) {
1121
				//do nothing (is root node)
1122
			} else {
1123
				logger.error("This TaxonNode has no Taxon: " + node.getUuid());
1124
			}
1125
		}
1126
		return result;
1127
	}
1128

    
1129
	/**
1130
	 * Returns the AnnotationType for a given UUID.
1131
	 * @param uuid The Annotation UUID.
1132
	 * @param label The Annotation label.
1133
	 * @param text The Annotation text.
1134
	 * @param labelAbbrev The Annotation label abbreviation.
1135
	 * @return The AnnotationType.
1136
	 */
1137
	protected AnnotationType getAnnotationType(UUID uuid, String label, String text, String labelAbbrev){
1138
		AnnotationType annotationType = (AnnotationType)getTermService().find(uuid);
1139
		if (annotationType == null) {
1140
			annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
1141
			annotationType.setUuid(uuid);
1142
//			annotationType.setVocabulary(AnnotationType.EDITORIAL().getVocabulary());
1143
			getTermService().save(annotationType);
1144
		}
1145
		return annotationType;
1146
	}
1147

    
1148
	/**
1149
	 * Traverses the classification recursively and stores determined values for every Taxon.
1150
	 * @param childNode The {@link TaxonNode TaxonNode} to process.
1151
	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1152
	 * @param treeIndex The TreeIndex at the current level.
1153
	 * @param fetchLevel Rank to stop fetching at.
1154
	 * @param state The {@link PesiExportState PesiExportState}.
1155
	 */
1156
	private void traverseTree(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, Rank fetchLevel, PesiExportState state) {
1157
		// Traverse all branches from this childNode until specified fetchLevel is reached.
1158
		StringBuffer localTreeIndex = new StringBuffer(treeIndex);
1159
		Taxon childTaxon = childNode.getTaxon();
1160
		if (childTaxon != null) {
1161
			if (isPesiTaxon(childTaxon)){
1162
				Integer taxonId = state.getDbId(childTaxon);
1163
				TaxonNameBase<?,?> childName = childTaxon.getName();
1164
				if (taxonId != null) {
1165
					Rank childRank = childName.getRank();
1166
					if (childRank != null) {
1167
						if (! childRank.equals(fetchLevel)) {
1168

    
1169
							localTreeIndex.append(taxonId + "#");
1170

    
1171
							saveData(childNode, parentNode, localTreeIndex, state, taxonId);
1172

    
1173
							// Store treeIndex as annotation for further use
1174
							Annotation annotation = Annotation.NewInstance(localTreeIndex.toString(), getTreeIndexAnnotationType(), Language.DEFAULT());
1175
							childNode.addAnnotation(annotation);
1176

    
1177
							for (TaxonNode newNode : childNode.getChildNodes()) {
1178
								if (newNode.getTaxon() != null && isPesiTaxon(newNode.getTaxon())){
1179
									traverseTree(newNode, childNode, localTreeIndex, fetchLevel, state);
1180
								}
1181
							}
1182

    
1183
						} else {
1184
	//						logger.debug("Target Rank " + fetchLevel.getLabel() + " reached");
1185
							return;
1186
						}
1187
					} else {
1188
						logger.error("Rank is NULL. FetchLevel can not be checked: " + childName.getUuid() + " (" + childName.getTitleCache() + ")");
1189
					}
1190
				} else {
1191
					logger.error("Taxon can not be found in state: " + childTaxon.getUuid() + " (" + childTaxon.getTitleCache() + ")");
1192
				}
1193
			}else{
1194
				if (logger.isDebugEnabled()){
1195
					logger.debug("Taxon is not a PESI taxon: " + childTaxon.getUuid());
1196
				}
1197
			}
1198

    
1199
		} else {
1200
			logger.error("Taxon is NULL for TaxonNode: " + childNode.getUuid());
1201
		}
1202
	}
1203

    
1204
	/**
1205
	 * Stores values in database for every recursive round.
1206
	 * @param childNode The {@link TaxonNode TaxonNode} to process.
1207
	 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1208
	 * @param treeIndex The TreeIndex at the current level.
1209
	 * @param state The {@link PesiExportState PesiExportState}.
1210
	 * @param currentTaxonFk The TaxonFk to store the values for.
1211
	 */
1212
	private void saveData(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, PesiExportState state, Integer currentTaxonFk) {
1213
		// We are differentiating kingdoms by the nomenclatural code for now.
1214
		// This needs to be handled in a better way as soon as we know how to differentiate between more kingdoms.
1215
		Taxon childTaxon = childNode.getTaxon();
1216
		if (isPesiTaxon(childTaxon)) {
1217
			TaxonBase<?> parentTaxon = null;
1218
			if (parentNode != null) {
1219
				parentTaxon = parentNode.getTaxon();
1220

    
1221
			}
1222

    
1223
			invokeParentTaxonFkAndTreeIndex(state.getDbId(parentTaxon), currentTaxonFk,	treeIndex);
1224
		}
1225

    
1226
	}
1227

    
1228
	/**
1229
	 * Inserts values into the Taxon database table.
1230
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1231
	 * @param state The {@link PesiExportState PesiExportState}.
1232
	 * @param stmt The prepared statement.
1233
	 * @return Whether save was successful or not.
1234
	 */
1235
	protected boolean invokeParentTaxonFkAndTreeIndex(Integer parentTaxonFk, Integer currentTaxonFk, StringBuffer treeIndex) {
1236
		try {
1237
			if (parentTaxonFk != null) {
1238
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(1, parentTaxonFk);
1239
			} else {
1240
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(1, null);
1241
			}
1242

    
1243
			if (treeIndex != null) {
1244
				parentTaxonFk_TreeIndex_KingdomFkStmt.setString(2, treeIndex.toString());
1245
			} else {
1246
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(2, null);
1247
			}
1248

    
1249
			if (currentTaxonFk != null) {
1250
				parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(3, currentTaxonFk);
1251
			} else {
1252
				parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(3, null);
1253
			}
1254

    
1255
			parentTaxonFk_TreeIndex_KingdomFkStmt.executeUpdate();
1256
			return true;
1257
		} catch (SQLException e) {
1258
			logger.error("ParentTaxonFk (" + parentTaxonFk ==null? "-":parentTaxonFk + ") and TreeIndex could not be inserted into database for taxon "+ (currentTaxonFk == null? "-" :currentTaxonFk) + ": " + e.getMessage());
1259
			e.printStackTrace();
1260
			return false;
1261
		}
1262
	}
1263

    
1264
	protected boolean invokeParentTaxonFk(Integer parentId, Integer childId) {
1265
		try {
1266
			parentTaxonFkStmt.setInt(1, parentId);
1267
			parentTaxonFkStmt.setInt(2, childId);
1268
			parentTaxonFkStmt.executeUpdate();
1269
			return true;
1270
		} catch (SQLException e) {
1271
			logger.warn("ParentTaxonFk (" + parentId ==null? "-":parentId + ") could not be inserted into database for taxon "+ (childId == null? "-" :childId) + ": " + e.getMessage());
1272
			e.printStackTrace();
1273
			return false;
1274
		}
1275
	}
1276

    
1277

    
1278
	/**
1279
	 * Inserts Rank data and KingdomFk into the Taxon database table.
1280
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1281
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1282
	 * @param taxonFk The TaxonFk to store the values for.
1283
	 * @param state
1284
	 * @param kindomFk The KingdomFk.
1285
	 * @return Whether save was successful or not.
1286
	 */
1287
	private boolean invokeRankDataAndKingdomFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, Integer taxonFk, Integer kingdomFk, PesiExportState state) {
1288
		try {
1289
			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1290
			if (rankFk != null) {
1291
				rankUpdateStmt.setInt(1, rankFk);
1292
			} else {
1293
				rankUpdateStmt.setObject(1, null);
1294
			}
1295

    
1296
			String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
1297
			if (rankCache != null) {
1298
				rankUpdateStmt.setString(2, rankCache);
1299
			} else {
1300
				rankUpdateStmt.setObject(2, null);
1301
			}
1302

    
1303
			if (kingdomFk != null) {
1304

    
1305
				rankUpdateStmt.setInt(3, kingdomFk);
1306
			} else {
1307
				rankUpdateStmt.setObject(3, null);
1308
			}
1309

    
1310
			if (taxonFk != null) {
1311
				rankUpdateStmt.setInt(4, taxonFk);
1312
			} else {
1313
				rankUpdateStmt.setObject(4, null);
1314
			}
1315

    
1316
			rankUpdateStmt.executeUpdate();
1317
			return true;
1318
		} catch (SQLException e) {
1319
			logger.error("Data (RankFk, RankCache, KingdomFk) could not be inserted into database: " + e.getMessage());
1320
			e.printStackTrace();
1321
			return false;
1322
		}
1323
	}
1324

    
1325
	/**
1326
	 * Inserts Rank data, TypeNameFk, KingdomFk, expertFk and speciesExpertFk into the Taxon database table.
1327
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1328
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1329
	 * @param taxonFk The TaxonFk to store the values for.
1330
	 * @param typeNameFk The TypeNameFk.
1331
	 * @param state
1332
	 * @param kindomFk The KingdomFk.
1333
	 * @param expertFk The ExpertFk.
1334
	 * @param speciesExpertFk The SpeciesExpertFk.
1335
	 * @return Whether save was successful or not.
1336
	 */
1337
	private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode,
1338
			Integer taxonFk, Integer typeNameFk, Integer kingdomFkk, PesiExportState state) {
1339
		try {
1340
			int index = 1;
1341
			Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1342
			if (rankFk != null) {
1343
				rankTypeExpertsUpdateStmt.setInt(index++, rankFk);
1344
			} else {
1345
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1346
			}
1347

    
1348
			String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
1349
			if (rankCache != null) {
1350
				rankTypeExpertsUpdateStmt.setString(index++, rankCache);
1351
			} else {
1352
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1353
			}
1354

    
1355
			if (typeNameFk != null) {
1356
				rankTypeExpertsUpdateStmt.setInt(index++, typeNameFk);
1357
			} else {
1358
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1359
			}
1360

    
1361
			if (kingdomFkk != null) {
1362
				rankTypeExpertsUpdateStmt.setInt(index++, kingdomFkk);
1363
			} else {
1364
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1365
			}
1366

    
1367
//			if (expertFk != null) {
1368
//				rankTypeExpertsUpdateStmt.setInt(5, expertFk);
1369
//			} else {
1370
//				rankTypeExpertsUpdateStmt.setObject(5, null);
1371
//			}
1372
//
1373
//			//TODO handle experts GUIDS
1374
//			if (speciesExpertFk != null) {
1375
//				rankTypeExpertsUpdateStmt.setInt(6, speciesExpertFk);
1376
//			} else {
1377
//				rankTypeExpertsUpdateStmt.setObject(6, null);
1378
//			}
1379
//
1380
			if (taxonFk != null) {
1381
				rankTypeExpertsUpdateStmt.setInt(index++, taxonFk);
1382
			} else {
1383
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1384
			}
1385

    
1386
			rankTypeExpertsUpdateStmt.executeUpdate();
1387
			return true;
1388
		} catch (SQLException e) {
1389
			logger.error("Data could not be inserted into database: " + e.getMessage());
1390
			e.printStackTrace();
1391
			return false;
1392
		} catch (Exception e) {
1393
			logger.error("Some exception occurred: " + e.getMessage());
1394
			e.printStackTrace();
1395
			return false;
1396
		}
1397
	}
1398

    
1399
	/**
1400
	 * Deletes all entries of database tables related to <code>Taxon</code>.
1401
	 * @param state The {@link PesiExportState PesiExportState}.
1402
	 * @return Whether the delete operation was successful or not.
1403
	 */
1404
	protected boolean doDelete(PesiExportState state) {
1405
		PesiExportConfigurator pesiConfig = state.getConfig();
1406

    
1407
		String sql;
1408
		Source destination =  pesiConfig.getDestination();
1409

    
1410
		// Clear Taxon
1411
		sql = "DELETE FROM " + dbTableName;
1412
		destination.setQuery(sql);
1413
		destination.update(sql);
1414
		return true;
1415
	}
1416

    
1417
	/* (non-Javadoc)
1418
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
1419
	 */
1420
	@Override
1421
	protected boolean isIgnore(PesiExportState state) {
1422
		return ! state.getConfig().isDoTaxa();
1423
	}
1424

    
1425

    
1426
	/**
1427
	 * Creates the kingdom fk.
1428
	 * @param taxonName
1429
	 * @return
1430
	 */
1431
	@SuppressWarnings("unused")  //used by mapper
1432
	private static Integer getKingdomFk(TaxonNameBase taxonName){
1433
		return PesiTransformer.nomenClaturalCode2Kingdom(taxonName.getNomenclaturalCode());
1434
	}
1435

    
1436
	/**
1437
	 * Creates the parent fk.
1438
	 * @param taxonName
1439
	 * @return
1440
	 */
1441
	@SuppressWarnings("unused")  //used by mapper
1442
	private static Integer getParentTaxonFk(TaxonBase<?> taxonBase, PesiExportState state){
1443
		if (taxonBase.isInstanceOf(Taxon.class)){
1444
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1445
			if (! isMisappliedName(taxon)){
1446
				Set<TaxonNode> nodes = taxon.getTaxonNodes();
1447
				if (nodes.size() == 0){
1448
					if (taxon.getName().getRank().isLower(Rank.KINGDOM())){
1449
						logger.warn("Accepted taxon has no parent. " + taxon.getTitleCache() + ", " +  taxon.getUuid());
1450
					}
1451
				}else if (nodes.size() > 1){
1452
					logger.warn("Taxon has more than 1 node attached. This is not supported by PESI export." +  taxon.getTitleCache() + ", " +  taxon.getUuid());
1453
				}else{
1454
					Taxon parent =nodes.iterator().next().getParent().getTaxon();
1455
					return state.getDbId(parent);
1456
				}
1457
			}
1458
		}
1459
		return null;
1460
	}
1461

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

    
1473

    
1474
	/**
1475
	 * Returns the <code>RankFk</code> attribute.
1476
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1477
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1478
	 * @return The <code>RankFk</code> attribute.
1479
	 * @see MethodMapper
1480
	 */
1481
	private static Integer getRankFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode) {
1482
		Integer result = null;
1483
		try {
1484
			if (nomenclaturalCode != null) {
1485
				if (taxonName != null) {
1486
					if (taxonName.getRank() == null) {
1487
						logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1488
					} else {
1489
						result = PesiTransformer.rank2RankId(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1490
					}
1491
					if (result == null) {
1492
						logger.warn("Rank could not be determined for PESI-Kingdom-Id " + PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode) + " and TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1493
					}
1494
				}
1495
			}
1496
		} catch (Exception e) {
1497
			e.printStackTrace();
1498
		}
1499
		return result;
1500
	}
1501

    
1502
	/**
1503
	 * Returns the rank cache for the taxon name based on the names nomenclatural code.
1504
	 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1505
	 * @param taxonName
1506
	 * @return
1507
	 */
1508
	@SuppressWarnings("unused")  //used by mapper
1509
	private static String getRankCache(TaxonNameBase<?,?> taxonName, PesiExportState state) {
1510
		return getRankCache(taxonName, taxonName.getNomenclaturalCode(), state);
1511
	}
1512

    
1513

    
1514
	/**
1515
	 * Returns the <code>RankCache</code> attribute.
1516
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1517
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1518
	 * @param state
1519
	 * @return The <code>RankCache</code> attribute.
1520
	 * @see MethodMapper
1521
	 */
1522
	private static String getRankCache(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, PesiExportState state) {
1523
		if (nomenclaturalCode != null) {
1524
			return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1525
		}else{
1526
			logger.warn("No nomenclatural code defined for name " + taxonName.getUuid());
1527
			return null;
1528
		}
1529

    
1530
	}
1531

    
1532

    
1533
	/**
1534
	 * Returns the <code>DisplayName</code> attribute.
1535
	 * @param taxon The {@link TaxonBase Taxon}.
1536
	 * @return The <code>DisplayName</code> attribute.
1537
	 * @see MethodMapper
1538
	 */
1539
	@SuppressWarnings("unused")  //used by Mapper
1540
	private static String getDisplayName(TaxonBase<?> taxon) {
1541
		TaxonNameBase<?,?> taxonName = taxon.getName();
1542
		String result = getDisplayName(taxonName);
1543
		if (isMisappliedName(taxon)){
1544
			result = result + " " + getAuthorString(taxon);
1545
		}
1546
		return result;
1547
	}
1548

    
1549
	/**
1550
	 * Returns the <code>AuthorString</code> attribute.
1551
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1552
	 * @return The <code>AuthorString</code> attribute.
1553
	 * @see MethodMapper
1554
	 */
1555
	@SuppressWarnings("unused") //used by mapper
1556
	protected static String getAuthorString(TaxonBase<?> taxon) {
1557
		try {
1558
			String result = null;
1559
			boolean isNonViralName = false;
1560
			String authorshipCache = null;
1561
			TaxonNameBase<?,?> taxonName = taxon.getName();
1562
			if (taxonName != null && taxonName.isInstanceOf(NonViralName.class)){
1563
				authorshipCache = CdmBase.deproxy(taxonName, NonViralName.class).getAuthorshipCache();
1564
				isNonViralName = true;
1565
			}
1566
			result = authorshipCache;
1567

    
1568
			// For a misapplied names there are special rules
1569
			if (isMisappliedName(taxon)){
1570
				if (taxon.getSec() != null){
1571
					String secTitle = taxon.getSec().getTitleCache();
1572
					if (! secTitle.startsWith("auct")){
1573
						secTitle = "sensu " + secTitle;
1574
					}else if (secTitle.equals("auct")){  //may be removed once the title cache is generated correctly for references with title auct. #
1575
						secTitle = "auct.";
1576
					}
1577
					return secTitle;
1578
				}else if (StringUtils.isBlank(authorshipCache)) {
1579
					// Set authorshipCache to "auct."
1580
					result = PesiTransformer.AUCT_STRING;
1581
				}else{
1582
					result = PesiTransformer.AUCT_STRING;
1583
//					result = authorshipCache;
1584
				}
1585
			}
1586

    
1587
			if (taxonName == null){
1588
				logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1589
			}else if (! isNonViralName){
1590
				logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1591
			}
1592

    
1593
			if (StringUtils.isBlank(result)) {
1594
				return null;
1595
			} else {
1596
				return result;
1597
			}
1598
		} catch (Exception e) {
1599
			e.printStackTrace();
1600
			return null;
1601
		}
1602

    
1603
	}
1604

    
1605

    
1606
	/**
1607
	 * Returns the <code>DisplayName</code> attribute.
1608
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1609
	 * @return The <code>DisplayName</code> attribute.
1610
	 * @see MethodMapper
1611
	 */
1612
	@SuppressWarnings("unused")  //used by Mapper
1613
	private static String getDisplayName(TaxonNameBase<?,?> taxonName) {
1614
		// TODO: extension?
1615
		if (taxonName == null) {
1616
			return null;
1617
		}else{
1618
			INonViralNameCacheStrategy<NonViralName<?>> cacheStrategy = getCacheStrategy(taxonName);
1619
			HTMLTagRules tagRules = new HTMLTagRules().
1620
					addRule(TagEnum.name, "i").
1621
					addRule(TagEnum.nomStatus, "@status@");
1622

    
1623
			NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1624
			String result = cacheStrategy.getFullTitleCache(nvn, tagRules);
1625
			cacheStrategy = null;
1626
			nvn = null;
1627
			return result.replaceAll(",?\\<@status@\\>.*\\</@status@\\>", "");
1628
		}
1629
	}
1630

    
1631
	@SuppressWarnings("unused")
1632
	private static String getGUID(TaxonNameBase<?,?> taxonName) {
1633
		UUID uuid = taxonName.getUuid();
1634
		String result = "NameUUID:" + uuid.toString();
1635
		return result;
1636
	}
1637

    
1638

    
1639
	/**
1640
	 * Returns the <code>WebShowName</code> attribute for a taxon.
1641
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1642
	 * @return The <code>WebShowName</code> attribute.
1643
	 * @see MethodMapper
1644
	*/
1645
	@SuppressWarnings("unused")
1646
	private static String getWebShowName(TaxonBase<?> taxon) {
1647
		TaxonNameBase<?,?> taxonName = taxon.getName();
1648
		String result = getWebShowName(taxonName);
1649
		if (isMisappliedName(taxon)){
1650
			result = result + " " + getAuthorString(taxon);
1651
		}
1652
		return result;
1653
	}
1654

    
1655
	/**
1656
	 * Returns the <code>WebShowName</code> attribute.
1657
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1658
	 * @return The <code>WebShowName</code> attribute.
1659
	 * @see MethodMapper
1660
	 */
1661
	private static String getWebShowName(TaxonNameBase<?,?> taxonName) {
1662
		//TODO extensions?
1663
		if (taxonName == null) {
1664
			return null;
1665
		}else{
1666
			INonViralNameCacheStrategy<NonViralName<?>> cacheStrategy = getCacheStrategy(taxonName);
1667

    
1668
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1669
			NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1670
			String result = cacheStrategy.getTitleCache(nvn, tagRules);
1671
			cacheStrategy = null;
1672
			nvn = null;
1673
			return result;
1674
		}
1675
	}
1676

    
1677

    
1678
	/**
1679
	 * Returns the <code>WebSearchName</code> attribute.
1680
	 * @param taxonName The {@link NonViralName NonViralName}.
1681
	 * @return The <code>WebSearchName</code> attribute.
1682
	 * @see MethodMapper
1683
	 */
1684
	@SuppressWarnings("unused")
1685
	private static String getWebSearchName(TaxonNameBase<?,?> taxonName) {
1686
		//TODO extensions?
1687
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1688
		NonViralNameDefaultCacheStrategy<NonViralName<?>> strategy = getCacheStrategy(nvn);
1689
		String result = strategy.getNameCache(nvn);
1690
		strategy = null;
1691
		nvn = null;
1692
		return result;
1693
	}
1694

    
1695

    
1696
	/**
1697
	 * Returns the <code>FullName</code> attribute.
1698
	 * @param taxonName The {@link NonViralName NonViralName}.
1699
	 * @return The <code>FullName</code> attribute.
1700
	 * @see MethodMapper
1701
	 */
1702
	@SuppressWarnings("unused")
1703
	private static String getFullName(TaxonNameBase taxonName) {
1704
		//TODO extensions?
1705
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1706
		String result = getCacheStrategy(nvn).getTitleCache(nvn);
1707
		Iterator<TaxonBase> taxa = taxonName.getTaxa().iterator();
1708
		if (taxonName.getTaxa().size() >0){
1709
			if (taxonName.getTaxa().size() == 1){
1710
				TaxonBase taxon = taxa.next();
1711
				if (isMisappliedName(taxon)){
1712
					result = result + " " + getAuthorString(taxon);
1713
				}
1714
				taxon = null;
1715
			}
1716
		}
1717
		taxa = null;
1718
		nvn = null;
1719
		return result;
1720
	}
1721

    
1722
	/**
1723
	 * Returns the SourceNameCache for the AdditionalSource table
1724
	 * @param taxonName
1725
	 * @return
1726
	 */
1727
	@SuppressWarnings("unused")
1728
	private static String getSourceNameCache(TaxonNameBase taxonName) {
1729
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1730
		if (nvn != null){
1731
			Reference<?> nomRef = (Reference)nvn.getNomenclaturalReference();
1732
			if (nomRef != null){
1733
				return nomRef.getAbbrevTitleCache();
1734
			}
1735

    
1736
		}
1737
		return null;
1738
	}
1739

    
1740

    
1741

    
1742
	/**
1743
	 * Returns the <code>FullName</code> attribute.
1744
	 * @param taxon The {@link TaxonBase taxon}.
1745
	 * @return The <code>FullName</code> attribute.
1746
	 * @see MethodMapper
1747
	 */
1748
	/*@SuppressWarnings("unused")
1749
	private static String getFullName(TaxonBase taxon) {
1750
		//TODO extensions?
1751
		TaxonNameBase name = taxon.getName();
1752
		String result = getFullName(name);
1753
		if (isMisappliedName(taxon)){
1754
			result = result + " " + getAuthorString(taxon);
1755
		}
1756

    
1757
		return result;
1758
	}
1759
*/
1760

    
1761
	/**
1762
	 * Returns the nomenclatural reference which is the reference
1763
	 * including the detail (microreference).
1764
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1765
	 * @return The <code>AuthorString</code> attribute.
1766
	 * @see MethodMapper
1767
	 */
1768
	@SuppressWarnings("unused")
1769
	private static String getNomRefString(TaxonNameBase<?,?> taxonName) {
1770
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1771
		if (ref == null){
1772
			return null;
1773
		}
1774
		if (! ref.isProtectedAbbrevTitleCache()){
1775
			ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1776
		}
1777
		return ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1778
	}
1779

    
1780

    
1781
	/**
1782
	 * Returns the <code>NameStatusFk</code> attribute.
1783
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1784
	 * @return The <code>NameStatusFk</code> attribute.
1785
	 * @see MethodMapper
1786
	 */
1787
	@SuppressWarnings("unused")
1788
	private static Integer getNameStatusFk(TaxonNameBase<?,?> taxonName) {
1789
		Integer result = null;
1790

    
1791
		NomenclaturalStatus state = getNameStatus(taxonName);
1792
		if (state != null) {
1793
			result = PesiTransformer.nomStatus2nomStatusFk(state.getType());
1794
		}
1795
		return result;
1796
	}
1797

    
1798
	/**
1799
	 * Returns the <code>NameStatusCache</code> attribute.
1800
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1801
	 * @return The <code>NameStatusCache</code> attribute.
1802
	 * @throws UndefinedTransformerMethodException
1803
	 * @see MethodMapper
1804
	 */
1805
	@SuppressWarnings("unused")
1806
	private static String getNameStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1807
		String result = null;
1808
		NomenclaturalStatus status = getNameStatus(taxonName);
1809
		if (status != null) {
1810
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1811
		}
1812
		return result;
1813
	}
1814

    
1815

    
1816
	private static NomenclaturalStatus getNameStatus(TaxonNameBase<?,?> taxonName) {
1817
		try {
1818
			if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1819
				NonViralName<?> nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1820
				Set<NomenclaturalStatus> states = nonViralName.getStatus();
1821
				if (states.size() == 1) {
1822
					NomenclaturalStatus status = states.iterator().next();
1823
					return status;
1824
				} else if (states.size() > 1) {
1825
					logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1826
				}
1827
			}
1828

    
1829
		} catch (Exception e) {
1830
			e.printStackTrace();
1831
		}
1832
		return null;
1833
	}
1834
	/**
1835
	 * Returns the <code>TaxonStatusFk</code> attribute.
1836
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1837
	 * @param state The {@link PesiExportState PesiExportState}.
1838
	 * @return The <code>TaxonStatusFk</code> attribute.
1839
	 * @see MethodMapper
1840
	 */
1841
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1842
		Integer result = null;
1843

    
1844
		try {
1845
			if (isMisappliedName(taxon)) {
1846
				Synonym synonym = Synonym.NewInstance(null, null);
1847

    
1848
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1849
				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1850
			} else {
1851
				result = PesiTransformer.taxonBase2statusFk(taxon);
1852
			}
1853

    
1854
		} catch (Exception e) {
1855
			e.printStackTrace();
1856
		}
1857
		return result;
1858
	}
1859

    
1860
	/**
1861
	 * Returns the <code>TaxonStatusCache</code> attribute.
1862
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1863
	 * @param state The {@link PesiExportState PesiExportState}.
1864
	 * @return The <code>TaxonStatusCache</code> attribute.
1865
	 * @throws UndefinedTransformerMethodException
1866
	 * @see MethodMapper
1867
	 */
1868
	@SuppressWarnings("unused")
1869
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1870
		return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1871
	}
1872

    
1873
	/**
1874
	 * Returns the <code>TypeNameFk</code> attribute.
1875
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1876
	 * @param state The {@link PesiExportState PesiExportState}.
1877
	 * @return The <code>TypeNameFk</code> attribute.
1878
	 * @see MethodMapper
1879
	 */
1880
	private static Integer getTypeNameFk(TaxonNameBase<?,?> taxonNameBase, PesiExportState state) {
1881
		Integer result = null;
1882
		if (taxonNameBase != null) {
1883
			Set<NameTypeDesignation> nameTypeDesignations = taxonNameBase.getNameTypeDesignations();
1884
			if (nameTypeDesignations.size() == 1) {
1885
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1886
				if (nameTypeDesignation != null) {
1887
					TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1888
					if (typeName != null) {
1889
						result = state.getDbId(typeName);
1890
					}
1891
				}
1892
			} else if (nameTypeDesignations.size() > 1) {
1893
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonNameBase.getUuid() + " (" + taxonNameBase.getTitleCache() + ")");
1894
			}
1895
		}
1896
		return result;
1897
	}
1898

    
1899
	/**
1900
	 * Returns the <code>TypeFullnameCache</code> attribute.
1901
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1902
	 * @return The <code>TypeFullnameCache</code> attribute.
1903
	 * @see MethodMapper
1904
	 */
1905
	@SuppressWarnings("unused")
1906
	private static String getTypeFullnameCache(TaxonNameBase<?,?> taxonName) {
1907
		String result = null;
1908

    
1909
		try {
1910
		if (taxonName != null) {
1911
			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1912
			if (nameTypeDesignations.size() == 1) {
1913
				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1914
				if (nameTypeDesignation != null) {
1915
					TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1916
					if (typeName != null) {
1917
						result = typeName.getTitleCache();
1918
					}
1919
				}
1920
			} else if (nameTypeDesignations.size() > 1) {
1921
				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1922
			}
1923
		}
1924

    
1925
		} catch (Exception e) {
1926
			e.printStackTrace();
1927
		}
1928
		return result;
1929
	}
1930

    
1931

    
1932
	/**
1933
	 * Returns the <code>QualityStatusFk</code> attribute.
1934
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1935
	 * @return The <code>QualityStatusFk</code> attribute.
1936
	 * @see MethodMapper
1937
	 */
1938
	private static Integer getQualityStatusFk(TaxonNameBase taxonName) {
1939
		BitSet sources = getSources(taxonName);
1940
		return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1941
	}
1942

    
1943

    
1944
	/**
1945
	 * Returns the <code>QualityStatusCache</code> attribute.
1946
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1947
	 * @return The <code>QualityStatusCache</code> attribute.
1948
	 * @throws UndefinedTransformerMethodException
1949
	 * @see MethodMapper
1950
	 */
1951
	@SuppressWarnings("unused")
1952
	private static String getQualityStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1953
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1954
	}
1955

    
1956

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

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

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

    
1985
	/**
1986
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1987
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1988
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1989
	 * @see MethodMapper
1990
	 */
1991
	@SuppressWarnings("unused")
1992
	private static String getTypeDesignationStatusCache(TaxonNameBase<?,?> taxonName) {
1993
		String result = null;
1994

    
1995
		try {
1996
		if (taxonName != null) {
1997
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1998
			if (typeDesignations.size() == 1) {
1999
				Object obj = typeDesignations.iterator().next().getTypeStatus();
2000
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
2001
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
2002
			} else if (typeDesignations.size() > 1) {
2003
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2004
			}
2005
		}
2006

    
2007
		} catch (Exception e) {
2008
			e.printStackTrace();
2009
		}
2010
		return result;
2011
	}
2012

    
2013
	/**
2014
	 * Returns the <code>FossilStatusFk</code> attribute.
2015
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2016
	 * @return The <code>FossilStatusFk</code> attribute.
2017
	 * @see MethodMapper
2018
	 */
2019
	@SuppressWarnings("unused")
2020
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
2021
		Integer result = null;
2022

    
2023
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
2024
		if (fossilStatuus.size() == 0){
2025
			return null;
2026
		}else if (fossilStatuus.size() > 1){
2027
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
2028
		}
2029
		String fossilStatus = fossilStatuus.iterator().next();
2030

    
2031
		int statusFk = state.getTransformer().FossilStatusCache2FossilStatusFk(fossilStatus);
2032
		return statusFk;
2033
	}
2034

    
2035
	/**
2036
	 * Returns the <code>FossilStatusCache</code> attribute.
2037
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2038
	 * @return The <code>FossilStatusCache</code> attribute.
2039
	 * @see MethodMapper
2040
	 */
2041
	@SuppressWarnings("unused")
2042
	private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
2043
		String result = null;
2044
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
2045
		if (fossilStatuus.size() == 0){
2046
			return null;
2047
		}
2048
		for (String strFossilStatus : fossilStatuus){
2049
			result = CdmUtils.concat(";", result, strFossilStatus);
2050
		}
2051
		return result;
2052
	}
2053

    
2054
	/**
2055
	 * Returns the <code>IdInSource</code> attribute.
2056
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2057
	 * @return The <code>IdInSource</code> attribute.
2058
	 * @see MethodMapper
2059
	 */
2060
	@SuppressWarnings("unused")
2061
	private static String getIdInSource(IdentifiableEntity taxonName) {
2062
		String result = null;
2063

    
2064
		try {
2065
			Set<IdentifiableSource> sources = getPesiSources(taxonName);
2066
			if (sources.size() > 1){
2067
				logger.warn("There is > 1 Pesi source. This is not yet handled.");
2068
			}
2069
			if (sources.size() == 0){
2070
				logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2071
			}
2072
			for (IdentifiableSource source : sources) {
2073
				Reference<?> ref = source.getCitation();
2074
				UUID refUuid = ref.getUuid();
2075
				String idInSource = source.getIdInSource();
2076
				if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
2077
					result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
2078
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2079
					result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
2080
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2081
					result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
2082
				}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){  //Index Fungorum
2083
					result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
2084
				}else{
2085
					if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2086
				}
2087

    
2088
				String sourceIdNameSpace = source.getIdNamespace();
2089
				if (sourceIdNameSpace != null) {
2090
					if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
2091
						result =  idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
2092
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
2093
						result =  idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
2094
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
2095
						result =  idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
2096
					} else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
2097
						result =  idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
2098
					}
2099
				}
2100
				if (result == null) {
2101
					logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
2102
				}
2103
			}
2104
		} catch (Exception e) {
2105
			e.printStackTrace();
2106
			logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
2107
		}
2108

    
2109
		if (result == null) {
2110
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2111
		}
2112
		return result;
2113
	}
2114

    
2115
	/**
2116
	 * Returns the idInSource for a given TaxonName only.
2117
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2118
	 * @return The idInSource.
2119
	 */
2120
	private static String getIdInSourceOnly(IdentifiableEntity identEntity) {
2121
		String result = null;
2122

    
2123
		// Get the sources first
2124
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2125

    
2126
		// Determine the idInSource
2127
		if (sources.size() == 1) {
2128
			IdentifiableSource source = sources.iterator().next();
2129
			if (source != null) {
2130
				result = source.getIdInSource();
2131
			}
2132
		} else if (sources.size() > 1) {
2133
			int count = 1;
2134
			result = "";
2135
			for (IdentifiableSource source : sources) {
2136
				result += source.getIdInSource();
2137
				if (count < sources.size()) {
2138
					result += "; ";
2139
				}
2140
				count++;
2141
			}
2142

    
2143
		}
2144

    
2145
		return result;
2146
	}
2147

    
2148
	/**
2149
	 * Returns the Sources for a given TaxonName only.
2150
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2151
	 * @return The Sources.
2152
	 */
2153
	private static Set<IdentifiableSource> getPesiSources(IdentifiableEntity identEntity) {
2154
		Set<IdentifiableSource> sources = new java.util.HashSet<IdentifiableSource>();
2155

    
2156
		//Taxon Names
2157
		if (identEntity.isInstanceOf(TaxonNameBase.class)){
2158
			// Sources from TaxonName
2159
			TaxonNameBase taxonName = CdmBase.deproxy(identEntity, TaxonNameBase.class);
2160
			Set<IdentifiableSource> testSources = identEntity.getSources();
2161
			sources = filterPesiSources(identEntity.getSources());
2162

    
2163
			if (sources.size() == 0 && testSources.size()>0){
2164
				IdentifiableSource source = testSources.iterator().next();
2165
				logger.warn("There are sources, but they are no pesi sources!!!" + source.getIdInSource() + " - " + source.getIdNamespace() + " - " + source.getCitation().getTitleCache());
2166
			}
2167
			if (sources.size() > 1) {
2168
				logger.warn("This TaxonName has more than one Source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() + ")");
2169
			}
2170

    
2171
			// name has no PESI source, take sources from TaxonBase
2172
			if (sources == null || sources.isEmpty()) {
2173
				Set<TaxonBase> taxa = taxonName.getTaxonBases();
2174
				for (TaxonBase taxonBase: taxa){
2175
					sources.addAll(filterPesiSources(taxonBase.getSources()));
2176
				}
2177
			}
2178

    
2179
		//for TaxonBases
2180
		}else if (identEntity.isInstanceOf(TaxonBase.class)){
2181
			sources = filterPesiSources(identEntity.getSources());
2182
		}
2183

    
2184
		/*TODO: deleted only for testing the inferred synonyms
2185
		if (sources == null || sources.isEmpty()) {
2186
			logger.warn("This TaxonName has no PESI Sources: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2187
		}else if (sources.size() > 1){
2188
			logger.warn("This Taxon(Name) has more than 1 PESI source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2189
		}
2190
		*/
2191
		return sources;
2192
	}
2193

    
2194
	// return all sources with a PESI reference
2195
	private static Set<IdentifiableSource> filterPesiSources(Set<? extends IdentifiableSource> sources) {
2196
		Set<IdentifiableSource> result = new HashSet<IdentifiableSource>();
2197
		for (IdentifiableSource source : sources){
2198
			Reference ref = source.getCitation();
2199
			UUID refUuid = ref.getUuid();
2200
			if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed) ||
2201
				refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)||
2202
				refUuid.equals(PesiTransformer.uuidSourceRefErms)||
2203
				refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum) ||
2204
				refUuid.equals(PesiTransformer.uuidSourceRefAuct)){
2205
				result.add(source);
2206
			}
2207
		}
2208
		return result;
2209
	}
2210

    
2211
	/**
2212
	 * Returns the <code>GUID</code> attribute.
2213
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2214
	 * @return The <code>GUID</code> attribute.
2215
	 * @see MethodMapper
2216
	 */
2217
	private static String getGUID(TaxonBase<?> taxon) {
2218
		if (taxon.getLsid() != null ){
2219
			return taxon.getLsid().getLsid();
2220
		}else if (taxon.hasMarker(PesiTransformer.uuidMarkerGuidIsMissing, true)){
2221
			return null;
2222
		}else{
2223
			return taxon.getUuid().toString();
2224
		}
2225
	}
2226

    
2227

    
2228

    
2229

    
2230
	/**
2231
	 * Returns the <code>DerivedFromGuid</code> attribute.
2232
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2233
	 * @return The <code>DerivedFromGuid</code> attribute.
2234
	 * @see MethodMapper
2235
	 */
2236
	@SuppressWarnings("unused")
2237
	private static String getDerivedFromGuid(TaxonBase<?> taxon) {
2238
		String result = null;
2239
		try {
2240
		// The same as GUID for now
2241
		result = getGUID(taxon);
2242
		} catch (Exception e) {
2243
			e.printStackTrace();
2244
		}
2245
		return result;
2246
	}
2247

    
2248
	/**
2249
	 * Returns the <code>CacheCitation</code> attribute.
2250
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2251
	 * @return The CacheCitation.
2252
	 * @see MethodMapper
2253
	 */
2254
	@SuppressWarnings("unused")
2255
	private static String getCacheCitation(TaxonBase taxon) {
2256
		// !!! See also doPhaseUpdates
2257

    
2258
		TaxonNameBase<?,?> taxonName = taxon.getName();
2259
		String result = "";
2260
		//TODO implement anew for taxa
2261
		try {
2262
			BitSet sources = getSources(taxonName);
2263
			if (sources.isEmpty()) {
2264
//				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2265
			} else if (sources.get(PesiTransformer.SOURCE_ERMS)) {
2266
				// 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...
2267
				// 		 So the following code is some kind of harmless assumption.
2268
				Set<Extension> extensions = taxonName.getExtensions();
2269
				for (Extension extension : extensions) {
2270
					if (extension.getType().equals(cacheCitationExtensionType)) {
2271
						result = extension.getValue();
2272
					}
2273
				}
2274
			} else {
2275
				String expertName = getExpertName(taxon);
2276
				String webShowName = getWebShowName(taxonName);
2277

    
2278
				// idInSource only
2279
				String idInSource = getIdInSourceOnly(taxonName);
2280

    
2281
				// build the cacheCitation
2282
				if (expertName != null) {
2283
					result += expertName + ". ";
2284
				} else {
2285
					if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
2286
				}
2287
				if (webShowName != null) {
2288
					result += webShowName + ". ";
2289
				} else {
2290
					logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2291
				}
2292

    
2293
				if (getOriginalDB(taxonName).equals("FaEu")) {
2294
					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
2295
				} else if (getOriginalDB(taxonName).equals("EM")) {
2296
					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
2297
				}
2298

    
2299
				if (idInSource != null) {
2300
					result += idInSource;
2301
				} else {
2302
					logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2303
				}
2304
			}
2305
		} catch (Exception e) {
2306
			e.printStackTrace();
2307
		}
2308

    
2309
		if (StringUtils.isBlank(result)) {
2310
			return null;
2311
		} else {
2312
			return result;
2313
		}
2314
	}
2315

    
2316
	/**
2317
	 * Returns the <code>OriginalDB</code> attribute.
2318
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2319
	 * @return The <code>OriginalDB</code> attribute.
2320
	 * @see MethodMapper
2321
	 */
2322
	private static String getOriginalDB(IdentifiableEntity identEntity) {
2323
		// Sources from TaxonName
2324
		BitSet sources  = getSources(identEntity);
2325
		return PesiTransformer.getOriginalDbBySources(sources);
2326
	}
2327

    
2328
	/**
2329
	 * Returns the <code>LastAction</code> attribute.
2330
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2331
	 * @return The <code>LastAction</code> attribute.
2332
	 * @see MethodMapper
2333
	 */
2334
	@SuppressWarnings("unused")
2335
	private static String getLastAction(IdentifiableEntity<?> identEntity) {
2336
		String result = null;
2337
		try {
2338
		Set<Extension> extensions = identEntity.getExtensions();
2339
		for (Extension extension : extensions) {
2340
			if (extension.getType().equals(lastActionExtensionType)) {
2341
				result = extension.getValue();
2342
			}
2343
		}
2344
		} catch (Exception e) {
2345
			e.printStackTrace();
2346
		}
2347
		return result;
2348
	}
2349

    
2350
	/**
2351
	 * Returns the <code>LastActionDate</code> attribute.
2352
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2353
	 * @return The <code>LastActionDate</code> attribute.
2354
	 * @see MethodMapper
2355
	 */
2356
	@SuppressWarnings({ "unused" })
2357
	private static DateTime getLastActionDate(IdentifiableEntity identEntity) {
2358
		DateTime result = null;
2359
		try {
2360
			Set<Extension> extensions = identEntity.getExtensions();
2361
			for (Extension extension : extensions) {
2362
				if (extension.getType().equals(lastActionDateExtensionType)) {
2363
					String dateTime = extension.getValue();
2364
					if (dateTime != null) {
2365
						DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
2366
						result = formatter.parseDateTime(dateTime);
2367
					}
2368
				}
2369
			}
2370
		} catch (Exception e) {
2371
			e.printStackTrace();
2372
		}
2373
		return result;
2374
	}
2375

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

    
2398
	/**
2399
	 * Returns the <code>ExpertFk</code> attribute.
2400
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2401
	 * @param state The {@link PesiExportState PesiExportState}.
2402
	 * @return The <code>ExpertFk</code> attribute.
2403
	 * @see MethodMapper
2404
	 */
2405
	private static Integer getExpertFk(Reference<?> reference, PesiExportState state) {
2406
		Integer result = state.getDbId(reference);
2407
		return result;
2408
	}
2409

    
2410
	/**
2411
	 * Returns the <code>SpeciesExpertName</code> attribute.
2412
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2413
	 * @return The <code>SpeciesExpertName</code> attribute.
2414
	 * @see MethodMapper
2415
	 */
2416
	@SuppressWarnings("unused")
2417
	private static String getSpeciesExpertName(TaxonBase<?> taxonName) {
2418
		String result = null;
2419
		try {
2420
		Set<Extension> extensions = taxonName.getExtensions();
2421
		for (Extension extension : extensions) {
2422
			if (extension.getType().equals(speciesExpertNameExtensionType)) {
2423
				result = extension.getValue();
2424
			}
2425
		}
2426
		} catch (Exception e) {
2427
			e.printStackTrace();
2428
		}
2429
		return result;
2430
	}
2431

    
2432
	/**
2433
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2434
	 * @param reference The {@link Reference Reference}.
2435
	 * @param state The {@link PesiExportState PesiExportState}.
2436
	 * @return The <code>SpeciesExpertFk</code> attribute.
2437
	 * @see MethodMapper
2438
	 */
2439
	private static Integer getSpeciesExpertFk(Reference<?> reference, PesiExportState state) {
2440
		Integer result = state.getDbId(reference);
2441
		return result;
2442
	}
2443

    
2444

    
2445
	/**
2446
	 * Returns the source (E+M, Fauna Europaea, Index Fungorum, ERMS) of a given
2447
	 * Identifiable Entity as a BitSet
2448
	 * @param identEntity
2449
	 * @return
2450
	 */
2451
	private static BitSet getSources(IdentifiableEntity<?> identEntity){
2452
		BitSet bitSet = new BitSet();
2453
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2454
		for (IdentifiableSource source : sources) {
2455
			Reference<?> ref = source.getCitation();
2456
			UUID refUuid = ref.getUuid();
2457
			if (refUuid.equals(BerlinModelTransformer.uuidSourceRefEuroMed)){
2458
				bitSet.set(PesiTransformer.SOURCE_EM);
2459
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2460
				bitSet.set(PesiTransformer.SOURCE_FE);
2461
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2462
				bitSet.set(PesiTransformer.SOURCE_ERMS);
2463
			}else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){
2464
				bitSet.set(PesiTransformer.SOURCE_IF);
2465
			}else{
2466
				if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2467
			}
2468
		}
2469
		return bitSet;
2470

    
2471
	}
2472

    
2473
	protected static NonViralNameDefaultCacheStrategy getCacheStrategy(TaxonNameBase<?, ?> taxonName) {
2474
		taxonName = CdmBase.deproxy(taxonName, TaxonNameBase.class);
2475
		NonViralNameDefaultCacheStrategy<?> cacheStrategy;
2476
		if (taxonName.isInstanceOf(ZoologicalName.class)){
2477
			cacheStrategy = zooNameStrategy;
2478
		}else if (taxonName.isInstanceOf(BotanicalName.class)) {
2479
			cacheStrategy = botanicalNameStrategy;
2480
		}else if (taxonName.getClass().equals(NonViralName.class)) {
2481
			cacheStrategy = nonViralNameStrategy;
2482
		}else if (taxonName.getClass().equals(BacterialName.class)) {
2483
			cacheStrategy = bacterialNameStrategy;
2484
		}else{
2485
			logger.error("Unhandled taxon name type. Can't define strategy class");
2486
			cacheStrategy = botanicalNameStrategy;
2487
		}
2488
		return cacheStrategy;
2489
	}
2490

    
2491
	/**
2492
	 * Returns the <code>TaxonFk1</code> attribute. It corresponds to a CDM <code>TaxonRelationship</code>.
2493
	 * @param relationship The {@link RelationshipBase Relationship}.
2494
	 * @param state The {@link PesiExportState PesiExportState}.
2495
	 * @return The <code>TaxonFk1</code> attribute.
2496
	 * @see MethodMapper
2497
	 */
2498
	private static Integer getTaxonFk1(RelationshipBase<?, ?, ?> relationship, PesiExportState state) {
2499

    
2500
		return getObjectFk(relationship, state, true);
2501
	}
2502

    
2503
	/**
2504
	 * Returns the <code>TaxonFk2</code> attribute. It corresponds to a CDM <code>SynonymRelationship</code>.
2505
	 * @param relationship The {@link RelationshipBase Relationship}.
2506
	 * @param state The {@link PesiExportState PesiExportState}.
2507
	 * @return The <code>TaxonFk2</code> attribute.
2508
	 * @see MethodMapper
2509
	 */
2510
	private static Integer getTaxonFk2(RelationshipBase<?, ?, ?> relationship, PesiExportState state) {
2511
		return getObjectFk(relationship, state, false);
2512
	}
2513

    
2514
	/**
2515
	 * Returns the database key of an object in the given relationship.
2516
	 * @param relationship {@link RelationshipBase RelationshipBase}.
2517
	 * @param state {@link PesiExportState PesiExportState}.
2518
	 * @param isFrom A boolean value indicating whether the database key of the parent or child in this relationship is searched. <code>true</code> means the child is searched. <code>false</code> means the parent is searched.
2519
	 * @return The database key of an object in the given relationship.
2520
	 */
2521
	private static Integer getObjectFk(RelationshipBase<?, ?, ?> relationship, PesiExportState state, boolean isFrom) {
2522
		TaxonBase<?> taxonBase = null;
2523
		if (relationship.isInstanceOf(TaxonRelationship.class)) {
2524
			TaxonRelationship tr = (TaxonRelationship)relationship;
2525
			taxonBase = (isFrom) ? tr.getFromTaxon():  tr.getToTaxon();
2526
		} else if (relationship.isInstanceOf(SynonymRelationship.class)) {
2527
			SynonymRelationship sr = (SynonymRelationship)relationship;
2528
			taxonBase = (isFrom) ? sr.getSynonym() : sr.getAcceptedTaxon();
2529
		} else if (relationship.isInstanceOf(NameRelationship.class) ||  relationship.isInstanceOf(HybridRelationship.class)) {
2530
			if (isFrom){
2531
				return state.getDbId(state.getCurrentFromObject());
2532
			}else{
2533
				return state.getDbId(state.getCurrentToObject());
2534
			}
2535
		}
2536
		if (taxonBase != null) {
2537
			if (! isPesiTaxon(taxonBase)){
2538
				logger.warn("Related taxonBase is not a PESI taxon. Taxon: " + taxonBase.getId() + "/" + taxonBase.getUuid() + "; TaxonRel: " +  relationship.getId() + "(" + relationship.getType().getTitleCache() + ")");
2539
				return null;
2540
			}else{
2541
				return state.getDbId(taxonBase);
2542
			}
2543

    
2544
		}
2545
		logger.warn("No taxon found in state for relationship: " + relationship.toString());
2546
		return null;
2547
	}
2548

    
2549
	/**
2550
	 * Returns the <code>RelQualifierCache</code> attribute.
2551
	 * @param relationship The {@link RelationshipBase Relationship}.
2552
	 * @return The <code>RelQualifierCache</code> attribute.
2553
	 * @see MethodMapper
2554
	 */
2555
	@SuppressWarnings("unused")
2556
	private static String getRelQualifierCache(RelationshipBase<?, ?, ?> relationship, PesiExportState state) {
2557
		String result = null;
2558
		NomenclaturalCode code = null;
2559
		if (relationship.isInstanceOf(TaxonRelationship.class)){
2560
			code = CdmBase.deproxy(relationship, TaxonRelationship.class).getToTaxon().getName().getNomenclaturalCode();
2561
		}else if (relationship.isInstanceOf(SynonymRelationship.class)){
2562
			code = CdmBase.deproxy(relationship, SynonymRelationship.class).getAcceptedTaxon().getName().getNomenclaturalCode();
2563
		}else if (relationship.isInstanceOf(NameRelationship.class)){
2564
			code = CdmBase.deproxy(relationship,  NameRelationship.class).getFromName().getNomenclaturalCode();
2565
		}else if (relationship.isInstanceOf(HybridRelationship.class)){
2566
			code = CdmBase.deproxy(relationship,  HybridRelationship.class).getParentName().getNomenclaturalCode();
2567
		}
2568
		if (code != null) {
2569
			result = state.getConfig().getTransformer().getCacheByRelationshipType(relationship, code);
2570
		} else {
2571
			logger.error("NomenclaturalCode is NULL while creating the following relationship: " + relationship.getUuid());
2572
		}
2573
		return result;
2574
	}
2575

    
2576
	/**
2577
	 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2578
	 * @param relationship The {@link RelationshipBase Relationship}.
2579
	 * @return The <code>RelTaxonQualifierFk</code> attribute.
2580
	 * @see MethodMapper
2581
	 */
2582
	@SuppressWarnings("unused")
2583
	private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2584
		return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2585
	}
2586
	/**
2587
	 * Returns the <code>Notes</code> attribute.
2588
	 * @param relationship The {@link RelationshipBase Relationship}.
2589
	 * @return The <code>Notes</code> attribute.
2590
	 * @see MethodMapper
2591
	 */
2592
	@SuppressWarnings("unused")
2593
	private static String getNotes(RelationshipBase<?, ?, ?> relationship) {
2594
		// TODO
2595
		return null;
2596
	}
2597

    
2598

    
2599
	/**
2600
	 * Returns the CDM to PESI specific export mappings.
2601
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2602
	 */
2603
	private PesiExportMapping getMapping() {
2604
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2605

    
2606
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2607
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2608
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2609
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2610

    
2611
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2612

    
2613
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2614
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2615
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misallied Names are handled differently
2616
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2617

    
2618
		// DisplayName
2619
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2620

    
2621
		// FossilStatus (Fk, Cache)
2622
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2623
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2624

    
2625
		//handled by name mapping
2626
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2627
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2628

    
2629
		//experts
2630
		ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
2631
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2632
		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
2633
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2634

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

    
2638
		addNameMappers(mapping);
2639

    
2640
		return mapping;
2641
	}
2642

    
2643
	/**
2644
	 * Returns the CDM to PESI specific export mappings.
2645
	 * @param state
2646
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2647
	 * @throws UndefinedTransformerMethodException
2648
	 */
2649
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2650
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2651

    
2652
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2653

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

    
2656
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonNameBase.class));
2657
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonNameBase.class));
2658
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonNameBase.class, PesiExportState.class));
2659
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
2660
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2661
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
2662
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonNameBase.class));
2663
		mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonNameBase.class));
2664

    
2665

    
2666

    
2667
		// DisplayName
2668
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonNameBase.class));
2669

    
2670
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2671
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2672

    
2673
		addNameMappers(mapping);
2674
		//TODO add author mapper, TypeNameFk
2675

    
2676
		return mapping;
2677
	}
2678

    
2679
	private void addNameMappers(PesiExportMapping mapping) {
2680
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2681
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2682
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2683
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2684

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

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

    
2691

    
2692
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonNameBase.class));
2693

    
2694
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonNameBase.class));
2695
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2696
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonNameBase.class));
2697
		//TODO TypeNameFk
2698

    
2699
		//quality status
2700
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonNameBase.class));
2701
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2702

    
2703
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2704
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2705

    
2706
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2707

    
2708
	}
2709

    
2710
	private PesiExportMapping getSynRelMapping() {
2711
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2712

    
2713
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getTaxonFk1", RelationshipBase.class, PesiExportState.class));
2714
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk2", this.getClass(), "getTaxonFk2", RelationshipBase.class, PesiExportState.class));
2715
		mapping.addMapper(MethodMapper.NewInstance("RelTaxonQualifierFk", this,  RelationshipBase.class));
2716
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this, RelationshipBase.class, PesiExportState.class));
2717
		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2718

    
2719
		return mapping;
2720
	}
2721

    
2722
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state)  throws UndefinedTransformerMethodException{
2723
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2724

    
2725
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2726
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonNameBase.class, "Name"));
2727

    
2728
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2729
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2730
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonNameBase.class));
2731

    
2732

    
2733
		//we have only nomenclatural references here
2734
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2735
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2736

    
2737
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2738

    
2739
		return mapping;
2740
	}
2741

    
2742
}
(11-11/12)