Project

General

Profile

Download (105 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().loadRankSpecificRootNodes(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.error("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
		}
848
		if (taxonList.size() == 0) {
849
			logger.info("No " + parentPluralString + " left to fetch.");
850
		}
851

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

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

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

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

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

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

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

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

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

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

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

    
912
						}
913
					}
914

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

    
930

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

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

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

    
988

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

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

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

    
1013

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

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

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

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

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

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

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

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

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

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

    
1167
							localTreeIndex.append(taxonId + "#");
1168

    
1169
							saveData(childNode, parentNode, localTreeIndex, state, taxonId);
1170

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

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

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

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

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

    
1219
			}
1220

    
1221
			invokeParentTaxonFkAndTreeIndex(state.getDbId(parentTaxon), currentTaxonFk,	treeIndex);
1222
		}
1223

    
1224
	}
1225

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

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

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

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

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

    
1275

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

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

    
1301
			if (kingdomFk != null) {
1302

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

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

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

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

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

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

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

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

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

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

    
1405
		String sql;
1406
		Source destination =  pesiConfig.getDestination();
1407

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

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

    
1423

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

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

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

    
1471

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

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

    
1511

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

    
1528
	}
1529

    
1530

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

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

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

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

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

    
1601
	}
1602

    
1603

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

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

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

    
1636

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

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

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

    
1675

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

    
1693

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

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

    
1734
		}
1735
		return null;
1736
	}
1737

    
1738

    
1739

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

    
1755
		return result;
1756
	}
1757
*/
1758

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

    
1778

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

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

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

    
1813

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

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

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

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

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

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

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

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

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

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

    
1929

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

    
1941

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

    
1954

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

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

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

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

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

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

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

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

    
2029
		int statusFk = state.getTransformer().FossilStatusCache2FossilStatusFk(fossilStatus);
2030
		return statusFk;
2031
	}
2032

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

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

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

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

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

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

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

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

    
2141
		}
2142

    
2143
		return result;
2144
	}
2145

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

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

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

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

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

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

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

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

    
2225

    
2226

    
2227

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

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

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

    
2276
				// idInSource only
2277
				String idInSource = getIdInSourceOnly(taxonName);
2278

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

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

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

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

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

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

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

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

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

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

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

    
2442

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

    
2469
	}
2470

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

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

    
2498
		return getObjectFk(relationship, state, true);
2499
	}
2500

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

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

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

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

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

    
2596

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

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

    
2609
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2610

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

    
2616
		// DisplayName
2617
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2618

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

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

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

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

    
2636
		addNameMappers(mapping);
2637

    
2638
		return mapping;
2639
	}
2640

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

    
2650
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2651

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

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

    
2663

    
2664

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

    
2668
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2669
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2670

    
2671
		addNameMappers(mapping);
2672
		//TODO add author mapper, TypeNameFk
2673

    
2674
		return mapping;
2675
	}
2676

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

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

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

    
2689

    
2690
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonNameBase.class));
2691

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

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

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

    
2704
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2705

    
2706
	}
2707

    
2708
	private PesiExportMapping getSynRelMapping() {
2709
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2710

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

    
2717
		return mapping;
2718
	}
2719

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

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

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

    
2730

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

    
2735
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2736

    
2737
		return mapping;
2738
	}
2739

    
2740
}
(11-11/12)