Project

General

Profile

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

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

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

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

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

    
95
    private static final long serialVersionUID = -3412722058790200078L;
96
    private static final Logger logger = Logger.getLogger(PesiTaxonExport.class);
97

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

    
100
	private static int modCount = 1000;
101
	private static final String dbTableName = "Taxon";
102
	private static final String dbTableNameSynRel = "RelTaxon";
103
	private static final String dbTableAdditionalSourceRel = "AdditionalTaxonSource";
104

    
105
	private static final String pluralString = "Taxa";
106
	private static final String parentPluralString = "Taxa";
107

    
108
	private PreparedStatement parentTaxonFk_TreeIndex_KingdomFkStmt;
109
	private PreparedStatement parentTaxonFkStmt;
110
	private PreparedStatement rankTypeExpertsUpdateStmt;
111
	private PreparedStatement rankUpdateStmt;
112
	private Integer kingdomFk;
113
	private AnnotationType treeIndexAnnotationType;
114
	private static ExtensionType lastActionExtensionType;
115
	private static ExtensionType lastActionDateExtensionType;
116
	private static ExtensionType expertNameExtensionType;
117
	private static ExtensionType speciesExpertNameExtensionType;
118
	private static ExtensionType cacheCitationExtensionType;
119
	public static TaxonNameDefaultCacheStrategy zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
120
	public static TaxonNameDefaultCacheStrategy nonViralNameStrategy = TaxonNameDefaultCacheStrategy.NewInstance();
121
	private static int currentTaxonId;
122

    
123
	protected AnnotationType getTreeIndexAnnotationType() {
124
		return treeIndexAnnotationType;
125
	}
126

    
127
	protected void setTreeIndexAnnotationType(AnnotationType treeIndexAnnotationType) {
128
		this.treeIndexAnnotationType = treeIndexAnnotationType;
129
	}
130

    
131
	enum NamePosition {
132
		beginning,
133
		end,
134
		between,
135
		alone,
136
		nowhere
137
	}
138

    
139
	public PesiTaxonExport() {
140
		super();
141
	}
142

    
143
	@Override
144
	public Class<? extends CdmBase> getStandardMethodParameter() {
145
		return standardMethodParameter;
146
	}
147

    
148
	@Override
149
	protected void doInvoke(PesiExportState state) {
150
		try {
151
			logger.info("*** Started Making " + pluralString + " ...");
152

    
153
			initPreparedStatements(state);
154

    
155
			// Stores whether this invoke was successful or not.
156
			boolean success = true;
157

    
158
			// PESI: Clear the database table Taxon.
159
			doDelete(state);
160

    
161
			// Get specific mappings: (CDM) Taxon -> (PESI) Taxon
162
			PesiExportMapping mapping = getMapping();
163
			PesiExportMapping synonymRelMapping = getSynRelMapping();
164
			PesiExportMapping additionalSourceMapping = getAdditionalSourceMapping(state);
165

    
166
			// Initialize the db mapper
167
			mapping.initialize(state);
168
			synonymRelMapping.initialize(state);
169
			additionalSourceMapping.initialize(state);
170

    
171
			// Find extensionTypes
172
			lastActionExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtLastAction);
173
			lastActionDateExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtLastActionDate);
174
			expertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
175
			speciesExpertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtSpeciesExpertName);
176
			cacheCitationExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtCacheCitation);
177

    
178
			//Export Taxa..
179
			success &= doPhase01(state, mapping, additionalSourceMapping);
180

    
181
			//"PHASE 1b: Handle names without taxa ...
182
			success &= doNames(state, additionalSourceMapping);
183

    
184

    
185
			// 2nd Round: Add ParentTaxonFk to each taxon
186
			success &= doPhase02(state);
187

    
188
			//PHASE 3: Add Rank data, KingdomFk, TypeNameFk ...
189
			success &= doPhase03(state);
190

    
191
			// 4nd Round: Add TreeIndex to each taxon
192
			success &= doPhase04(state);
193

    
194

    
195
			//"PHASE 5: Creating Inferred Synonyms...
196
			success &= doPhase05(state, mapping, synonymRelMapping);
197

    
198
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
199

    
200
			if (!success){
201
				state.getResult().addError("An error occurred in PesiTaxonExport.doInvoke. Success = false");
202
			}
203
			return;
204
		} catch (Exception e) {
205
			e.printStackTrace();
206
			logger.error(e.getMessage());
207
			state.getResult().addException(e);
208
		}
209
	}
210

    
211

    
212
	private void initPreparedStatements(PesiExportState state) throws SQLException {
213
		initTreeIndexStatement(state);
214
		initRankExpertsUpdateStmt(state);
215
		initRankUpdateStatement(state);
216

    
217
		initParentFkStatement(state);
218
	}
219

    
220
	// Prepare TreeIndex-And-KingdomFk-Statement
221
	private void initTreeIndexStatement(PesiExportState state) throws SQLException {
222
		Connection connection = state.getConfig().getDestination().getConnection();
223
		String parentTaxonFk_TreeIndex_KingdomFkSql = "UPDATE Taxon SET ParentTaxonFk = ?, TreeIndex = ? WHERE TaxonId = ?";
224
		parentTaxonFk_TreeIndex_KingdomFkStmt = connection.prepareStatement(parentTaxonFk_TreeIndex_KingdomFkSql);
225
	}
226

    
227
	// Prepare TreeIndex-And-KingdomFk-Statement
228
	private void initParentFkStatement(PesiExportState state) throws SQLException {
229
		Connection connection = state.getConfig().getDestination().getConnection();
230
		String parentTaxonFkSql = "UPDATE Taxon SET ParentTaxonFk = ? WHERE TaxonId = ?";
231
		parentTaxonFkStmt = connection.prepareStatement(parentTaxonFkSql);
232
	}
233

    
234
	private void initRankUpdateStatement(PesiExportState state) throws SQLException {
235
		Connection connection = state.getConfig().getDestination().getConnection();
236
		String rankSql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, KingdomFk = ? WHERE TaxonId = ?";
237
		rankUpdateStmt = connection.prepareStatement(rankSql);
238
	}
239

    
240
	private void initRankExpertsUpdateStmt(PesiExportState state) throws SQLException {
241
//		String sql_old = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ?, " +
242
//				"ExpertFk = ?, SpeciesExpertFk = ? WHERE TaxonId = ?";
243
		//TODO handle experts GUIDs
244
		Connection connection = state.getConfig().getDestination().getConnection();
245

    
246
		String sql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ? " +
247
				" WHERE TaxonId = ?";
248
		rankTypeExpertsUpdateStmt = connection.prepareStatement(sql);
249
	}
250

    
251
	private boolean doPhase01(PesiExportState state, PesiExportMapping mapping, PesiExportMapping additionalSourceMapping){
252
		int count = 0;
253
		int pastCount = 0;
254
		List<TaxonBase> list;
255
		boolean success = true;
256
		// Get the limit for objects to save within a single transaction.
257
		int limit = state.getConfig().getLimitSave();
258

    
259
		logger.info("PHASE 1: Export Taxa...limit is " + limit);
260
		// Start transaction
261
		TransactionStatus txStatus = startTransaction(true);
262
		if (logger.isDebugEnabled()) {
263
            logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
264
            logger.info("Taking snapshot at the beginning of phase 1 of taxonExport");
265
            //ProfilerController.memorySnapshot();
266
        }
267

    
268
		int partitionCount = 0;
269
		while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)) != null   ) {
270

    
271
			logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
272

    
273
			for (TaxonBase<?> taxon : list) {
274
				doCount(count++, modCount, pluralString);
275
				TaxonName taxonName = taxon.getName();
276

    
277
				TaxonName nvn = CdmBase.deproxy(taxonName);
278
//				System.err.println(nvn.getTitleCache());
279
				if (! nvn.isProtectedTitleCache()){
280
					nvn.setTitleCache(null, false);
281
				}
282
				if (! nvn.isProtectedNameCache()){
283
					nvn.setNameCache(null, false);
284
				}
285
				if (! nvn.isProtectedFullTitleCache()){
286
					nvn.setFullTitleCache(null, false);
287
				}
288
				if (! nvn.isProtectedAuthorshipCache()){
289
					nvn.setAuthorshipCache(null, false);
290
				}
291
				try{
292
    				if (nvn.getRank().equals(Rank.KINGDOM())){
293
    				    if(taxon.isInstanceOf(Taxon.class)){
294
    				        String treeIndex = ((Taxon)taxon).getTaxonNodes().iterator().next().treeIndex();
295
    				        Integer kingdomId = PesiTransformer.pesiKingdomMap.get(nvn.getGenusOrUninomial());
296
    				        state.getTreeIndexKingdomMap().put(treeIndex, kingdomId);
297
    				    }else{
298
    				        logger.warn("Kingdom taxon is not of class Taxon but " + taxon.getClass().getSimpleName() + ": " + nvn.getGenusOrUninomial());
299
    				    }
300
    				}
301
				}catch(NullPointerException e){
302
				    logger.error(nvn.getTitleCache() + " has no Rank!");
303
				    System.err.println(nvn.getTitleCache() + " has no Rank!");
304
				}
305
				//core mapping
306
				success &= mapping.invoke(taxon);
307
				//additional source
308
				if (nvn.getNomenclaturalReference() != null || StringUtils.isNotBlank(nvn.getNomenclaturalMicroReference() )){
309
					additionalSourceMapping.invoke(taxon);
310
				}
311

    
312
				//TODO switch on again, leads to some warnings in ERMS for taxa of not correctly handled kingdoms
313
//				validatePhaseOne(taxon, nvn);
314
			}
315

    
316
			// Commit transaction
317
			commitTransaction(txStatus);
318
			logger.debug("Committed transaction.");
319
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 01)");
320
			pastCount = count;
321
			/*logger.warn("Taking snapshot at the end of the loop of phase 1 of taxonExport");
322
			//ProfilerController.memorySnapshot();
323
			*/
324
			// Start transaction
325
			txStatus = startTransaction(true);
326
			if (logger.isDebugEnabled()) {
327
                logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
328
            }
329

    
330
		}
331
		logger.info("No " + pluralString + " left to fetch.");
332

    
333
		// Commit transaction
334
		commitTransaction(txStatus);
335
		txStatus = null;
336
		logger.debug("Committed transaction.");
337
		if (logger.isDebugEnabled()){
338
			logger.debug("Taking snapshot at the end of phase 1 of taxonExport");
339
//			ProfilerController.memorySnapshot();
340
		}
341
		return success;
342
	}
343

    
344

    
345
	private void validatePhaseOne(TaxonBase<?> taxon, TaxonName taxonName) {
346

    
347
	    // Check whether some rules are violated
348
		NomenclaturalCode nomenclaturalCode = taxonName.getNameType();
349
		String genusOrUninomial = taxonName.getGenusOrUninomial();
350
		String specificEpithet = taxonName.getSpecificEpithet();
351
		String infraSpecificEpithet = taxonName.getInfraSpecificEpithet();
352
		String infraGenericEpithet = taxonName.getInfraGenericEpithet();
353
		Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
354

    
355
		if (rankFk == null) {
356
			logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
357
		} else {
358

    
359
			// Check whether infraGenericEpithet is set correctly
360
			// 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
361
			// 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
362

    
363
			int ancestorLevel = 0;
364
			if (taxonName.getRank().equals(Rank.SUBSPECIES())) {
365
				// The accepted taxon two rank levels above should be of rank subgenus
366
				ancestorLevel  = 2;
367
			}
368
			if (taxonName.getRank().equals(Rank.SPECIES())) {
369
				// The accepted taxon one rank level above should be of rank subgenus
370
				ancestorLevel = 1;
371
			}
372
			if (ancestorLevel > 0) {
373
				if (validateAncestorOfSpecificRank(taxon, ancestorLevel, Rank.SUBGENUS())) {
374
					// The child (species or subspecies) of this parent (subgenus) has to have an infraGenericEpithet
375
					if (infraGenericEpithet == null) {
376
						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() + ")");
377
						// maybe the taxon could be named here
378
					}
379
				}
380
			}
381

    
382
			if (infraGenericEpithet == null && rankFk.intValue() == 190) {
383
				logger.warn("InfraGenericEpithet was not determined although it should exist for rank 190: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
384
			}
385
			if (specificEpithet != null && rankFk.intValue() < 216) {
386
				logger.warn("SpecificEpithet was determined for rank " + rankFk + " although it should only exist for ranks higher or equal to 220: TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
387
			}
388
			if (infraSpecificEpithet != null && rankFk.intValue() < 225) {
389
				String message = "InfraSpecificEpithet '" +infraSpecificEpithet + "' was determined for rank " + rankFk + " although it should only exist for ranks higher or equal to 230: "  + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")";
390
				if (StringUtils.isNotBlank(infraSpecificEpithet)){
391
					logger.warn(message);
392
				}else{
393
					logger.warn(message);
394
				}
395
			}
396
		}
397
		if (infraSpecificEpithet != null && specificEpithet == null) {
398
			logger.warn("An infraSpecificEpithet was determined, but a specificEpithet was not determined: "  + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
399
		}
400
		if (genusOrUninomial == null) {
401
			logger.warn("GenusOrUninomial was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
402
		}
403
	}
404

    
405
	/**
406
	 * 2nd Round: Add ParentTaxonFk to each taxon and add Biota if not exists
407
	 * @param state
408
	 * @return
409
	 */
410
	private boolean doPhase02(PesiExportState state) {
411
		int count = 0;
412
		int pastCount = 0;
413
		boolean success = true;
414
		if (! state.getConfig().isDoParentAndBiota()){
415
			logger.info ("Ignore PHASE 2: Make ParentFk and Biota...");
416
			return success;
417
		}
418

    
419
		List<Taxon> list;
420

    
421
		// Get the limit for objects to save within a single transaction.
422
		int limit = state.getConfig().getLimitSave();
423

    
424
		insertBiota(state);
425

    
426
		logger.info("PHASE 2: Make ParentFk and Biota ... limit is " + limit);
427
		// Start transaction
428
		TransactionStatus txStatus = startTransaction(true);
429
		int partitionCount = 0;
430

    
431
//		ProfilerController.memorySnapshot();
432
		while ((list = getNextTaxonPartition(Taxon.class, limit, partitionCount++, null)) != null   ) {
433

    
434
			if(logger.isDebugEnabled()) {
435
                logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
436
            }
437
			for (Taxon taxon : list) {
438
				for (TaxonNode node : taxon.getTaxonNodes()){
439
					doCount(count++, modCount, pluralString);
440
					TaxonNode parentNode = node.getParent();
441
					if (parentNode != null && parentNode.getTaxon() != null){  //new root node handling requires has root taxon with taxon == null
442
						int childId = state.getDbId( taxon);
443
						int parentId = state.getDbId(parentNode.getTaxon());
444
						success &= invokeParentTaxonFk(parentId, childId);
445
					}
446
				}
447
			}
448

    
449
			// Commit transaction
450
			commitTransaction(txStatus);
451
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 2)");
452
			pastCount = count;
453
			// Start transaction
454
			txStatus = startTransaction(true);
455
			if (logger.isDebugEnabled()){
456
			    logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
457
			}
458
		}
459
		logger.info("No " + pluralString + " left to fetch.");
460

    
461
		// Commit transaction
462
		commitTransaction(txStatus);
463

    
464
		return success;
465
	}
466

    
467
	/**
468
	 * Inserts the Biota Taxon if not yet exists.
469
	 * @param state
470
	 * @throws SQLException
471
	 */
472
	private void insertBiota(PesiExportState state) {
473
		try {
474
			ResultSet rs = state.getConfig().getDestination().getResultSet("SELECT * FROM Taxon WHERE GenusOrUninomial = 'Biota' ");
475
			if (rs.next() == false){
476
				int biotaId = state.getConfig().getNameIdStart() -1 ;
477
				String sqlInsertBiota = "INSERT INTO Taxon (TaxonId, KingdomFk, RankFk, RankCache, GenusOrUninomial, WebSearchName, WebShowName, FullName, DisplayName, TaxonStatusFk, TaxonStatusCache) " +
478
									       " VALUES (" + biotaId + ",    0,    0,   'Superdomain',   'Biota',          'Biota',  '<i>Biota</i>',   'Biota', '<i>Biota</i>',  1 ,      'accepted')";
479
				state.getConfig().getDestination().update(sqlInsertBiota);
480
			}
481
			rs = null;
482
		} catch (SQLException e) {
483
			logger.warn ("Biota could not be requested or inserted");
484
		}
485
	}
486

    
487
	// 4th round: Add TreeIndex to each taxon
488
	private boolean doPhase04(PesiExportState state) {
489
		boolean success = true;
490

    
491
		logger.info("PHASE 4: Make TreeIndex ... ");
492

    
493
		//TODO test if possible to move to phase 02
494
		String sql = " UPDATE Taxon SET ParentTaxonFk = (SELECT TaxonId FROM Taxon WHERE RankFk = 0) " +
495
				" WHERE (RankFk = 10) and TaxonStatusFk = 1 ";
496
		state.getConfig().getDestination().update(sql);
497

    
498
		state.getConfig().getDestination().update("EXEC dbo.recalculateallstoredpaths");
499

    
500
		logger.info("PHASE 4: Make TreeIndex DONE");
501

    
502
		return success;
503

    
504
	}
505

    
506

    
507
//	// 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
508
//	private boolean doPhase02_OLD(PesiExportState state) {
509
//		boolean success = true;
510
//		boolean includeUnpublished = false;
511
//		if (! state.getConfig().isDoTreeIndex()){
512
//			logger.info ("Ignore PHASE 2: ParentTaxonFk and TreeIndex");
513
//			return success;
514
//		}
515
//
516
//		List<Classification> classificationList = null;
517
//		logger.info("PHASE 2: Add ParenTaxonFk and TreeIndex...");
518
//
519
//		// Specify starting ranks for tree traversing
520
//		rankList.add(Rank.KINGDOM());
521
//		rankList.add(Rank.GENUS());
522
//
523
//		// Specify where to stop traversing (value) when starting at a specific Rank (key)
524
//		rank2endRankMap.put(Rank.GENUS(), null); // Since NULL does not match an existing Rank, traverse all the way down to the leaves
525
//		rank2endRankMap.put(Rank.KINGDOM(), Rank.GENUS()); // excludes rank genus
526
//
527
//		StringBuffer treeIndex = new StringBuffer();
528
//
529
//		// Retrieve list of classifications
530
//		TransactionStatus txStatus = startTransaction(true);
531
//		logger.info("Started transaction for parentFk and treeIndex. Fetching all classifications...");
532
//		classificationList = getClassificationService().listClassifications(null, 0, null, null);
533
//		commitTransaction(txStatus);
534
//		logger.debug("Committed transaction.");
535
//
536
//		logger.info("Fetched " + classificationList.size() + " classification(s).");
537
//
538
//		setTreeIndexAnnotationType(getAnnotationType(uuidTreeIndex, "TreeIndex", "TreeIndex", "TI"));
539
//		List<TaxonNode> rankSpecificRootNodes;
540
//		for (Classification classification : classificationList) {
541
//			for (Rank rank : rankList) {
542
//
543
//				txStatus = startTransaction(true);
544
//				logger.info("Started transaction to fetch all rootNodes specific to Rank " + rank.getLabel() + " ...");
545
//
546
//				rankSpecificRootNodes = getClassificationService().listRankSpecificRootNodes(classification,
547
//				        null, rank, includeUnpublished, null, null, null);
548
//				logger.info("Fetched " + rankSpecificRootNodes.size() + " RootNodes for Rank " + rank.getLabel());
549
//
550
//				commitTransaction(txStatus);
551
//				logger.debug("Committed transaction.");
552
//
553
//				for (TaxonNode rootNode : rankSpecificRootNodes) {
554
//					txStatus = startTransaction(false);
555
//					Rank endRank = rank2endRankMap.get(rank);
556
//					if (endRank != null) {
557
//						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till Rank " + endRank.getLabel() + " ...");
558
//					} else {
559
//						logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till leaves are reached ...");
560
//					}
561
//
562
//					TaxonNode newNode = getTaxonNodeService().load(rootNode.getUuid());
563
//
564
//					if (isPesiTaxon(newNode.getTaxon())){
565
//						TaxonNode parentNode = newNode.getParent();
566
//						if (rank.equals(Rank.KINGDOM())) {
567
//							treeIndex = new StringBuffer();
568
//							treeIndex.append("#");
569
//						} else {
570
//							// Get treeIndex from parentNode
571
//							if (parentNode != null) {
572
//								boolean annotationFound = false;
573
//								Set<Annotation> annotations = parentNode.getAnnotations();
574
//								for (Annotation annotation : annotations) {
575
//									AnnotationType annotationType = annotation.getAnnotationType();
576
//									if (annotationType != null && annotationType.equals(getTreeIndexAnnotationType())) {
577
//										treeIndex = new StringBuffer(CdmUtils.Nz(annotation.getText()));
578
//										annotationFound = true;
579
//	//									logger.error("treeIndex: " + treeIndex);
580
//										break;
581
//									}
582
//								}
583
//								if (!annotationFound) {
584
//									// This should not happen because it means that the treeIndex was not set correctly as an annotation to parentNode
585
//									logger.error("TreeIndex could not be read from annotation of TaxonNode: " + parentNode.getUuid() + ", Taxon: " + parentNode.getTaxon().getUuid());
586
//									treeIndex = new StringBuffer();
587
//									treeIndex.append("#");
588
//								}
589
//							} else {
590
//								// TreeIndex could not be determined, but it's unclear how to proceed to generate a correct treeIndex if the parentNode is NULL
591
//								logger.error("ParentNode for RootNode is NULL. TreeIndex could not be determined: " + newNode.getUuid());
592
//								treeIndex = new StringBuffer(); // This just prevents growing of the treeIndex in a wrong manner
593
//								treeIndex.append("#");
594
//							}
595
//						}
596
//						nomenclaturalCode = newNode.getTaxon().getName().getNameType();
597
//						kingdomFk = PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
598
//						traverseTree(newNode, parentNode, treeIndex, endRank, state);
599
//						parentNode =null;
600
//					}else{
601
//						logger.debug("Taxon is not a PESI taxon: " + newNode.getTaxon().getUuid());
602
//					}
603
//
604
//					newNode = null;
605
//
606
//					try {
607
//						commitTransaction(txStatus);
608
//						logger.debug("Committed transaction.");
609
//					} catch (Exception e) {
610
//						logger.error(e.getMessage());
611
//						e.printStackTrace();
612
//					}
613
//
614
//				}
615
//				rankSpecificRootNodes = null;
616
//			}
617
//
618
//		}
619
//
620
//		logger.warn("Taking snapshot at the end of phase 2 of taxonExport");
621
//		//ProfilerController.memorySnapshot();
622
//		return success;
623
//	}
624

    
625
	//PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...
626
	private boolean doPhase03(PesiExportState state) {
627
		int count = 0;
628
		int pastCount = 0;
629
		boolean success = true;
630
		if (! state.getConfig().isDoTreeIndex()){
631
			logger.info ("Ignore PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
632
			return success;
633
		}
634
		// Get the limit for objects to save within a single transaction.
635
		int limit = state.getConfig().getLimitSave();
636

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

    
641
		// Start transaction
642
		TransactionStatus txStatus = startTransaction(true);
643
		if (logger.isDebugEnabled()) {
644
            logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
645
        }
646
		int partitionCount = 0;
647
		while ((list = getNextTaxonPartition(TaxonBase.class, limit, partitionCount++, null)) != null) {
648

    
649
			if (logger.isDebugEnabled()) {
650
                logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
651
            }
652
			for (TaxonBase<?> taxon : list) {
653
				TaxonName taxonName = CdmBase.deproxy(taxon.getName());
654
				// Determine expertFk
655
//				Integer expertFk = makeExpertFk(state, taxonName);
656
//
657
//				// Determine speciesExpertFk
658
//				Integer speciesExpertFk = makeSpeciesExpertFk(state, taxonName);
659

    
660
				doCount(count++, modCount, pluralString);
661
				Integer typeNameFk = getTypeNameFk(taxonName, state);
662
				Integer kingdomFk = findKingdomIdFromTreeIndex(taxon, state);
663
				 //       PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
664

    
665
				//TODO why are expertFks needed? (Andreas M.)
666
//				if (expertFk != null || speciesExpertFk != null) {
667
				    NomenclaturalCode nomCode = taxonName.getNameType();
668
				    //is there a reason why we do pass nomCode separately? Before nomCode was class variable, but not clear why and when it was set
669
					invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, nomCode, state.getDbId(taxon),
670
							typeNameFk, kingdomFk, state);
671
//				}
672
			}
673

    
674
			// Commit transaction
675
			commitTransaction(txStatus);
676
			if (logger.isDebugEnabled()){logger.debug("Committed transaction.");}
677
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count + " (Phase 3)");
678
			pastCount = count;
679

    
680
			// Start transaction
681
			txStatus = startTransaction(true);
682
			if (logger.isDebugEnabled()) {
683
                logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
684
            }
685
		}
686
		logger.info("No " + pluralString + " left to fetch.");
687

    
688
		// Commit transaction
689
		commitTransaction(txStatus);
690

    
691
		if (logger.isDebugEnabled()){
692
		    logger.debug("Committed transaction.");
693
		    logger.debug("Try to take snapshot at the end of phase 3 of taxonExport, number of partitions: " + partitionCount);
694
		    //ProfilerController.memorySnapshot();
695
		}
696
		return success;
697
	}
698

    
699
    private static Integer findKingdomIdFromTreeIndex(TaxonBase<?> taxonBase,PesiExportState state) {
700
        Taxon taxon;
701
        if (taxonBase instanceof Synonym){
702
            taxon = ((Synonym) taxonBase).getAcceptedTaxon();
703
        }else{
704
            taxon = (Taxon)taxonBase;
705
        }
706
        if (taxon != null){
707
            Set<TaxonNode> nodes = taxon.getTaxonNodes();
708
            if (nodes.size()>1){
709
                logger.warn("The taxon has more then 1 taxon node: " + taxon.getTitleCache() + ". Take arbitrary one.");
710
            }
711
            if (!nodes.isEmpty()){
712
                String treeIndex = nodes.iterator().next().treeIndex();
713

    
714
                Pattern pattern = Pattern.compile("#t[0-9]+#([0-9]+#){3}");
715
                Matcher matcher = pattern.matcher(treeIndex);
716
                Integer kingdomID = null;
717
                if(matcher.find()) {
718
                    String treeIndexKingdom = matcher.group(0);
719
                    kingdomID = state.getTreeIndexKingdomMap().get(treeIndexKingdom);
720
                }else{
721
                    pattern = Pattern.compile("#t[0-9]+#([0-9]+#){2}");
722
                    matcher = pattern.matcher(treeIndex);
723
                    if(matcher.find()) {
724
                        String treeIndexKingdom = matcher.group(0);
725
                        kingdomID = state.getTreeIndexKingdomMap().get(treeIndexKingdom);
726
                    }
727
                }
728
                if(Rank.DOMAIN().equals(taxon.getName().getRank())){
729
                    return null;
730
                }
731
                if(kingdomID == null){
732
                    logger.warn("Kingdom could not be defined for treeindex " + treeIndex);
733
                }
734
                return kingdomID;
735
            } else {
736
                NomenclaturalCode nomenclaturalCode = taxon.getName().getNameType();
737
                logger.warn("The taxon has no nodes: " + taxon.getTitleCache() + ". The kingdom is taken from the nomenclatural code: " + PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
738
                return PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
739
            }
740
        } else{
741
            NomenclaturalCode nomenclaturalCode = taxonBase.getName().getNameType();
742
            logger.warn("Taxon is synonym with no accepted taxon attached: " + taxonBase.getTitleCache() + ". The kingdom is taken from the nomenclatural code: " + PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode) );
743
            return PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode);
744
        }
745
    }
746

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

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

    
762
		// Determine the count of elements in data warehouse database table Taxon
763
		currentTaxonId = determineTaxonCount(state);
764
		currentTaxonId++;
765

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

    
772
		// Start transaction
773
		TransactionStatus txStatus = startTransaction(true);
774
		if (logger.isDebugEnabled()) {
775
            logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
776
        }
777
		List<TaxonBase> taxonList = null;
778

    
779
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SPECIES(), pageSize, pageNumber)).size() > 0) {
780

    
781
		    Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
782

    
783
			if (logger.isDebugEnabled()) {
784
                logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
785
            }
786
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
787
					synRelMapping, taxonList));
788

    
789
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
790
			// Commit transaction
791
			commitTransaction(txStatus);
792
			if (logger.isDebugEnabled()){logger.debug("Committed transaction.");}
793
			logger.info("Exported " + (taxonList.size()) + " " + inferredSynonymPluralString + ". Total: " + count);
794
			//pastCount = count;
795

    
796
			// Save Rank Data and KingdomFk for inferred synonyms
797
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
798
			    TaxonName taxonName = inferredSynonymsDataToBeSaved.get(taxonFk);
799
                NomenclaturalCode nomCode = taxonName.getNameType(); //nomCode was class variable before, not sure if this was important
800
                invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomCode, taxonFk, kingdomFk, state);
801
			}
802

    
803
			// Start transaction
804
			txStatus = startTransaction(true);
805
			if (logger.isDebugEnabled()) {
806
                logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
807
            }
808

    
809
			// Increment pageNumber
810
			pageNumber++;
811
		}
812
		taxonList = null;
813
		while ((taxonList  = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", "*", Rank.SUBSPECIES(), pageSize, pageNumber)).size() > 0) {
814
			Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
815

    
816
			logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
817
			inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
818
					synRelMapping, taxonList));
819

    
820
			doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
821
			// Commit transaction
822
			commitTransaction(txStatus);
823
			logger.debug("Committed transaction.");
824
			logger.info("Exported " + taxonList.size()+ " " + inferredSynonymPluralString + ". Total: " + count);
825
			//pastCount = count;
826

    
827
			// Save Rank Data and KingdomFk for inferred synonyms
828
			for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
829
			    TaxonName taxonName = inferredSynonymsDataToBeSaved.get(taxonFk);
830
			    NomenclaturalCode nomCode = taxonName.getNameType(); //nomCode was class variable before, not sure if this was important
831
				invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomCode, taxonFk, kingdomFk, state);
832
			}
833

    
834
			// Start transaction
835
			txStatus = startTransaction(true);
836
			logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
837

    
838
			// Increment pageNumber
839
			pageNumber++;
840
			inferredSynonymsDataToBeSaved = null;
841
		}
842
		if (taxonList.size() == 0) {
843
			logger.info("No " + parentPluralString + " left to fetch.");
844
		}
845

    
846
		taxonList = null;
847
//		logger.warn("Taking snapshot at the end of phase 5 of taxonExport");
848
//		ProfilerController.memorySnapshot();
849

    
850
		// Commit transaction
851
		commitTransaction(txStatus);
852
		System.gc();
853
		logger.debug("Taking snapshot at the end of phase 5 after gc() of taxonExport");
854
		//ProfilerController.memorySnapshot();
855
		logger.debug("Committed transaction.");
856
		return success;
857
	}
858

    
859
	/**
860
	 * @param state
861
	 * @param mapping
862
	 * @param synRelMapping
863
	 * @param currentTaxonId
864
	 * @param taxonList
865
	 * @param inferredSynonymsDataToBeSaved
866
	 * @return
867
	 */
868
	private Map<Integer, TaxonName> createInferredSynonymsForTaxonList(PesiExportState state,
869
			PesiExportMapping mapping, PesiExportMapping synRelMapping,	 List<TaxonBase> taxonList) {
870

    
871
		Taxon acceptedTaxon;
872
		Classification classification = null;
873
		List<Synonym> inferredSynonyms = null;
874
		boolean localSuccess = true;
875

    
876
		Map<Integer, TaxonName> inferredSynonymsDataToBeSaved = new HashMap<>();
877

    
878
		for (TaxonBase<?> taxonBase : taxonList) {
879

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

    
884
				if (taxonName.isZoological()) {
885
					kingdomFk = findKingdomIdFromTreeIndex(taxonBase, state);
886

    
887
					Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
888
					TaxonNode singleNode = null;
889

    
890
					if (taxonNodes.size() > 0) {
891
						// Determine the classification of the current TaxonNode
892

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

    
907
					if (classification != null) {
908
						try{
909
						    TaxonName name = acceptedTaxon.getName();
910
							//if (name.isSpecies() || name.isInfraSpecific()){
911
								inferredSynonyms  = getTaxonService().createAllInferredSynonyms(acceptedTaxon, classification, true);
912
							//}
913
//								inferredSynonyms = getTaxonService().createInferredSynonyms(classification, acceptedTaxon, SynonymType.INFERRED_GENUS_OF());
914
							if (inferredSynonyms != null) {
915
								for (Synonym synonym : inferredSynonyms) {
916
//									TaxonName synonymName = synonym.getName();
917
									MarkerType markerType =getUuidMarkerType(PesiTransformer.uuidMarkerGuidIsMissing, state);
918
									synonym.addMarker(Marker.NewInstance(markerType, true));
919
									// Both Synonym and its TaxonName have no valid Id yet
920
									synonym.setId(currentTaxonId++);
921

    
922

    
923
									localSuccess &= mapping.invoke(synonym);
924
									//get SynonymRelationship and export
925
									if (synonym.getAcceptedTaxon() == null ){
926
										IdentifiableSource source = synonym.getSources().iterator().next();
927
										if (source.getIdNamespace().contains("Potential combination")){
928
											acceptedTaxon.addSynonym(synonym, SynonymType.POTENTIAL_COMBINATION_OF());
929
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to potential combination");
930
										} else if (source.getIdNamespace().contains("Inferred Genus")){
931
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_GENUS_OF());
932
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred genus");
933
										} else if (source.getIdNamespace().contains("Inferred Epithet")){
934
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_EPITHET_OF());
935
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred epithet");
936
										} else{
937
											acceptedTaxon.addSynonym(synonym, SynonymType.INFERRED_SYNONYM_OF());
938
											logger.error(synonym.getTitleCache() + " is not attached to " + acceptedTaxon.getTitleCache() + " type is set to inferred synonym");
939
										}
940

    
941
										localSuccess &= synRelMapping.invoke(synonym);
942
										if (!localSuccess) {
943
											logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
944
										}
945
									} else {
946
										localSuccess &= synRelMapping.invoke(synonym);
947
										if (!localSuccess) {
948
											logger.error("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
949
										} else {
950
											logger.info("Synonym relationship successfully exported: " + synonym.getTitleCache() + "  " +acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
951
										}
952
									}
953

    
954
									inferredSynonymsDataToBeSaved.put(synonym.getId(), synonym.getName());
955
								}
956
							}
957
						}catch(Exception e){
958
							logger.error(e.getMessage());
959
							e.printStackTrace();
960
						}
961
					} else {
962
						logger.error("Classification is NULL. Inferred Synonyms could not be created for this Taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() + ")");
963
					}
964
				} else {
965
//							logger.error("TaxonName is not a ZoologicalName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
966
				}
967
			} else {
968
				logger.error("This TaxonBase is not a Taxon even though it should be: " + taxonBase.getUuid() + " (" + taxonBase.getTitleCache() + ")");
969
			}
970
		}
971
		return inferredSynonymsDataToBeSaved;
972
	}
973

    
974

    
975
	/**
976
	 * Handles names that do not appear in taxa
977
	 * @param state
978
	 * @param mapping
979
	 */
980
	private boolean doNames(PesiExportState state, PesiExportMapping additionalSourceMapping)  throws SQLException {
981

    
982
		boolean success = true;
983
		if (! state.getConfig().isDoPureNames()){
984
			logger.info ("Ignore PHASE 1b: PureNames");
985
			return success;
986
		}
987

    
988
		try {
989
			PesiExportMapping mapping = getPureNameMapping(state);
990
			mapping.initialize(state);
991
			int count = 0;
992
			int pastCount = 0;
993
			List<TaxonName> list;
994
			success = true;
995
			// Get the limit for objects to save within a single transaction.
996
			int limit = state.getConfig().getLimitSave();
997

    
998
			logger.info("PHASE 1b: Export Pure Names ...");
999
			// Start transaction
1000
			TransactionStatus txStatus = startTransaction(true);
1001
			logger.info("Started new transaction for Pure Names. Fetching some " + pluralString + " (max: " + limit + ") ...");
1002

    
1003
			int partitionCount = 0;
1004
			while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null   ) {
1005

    
1006
				logger.info("Fetched " + list.size() + " names without taxa. Exporting...");
1007
				for (TaxonName taxonName : list) {
1008
					doCount(count++, modCount, pluralString);
1009
					success &= mapping.invoke(taxonName);
1010
					//additional source
1011
					if (taxonName.getNomenclaturalReference() != null || StringUtils.isNotBlank(taxonName.getNomenclaturalMicroReference() )){
1012
						additionalSourceMapping.invoke(taxonName);
1013
					}
1014
				}
1015

    
1016
				// Commit transaction
1017
				commitTransaction(txStatus);
1018
				logger.debug("Committed transaction.");
1019
				logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
1020
				pastCount = count;
1021

    
1022
				// Start transaction
1023
				txStatus = startTransaction(true);
1024
				logger.info("Started new transaction for PureNames. Fetching some " + pluralString + " (max: " + limit + ") ...");
1025
			}
1026
			logger.info("No " + pluralString + " left to fetch.");
1027

    
1028
			// Commit transaction
1029
			commitTransaction(txStatus);
1030
			logger.debug("Committed transaction.");
1031
		} catch (Exception e) {
1032
			logger.error("Error occurred in pure name export");
1033
			e.printStackTrace();
1034
			success = false;
1035
		}
1036
		return success;
1037
	}
1038

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

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

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

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

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

    
1150
							localTreeIndex.append(taxonId + "#");
1151

    
1152
							saveData(childNode, parentNode, localTreeIndex, state, taxonId);
1153

    
1154
							// Store treeIndex as annotation for further use
1155
							Annotation annotation = Annotation.NewInstance(localTreeIndex.toString(), getTreeIndexAnnotationType(), Language.DEFAULT());
1156
							childNode.addAnnotation(annotation);
1157

    
1158
							for (TaxonNode newNode : childNode.getChildNodes()) {
1159
								if (newNode.getTaxon() != null && isPesiTaxon(newNode.getTaxon())){
1160
									traverseTree(newNode, childNode, localTreeIndex, fetchLevel, state);
1161
								}
1162
							}
1163

    
1164
						} else {
1165
	//						logger.debug("Target Rank " + fetchLevel.getLabel() + " reached");
1166
							return;
1167
						}
1168
					} else {
1169
						logger.error("Rank is NULL. FetchLevel can not be checked: " + childName.getUuid() + " (" + childName.getTitleCache() + ")");
1170
					}
1171
				} else {
1172
					logger.error("Taxon can not be found in state: " + childTaxon.getUuid() + " (" + childTaxon.getTitleCache() + ")");
1173
				}
1174
			}else{
1175
				if (logger.isDebugEnabled()){
1176
					logger.debug("Taxon is not a PESI taxon: " + childTaxon.getUuid());
1177
				}
1178
			}
1179
		} else {
1180
			logger.error("Taxon is NULL for TaxonNode: " + childNode.getUuid());
1181
		}
1182
	}
1183

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

    
1202
			invokeParentTaxonFkAndTreeIndex(state.getDbId(parentTaxon), currentTaxonFk,	treeIndex);
1203
		}
1204
	}
1205

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

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

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

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

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

    
1257

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

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

    
1283
			if (kingdomFk != null) {
1284

    
1285
				rankUpdateStmt.setInt(3, kingdomFk);
1286
			} else {
1287
				rankUpdateStmt.setObject(3, null);
1288
			}
1289

    
1290
			if (taxonFk != null) {
1291
				rankUpdateStmt.setInt(4, taxonFk);
1292
			} else {
1293
				rankUpdateStmt.setObject(4, null);
1294
			}
1295

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

    
1305
	/**
1306
	 * Inserts Rank data, TypeNameFk, KingdomFk, expertFk and speciesExpertFk into the Taxon database table.
1307
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1308
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1309
	 * @param taxonFk The TaxonFk to store the values for.
1310
	 * @param typeNameFk The TypeNameFk.
1311
	 * @param state
1312
	 * @param kindomFk The KingdomFk.
1313
	 * @param expertFk The ExpertFk.
1314
	 * @param speciesExpertFk The SpeciesExpertFk.
1315
	 * @return Whether save was successful or not.
1316
	 */
1317
	private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonName taxonName, NomenclaturalCode nomenclaturalCode,
1318
			Integer taxonFk, Integer typeNameFk, Integer kingdomFk, PesiExportState state) {
1319

    
1320
	    Integer rankFk = null;
1321
	    try {
1322
			int index = 1;
1323
			rankFk = getRankFk(taxonName, nomenclaturalCode);
1324
			if (rankFk != null) {
1325
				rankTypeExpertsUpdateStmt.setInt(index++, rankFk);
1326
			} else {
1327
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1328
			}
1329

    
1330
			String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
1331
			if (rankCache != null) {
1332
				rankTypeExpertsUpdateStmt.setString(index++, rankCache);
1333
			} else {
1334
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1335
			}
1336

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

    
1343
			if (kingdomFk != null) {
1344
				rankTypeExpertsUpdateStmt.setInt(index++, kingdomFk);
1345
			} else {
1346
				rankTypeExpertsUpdateStmt.setObject(index++, null);
1347
			}
1348

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

    
1368
			rankTypeExpertsUpdateStmt.executeUpdate();
1369
			return true;
1370
		} catch (SQLException e) {
1371
			logger.error("Data could not be inserted into database: " + e.getMessage() + "; rankFk = " + rankFk + "; kingdomFk = " + kingdomFk );
1372
			e.printStackTrace();
1373
			return false;
1374
		} catch (Exception e) {
1375
			logger.error("Some exception occurred: " + e.getMessage() + "; rankFk = " + rankFk + "; kingdomFk = " + kingdomFk);
1376
			e.printStackTrace();
1377
			return false;
1378
		}
1379
	}
1380

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

    
1389
		String sql;
1390
		Source destination =  pesiConfig.getDestination();
1391

    
1392
		// Clear Taxon
1393
		sql = "DELETE FROM " + dbTableName;
1394
		destination.update(sql);
1395
		return true;
1396
	}
1397

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

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

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

    
1445

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

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

    
1483
	/**
1484
	 * Returns the <code>RankCache</code> attribute.
1485
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1486
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1487
	 * @param state
1488
	 * @return The <code>RankCache</code> attribute.
1489
	 * @see MethodMapper
1490
	 */
1491
	private static String getRankCache(TaxonName taxonName, NomenclaturalCode nomenclaturalCode, PesiExportState state) {
1492
	    List<TaxonNode> nodes = getTaxonNodes(taxonName);
1493
	    if (Rank.DOMAIN().equals(taxonName.getRank())){
1494
            return state.getTransformer().getCacheByRankAndKingdom(Rank.DOMAIN(), null);
1495
        }else if (!nodes.isEmpty()) {
1496
            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), findKingdomIdFromTreeIndex(nodes.iterator().next().getTaxon(), state)); //PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
1497
        }else if (nomenclaturalCode != null){
1498
            return state.getTransformer().getCacheByRankAndKingdom(taxonName.getRank(), PesiTransformer.nomenclaturalCode2Kingdom(nomenclaturalCode));
1499
        }else{
1500
			logger.warn("No nomenclatural code defined for name " + taxonName.getUuid());
1501
			return null;
1502
		}
1503
	}
1504

    
1505
	/**
1506
     * @param taxonName
1507
     * @return
1508
     */
1509
    private static List<TaxonNode> getTaxonNodes(TaxonName taxonName) {
1510
        List<TaxonNode> result = new ArrayList<>();
1511
        for (TaxonBase<?> tb:taxonName.getTaxonBases()){
1512
            Taxon taxon;
1513
            //TODO handle ERMS taxon relationships
1514
            if (tb.isInstanceOf(Taxon.class)){
1515
                taxon = CdmBase.deproxy(tb, Taxon.class);
1516
            }else{
1517
                taxon = CdmBase.deproxy(tb, Synonym.class).getAcceptedTaxon();
1518
            }
1519
            if (taxon != null){
1520
                for (TaxonNode node : taxon.getTaxonNodes()){
1521
                    result.add(node);
1522
                }
1523
            }
1524
        }
1525
        return result;
1526
    }
1527

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

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

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

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

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

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

    
1617
			String result;
1618
			if (getSources(taxonName).get(PesiTransformer.SOURCE_ERMS)){
1619
			    result = cacheStrategy.getTitleCache(taxonName, tagRules);  //according to SQL script (also in ERMS sources are not abbreviated)
1620
			}else if (getSources(taxonName).get(PesiTransformer.SOURCE_EM)){
1621
			    result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
1622
			}else{
1623
			    //TODO define for FE + IF and for multiple sources
1624
			    result = cacheStrategy.getFullTitleCache(taxonName, tagRules);
1625
			}
1626
			return result.replaceAll(",?\\<@status@\\>.*\\</@status@\\>", "");
1627
		}
1628
	}
1629

    
1630
	@SuppressWarnings("unused")
1631
	private static String getGUID(TaxonName taxonName) {
1632
		UUID uuid = taxonName.getUuid();
1633
		String result = "NameUUID:" + uuid.toString();
1634
		return result;
1635
	}
1636

    
1637

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

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

    
1667
			HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1668
			String result = cacheStrategy.getTitleCache(taxonName, tagRules);
1669
			return result;
1670
		}
1671
	}
1672

    
1673

    
1674
	/**
1675
	 * Returns the <code>WebSearchName</code> attribute.
1676
	 * @param taxonName The {@link NonViralName NonViralName}.
1677
	 * @return The <code>WebSearchName</code> attribute.
1678
	 * @see MethodMapper
1679
	 */
1680
	@SuppressWarnings("unused")
1681
	private static String getWebSearchName(TaxonName taxonName) {
1682
		//TODO extensions?
1683
	    TaxonNameDefaultCacheStrategy strategy = getCacheStrategy(taxonName);
1684
		String result = strategy.getNameCache(taxonName);
1685
		return result;
1686
	}
1687

    
1688

    
1689
	/**
1690
	 * Returns the <code>FullName</code> attribute.
1691
	 * @param taxonName The {@link NonViralName NonViralName}.
1692
	 * @return The <code>FullName</code> attribute.
1693
	 * @see MethodMapper
1694
	 */
1695
	@SuppressWarnings("unused")
1696
	private static String getFullName(TaxonName taxonName) {
1697
		//TODO extensions?
1698
		String result = getCacheStrategy(taxonName).getTitleCache(taxonName);
1699
		Iterator<Taxon> taxa = taxonName.getTaxa().iterator();
1700
		if (taxonName.getTaxa().size() >0){
1701
			if (taxonName.getTaxa().size() == 1){
1702
				Taxon taxon = taxa.next();
1703
				if (isMisappliedName(taxon)){
1704
					result = result + " " + getAuthorString(taxon);
1705
				}
1706
				taxon = null;
1707
			}
1708
		}
1709
		return result;
1710
	}
1711

    
1712
	/**
1713
	 * Returns the SourceNameCache for the AdditionalSource table
1714
	 * @param taxonName
1715
	 * @return
1716
	 */
1717
	static boolean isFirstAbbrevTitle = true;
1718
	@SuppressWarnings("unused")
1719
	private static String getSourceNameCache(TaxonName taxonName) {
1720
		if (taxonName != null){
1721
			Reference nomRef = taxonName.getNomenclaturalReference();
1722
			if (nomRef != null ){
1723
			    if (isFirstAbbrevTitle){
1724
			        //#5388 is definetely not the correct ticket number
1725
			        logger.warn("Semantics of getAbbrevTitleCache has changed. Please check if output is still correct. See #5388");
1726
			        isFirstAbbrevTitle = false;
1727
			    }
1728
			    return nomRef.getAbbrevTitleCache();
1729
			}
1730
		}
1731
		return null;
1732
	}
1733

    
1734

    
1735

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

    
1751
		return result;
1752
	}
1753
*/
1754

    
1755
	/**
1756
	 * Returns the nomenclatural reference which is the reference
1757
	 * including the detail (microreference).
1758
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1759
	 * @return The <code>AuthorString</code> attribute.
1760
	 * @see MethodMapper
1761
	 */
1762
	@SuppressWarnings("unused")
1763
	private static String getNomRefString(TaxonName taxonName) {
1764
		INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1765
		if (ref == null){
1766
			return null;
1767
		}
1768
		String result = null;
1769
		BitSet sources = getSources(taxonName);
1770
		int len = sources.length();
1771
		if(sources.get(PesiTransformer.SOURCE_EM)){
1772
		    if (! ref.isProtectedAbbrevTitleCache()){
1773
		        ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1774
		    }
1775
		    result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1776
		}else if(sources.get(PesiTransformer.SOURCE_FE)||sources.get(PesiTransformer.SOURCE_IF) ){
1777
            //TODO still need to check if correct for FE + IF
1778
		    if (! ref.isProtectedAbbrevTitleCache()){
1779
                ref.setAbbrevTitleCache(null, false);  //to remove a false cache
1780
            }
1781
            result = ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1782
            return result;   // according to SQL script
1783
		}else if(sources.get(PesiTransformer.SOURCE_ERMS)) {
1784
            //result = null; //according to SQL script
1785
		}else{
1786
		    logger.warn("Source not yet supported");
1787
		}
1788
		return result;
1789
	}
1790

    
1791

    
1792
	/**
1793
	 * Returns the <code>NameStatusFk</code> attribute.
1794
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1795
	 * @return The <code>NameStatusFk</code> attribute.
1796
	 * @see MethodMapper
1797
	 */
1798
	@SuppressWarnings("unused")
1799
	private static Integer getNameStatusFk(TaxonName taxonName) {
1800
		Integer result = null;
1801

    
1802
		NomenclaturalStatus state = getNameStatus(taxonName);
1803
		if (state != null) {
1804
			result = PesiTransformer.nomStatus2nomStatusFk(state.getType());
1805
		}
1806
		return result;
1807
	}
1808

    
1809
	/**
1810
	 * Returns the <code>NameStatusCache</code> attribute.
1811
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1812
	 * @return The <code>NameStatusCache</code> attribute.
1813
	 * @throws UndefinedTransformerMethodException
1814
	 * @see MethodMapper
1815
	 */
1816
	@SuppressWarnings("unused")
1817
	private static String getNameStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1818
		String result = null;
1819
		NomenclaturalStatus status = getNameStatus(taxonName);
1820
		if (status != null) {
1821
			result = state.getTransformer().getCacheByNomStatus(status.getType());
1822
		}
1823
		return result;
1824
	}
1825

    
1826

    
1827
	private static NomenclaturalStatus getNameStatus(TaxonName taxonName) {
1828
		try {
1829
			if (taxonName != null) {
1830
			    Set<NomenclaturalStatus> states = taxonName.getStatus();
1831
				if (states.size() == 1) {
1832
					NomenclaturalStatus status = states.iterator().next();
1833
					return status;
1834
				} else if (states.size() > 1) {
1835
					logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1836
				}
1837
			}
1838
		} catch (Exception e) {
1839
			e.printStackTrace();
1840
		}
1841
		return null;
1842
	}
1843
	/**
1844
	 * Returns the <code>TaxonStatusFk</code> attribute.
1845
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1846
	 * @param state The {@link PesiExportState PesiExportState}.
1847
	 * @return The <code>TaxonStatusFk</code> attribute.
1848
	 * @see MethodMapper
1849
	 */
1850
	private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1851
		Integer result = null;
1852

    
1853
		try {
1854
			if (isMisappliedName(taxon)) {
1855
				Synonym synonym = Synonym.NewInstance(null, null);
1856

    
1857
				// This works as long as only the instance is important to differentiate between TaxonStatus.
1858
				result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1859
			} else {
1860
				result = PesiTransformer.taxonBase2statusFk(taxon);
1861
			}
1862
		} catch (Exception e) {
1863
			e.printStackTrace();
1864
		}
1865
		return result;
1866
	}
1867

    
1868
	/**
1869
	 * Returns the <code>TaxonStatusCache</code> attribute.
1870
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1871
	 * @param state The {@link PesiExportState PesiExportState}.
1872
	 * @return The <code>TaxonStatusCache</code> attribute.
1873
	 * @throws UndefinedTransformerMethodException
1874
	 * @see MethodMapper
1875
	 */
1876
	@SuppressWarnings("unused")
1877
	private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1878
		return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1879
	}
1880

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

    
1907
	/**
1908
	 * Returns the <code>TypeFullnameCache</code> attribute.
1909
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1910
	 * @return The <code>TypeFullnameCache</code> attribute.
1911
	 * @see MethodMapper
1912
	 */
1913
	@SuppressWarnings("unused")
1914
	private static String getTypeFullnameCache(TaxonName taxonName) {
1915
		String result = null;
1916

    
1917
		try {
1918
    		if (taxonName != null) {
1919
    			Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1920
    			if (nameTypeDesignations.size() == 1) {
1921
    				NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1922
    				if (nameTypeDesignation != null) {
1923
    					TaxonName typeName = nameTypeDesignation.getTypeName();
1924
    					if (typeName != null) {
1925
    						result = typeName.getTitleCache();
1926
    					}
1927
    				}
1928
    			} else if (nameTypeDesignations.size() > 1) {
1929
    				logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1930
    			}
1931
    		}
1932
		} catch (Exception e) {
1933
			e.printStackTrace();
1934
		}
1935
		return result;
1936
	}
1937

    
1938

    
1939
	/**
1940
	 * Returns the <code>QualityStatusFk</code> attribute.
1941
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1942
	 * @return The <code>QualityStatusFk</code> attribute.
1943
	 * @see MethodMapper
1944
	 */
1945
	private static Integer getQualityStatusFk(TaxonName taxonName) {
1946
		BitSet sources = getSources(taxonName);
1947
		return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1948
	}
1949

    
1950

    
1951
	/**
1952
	 * Returns the <code>QualityStatusCache</code> attribute.
1953
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1954
	 * @return The <code>QualityStatusCache</code> attribute.
1955
	 * @throws UndefinedTransformerMethodException
1956
	 * @see MethodMapper
1957
	 */
1958
	@SuppressWarnings("unused")
1959
	private static String getQualityStatusCache(TaxonName taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1960
		return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1961
	}
1962

    
1963

    
1964
	/**
1965
	 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1966
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1967
	 * @return The <code>TypeDesignationStatusFk</code> attribute.
1968
	 * @see MethodMapper
1969
	 */
1970
	@SuppressWarnings("unused")
1971
	private static Integer getTypeDesignationStatusFk(TaxonName taxonName) {
1972
		Integer result = null;
1973

    
1974
		try {
1975
		if (taxonName != null) {
1976
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1977
			if (typeDesignations.size() == 1) {
1978
				Object obj = typeDesignations.iterator().next().getTypeStatus();
1979
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1980
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1981
			} else if (typeDesignations.size() > 1) {
1982
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1983
			}
1984
		}
1985

    
1986
		} catch (Exception e) {
1987
			e.printStackTrace();
1988
		}
1989
		return result;
1990
	}
1991

    
1992
	/**
1993
	 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1994
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1995
	 * @return The <code>TypeDesignationStatusCache</code> attribute.
1996
	 * @see MethodMapper
1997
	 */
1998
	@SuppressWarnings("unused")
1999
	private static String getTypeDesignationStatusCache(TaxonName taxonName) {
2000
		String result = null;
2001

    
2002
		try {
2003
		if (taxonName != null) {
2004
			Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
2005
			if (typeDesignations.size() == 1) {
2006
				Object obj = typeDesignations.iterator().next().getTypeStatus();
2007
				NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
2008
				result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
2009
			} else if (typeDesignations.size() > 1) {
2010
				logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2011
			}
2012
		}
2013

    
2014
		} catch (Exception e) {
2015
			e.printStackTrace();
2016
		}
2017
		return result;
2018
	}
2019

    
2020
	/**
2021
	 * Returns the <code>FossilStatusFk</code> attribute.
2022
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2023
	 * @return The <code>FossilStatusFk</code> attribute.
2024
	 * @see MethodMapper
2025
	 */
2026
	@SuppressWarnings("unused")
2027
	private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
2028
		Integer result = null;
2029

    
2030
		Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidExtFossilStatus);
2031
		if (fossilStatuus.size() == 0){
2032
			return null;
2033
		}else if (fossilStatuus.size() > 1){
2034
			logger.warn("More than 1 fossil status given for " +  identEntity.getTitleCache() + " " + identEntity.getUuid());
2035
		}
2036
		String fossilStatus = fossilStatuus.iterator().next();
2037

    
2038
		int statusFk = state.getTransformer().fossilStatusCache2FossilStatusFk(fossilStatus);
2039
		return statusFk;
2040
	}
2041

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

    
2061
	/**
2062
	 * Returns the <code>IdInSource</code> attribute.
2063
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2064
	 * @return The <code>IdInSource</code> attribute.
2065
	 * @see MethodMapper
2066
	 */
2067
	@SuppressWarnings("unused")
2068
	private static String getIdInSource(IdentifiableEntity taxonName) {
2069
		String result = null;
2070

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

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

    
2116
		if (result == null) {
2117
			logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2118
		}
2119
		return result;
2120
	}
2121

    
2122
	/**
2123
	 * Returns the idInSource for a given TaxonName only.
2124
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2125
	 * @return The idInSource.
2126
	 */
2127
	private static String getIdInSourceOnly(IdentifiableEntity identEntity) {
2128
		String result = null;
2129

    
2130
		// Get the sources first
2131
		Set<IdentifiableSource> sources = getPesiSources(identEntity);
2132

    
2133
		// Determine the idInSource
2134
		if (sources.size() == 1) {
2135
			IdentifiableSource source = sources.iterator().next();
2136
			if (source != null) {
2137
				result = source.getIdInSource();
2138
			}
2139
		} else if (sources.size() > 1) {
2140
			int count = 1;
2141
			result = "";
2142
			for (IdentifiableSource source : sources) {
2143
				result += source.getIdInSource();
2144
				if (count < sources.size()) {
2145
					result += "; ";
2146
				}
2147
				count++;
2148
			}
2149

    
2150
		}
2151

    
2152
		return result;
2153
	}
2154

    
2155
	/**
2156
	 * Returns the <code>GUID</code> attribute.
2157
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2158
	 * @return The <code>GUID</code> attribute.
2159
	 * @see MethodMapper
2160
	 */
2161
	private static String getGUID(TaxonBase<?> taxon) {
2162
		if (taxon.getLsid() != null ){
2163
			return taxon.getLsid().getLsid();
2164
		}else if (taxon.hasMarker(PesiTransformer.uuidMarkerGuidIsMissing, true)){
2165
			return null;
2166
		}else{
2167
			return taxon.getUuid().toString();
2168
		}
2169
	}
2170

    
2171
	/**
2172
	 * Returns the <code>DerivedFromGuid</code> attribute.
2173
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2174
	 * @return The <code>DerivedFromGuid</code> attribute.
2175
	 * @see MethodMapper
2176
	 */
2177
	@SuppressWarnings("unused")
2178
	private static String getDerivedFromGuid(TaxonBase<?> taxon) {
2179
		String result = null;
2180
		try {
2181
		// The same as GUID for now
2182
		result = getGUID(taxon);
2183
		} catch (Exception e) {
2184
			e.printStackTrace();
2185
		}
2186
		return result;
2187
	}
2188

    
2189
	/**
2190
	 * Returns the <code>CacheCitation</code> attribute.
2191
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2192
	 * @return The CacheCitation.
2193
	 * @see MethodMapper
2194
	 */
2195
	@SuppressWarnings("unused")
2196
	private static String getCacheCitation(TaxonBase<?> taxon) {
2197
		// !!! See also doPhaseUpdates
2198

    
2199
		TaxonName taxonName = taxon.getName();
2200
		String result = "";
2201
		//TODO implement anew for taxa
2202
		try {
2203
			BitSet sources = getSources(taxon);
2204
			if (sources.isEmpty()) {
2205
//				logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2206
			} else if (sources.get(PesiTransformer.SOURCE_ERMS)) {
2207
				Set<Extension> extensions = taxon.getExtensions();
2208
				for (Extension extension : extensions) {
2209
					if (extension.getType().equals(cacheCitationExtensionType)) {
2210
						result = extension.getValue();
2211
					}
2212
				}
2213
			} else {
2214
				String expertName = getExpertName(taxon);
2215
				String webShowName = getWebShowName(taxonName);
2216

    
2217
				// idInSource only
2218
				String idInSource = getIdInSourceOnly(taxonName);
2219

    
2220
				// build the cacheCitation
2221
				if (expertName != null) {
2222
					result += expertName + ". ";
2223
				} else {
2224
					if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
2225
				}
2226
				if (webShowName != null) {
2227
					result += webShowName + ". ";
2228
				} else {
2229
					logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2230
				}
2231

    
2232
				if (getOriginalDB(taxonName).equals("FaEu")) {
2233
					result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
2234
				} else if (getOriginalDB(taxonName).equals("EM")) {
2235
					result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
2236
				}
2237

    
2238
				if (idInSource != null) {
2239
					result += idInSource;
2240
				} else {
2241
					logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2242
				}
2243
			}
2244
		} catch (Exception e) {
2245
			e.printStackTrace();
2246
		}
2247

    
2248
		if (StringUtils.isBlank(result)) {
2249
			return null;
2250
		} else {
2251
			return result;
2252
		}
2253
	}
2254

    
2255
	/**
2256
	 * Returns the <code>OriginalDB</code> attribute.
2257
	 * @param identifiableEntity
2258
	 * @return The <code>OriginalDB</code> attribute.
2259
	 * @see MethodMapper
2260
	 */
2261
	@SuppressWarnings("unused")
2262
	private static String getOriginalDB(IdentifiableEntity identifiableEntity) {
2263
		BitSet sources  = getSources(identifiableEntity);
2264
		return PesiTransformer.getOriginalDbBySources(sources);
2265
	}
2266

    
2267
	/**
2268
	 * Returns the <code>LastAction</code> attribute.
2269
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2270
	 * @return The <code>LastAction</code> attribute.
2271
	 * @see MethodMapper
2272
	 */
2273
	@SuppressWarnings("unused")
2274
	private static String getLastAction(IdentifiableEntity<?> identEntity) {
2275
		String result = null;
2276
		try {
2277
		Set<Extension> extensions = identEntity.getExtensions();
2278
		for (Extension extension : extensions) {
2279
			if (extension.getType().equals(lastActionExtensionType)) {
2280
				result = extension.getValue();
2281
			}
2282
		}
2283
		} catch (Exception e) {
2284
			e.printStackTrace();
2285
		}
2286
		return result;
2287
	}
2288

    
2289
	/**
2290
	 * Returns the <code>LastActionDate</code> attribute.
2291
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2292
	 * @return The <code>LastActionDate</code> attribute.
2293
	 * @see MethodMapper
2294
	 */
2295
	@SuppressWarnings({ "unused" })
2296
	private static DateTime getLastActionDate(IdentifiableEntity identEntity) {
2297
		DateTime result = null;
2298
		try {
2299
			Set<Extension> extensions = identEntity.getExtensions();
2300
			for (Extension extension : extensions) {
2301
				if (extension.getType().equals(lastActionDateExtensionType)) {
2302
					String dateTime = extension.getValue();
2303
					if (dateTime != null) {
2304
						DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
2305
						result = formatter.parseDateTime(dateTime);
2306
					}
2307
				}
2308
			}
2309
		} catch (Exception e) {
2310
			e.printStackTrace();
2311
		}
2312
		return result;
2313
	}
2314

    
2315
	/**
2316
	 * Returns the <code>ExpertName</code> attribute.
2317
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2318
	 * @return The <code>ExpertName</code> attribute.
2319
	 * @see MethodMapper
2320
	 */
2321
	@SuppressWarnings("unused")
2322
	private static String getExpertName(TaxonBase<?> taxonName) {
2323
		String result = null;
2324
		try {
2325
		Set<Extension> extensions = taxonName.getExtensions();
2326
		for (Extension extension : extensions) {
2327
			if (extension.getType().equals(expertNameExtensionType)) {
2328
				result = extension.getValue();
2329
			}
2330
		}
2331
		} catch (Exception e) {
2332
			e.printStackTrace();
2333
		}
2334
		return result;
2335
	}
2336

    
2337
	/**
2338
	 * Returns the <code>ExpertFk</code> attribute.
2339
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
2340
	 * @param state The {@link PesiExportState PesiExportState}.
2341
	 * @return The <code>ExpertFk</code> attribute.
2342
	 * @see MethodMapper
2343
	 */
2344
	private static Integer getExpertFk(Reference reference, PesiExportState state) {
2345
		Integer result = state.getDbId(reference);
2346
		return result;
2347
	}
2348

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

    
2371
	/**
2372
	 * Returns the <code>SpeciesExpertFk</code> attribute.
2373
	 * @param reference The {@link Reference Reference}.
2374
	 * @param state The {@link PesiExportState PesiExportState}.
2375
	 * @return The <code>SpeciesExpertFk</code> attribute.
2376
	 * @see MethodMapper
2377
	 */
2378
	private static Integer getSpeciesExpertFk(Reference reference, PesiExportState state) {
2379
		Integer result = state.getDbId(reference);
2380
		return result;
2381
	}
2382

    
2383
	protected static TaxonNameDefaultCacheStrategy getCacheStrategy(TaxonName taxonName) {
2384
		taxonName = CdmBase.deproxy(taxonName);
2385
		TaxonNameDefaultCacheStrategy cacheStrategy;
2386
		if (taxonName.isZoological()){
2387
			cacheStrategy = zooNameStrategy;
2388
		}else if (taxonName.isBotanical()) {
2389
			cacheStrategy = nonViralNameStrategy;
2390
		}else if (taxonName.isNonViral()) {
2391
			cacheStrategy = nonViralNameStrategy;
2392
		}else if (taxonName.isBacterial()) {
2393
			cacheStrategy = nonViralNameStrategy;
2394
		}else{
2395
			logger.error("Unhandled taxon name type. Can't define strategy class");
2396
			cacheStrategy = nonViralNameStrategy;
2397
		}
2398
		return cacheStrategy;
2399
	}
2400

    
2401
	/**
2402
	 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2403
	 * @param relationship The {@link RelationshipBase Relationship}.
2404
	 * @return The <code>RelTaxonQualifierFk</code> attribute.
2405
	 * @see MethodMapper
2406
	 */
2407
	@SuppressWarnings("unused")
2408
	private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2409
		return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2410
	}
2411
    @SuppressWarnings("unused")
2412
    private static String getSynonymTypeCache(Synonym synonym, PesiExportState state) {
2413
        String result = null;
2414
        NomenclaturalCode code = null;
2415
        code = CdmBase.deproxy(synonym, Synonym.class).getAcceptedTaxon().getName().getNameType();
2416

    
2417
        if (code != null) {
2418
            result = state.getConfig().getTransformer().getCacheBySynonymType(synonym, code);
2419
        } else {
2420
            logger.error("NomenclaturalCode is NULL while creating the following synonym: " + synonym.getUuid());
2421
        }
2422
        return result;
2423
    }
2424

    
2425

    
2426
	/**
2427
	 * Returns the CDM to PESI specific export mappings.
2428
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2429
	 */
2430
	private PesiExportMapping getMapping() {
2431
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2432

    
2433
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2434
		mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2435
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2436
		mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2437

    
2438
		mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2439

    
2440
		mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2441
		mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2442
		mapping.addMapper(MethodMapper.NewInstance("AuthorString", this));  //For Taxon because Misapllied Names are handled differently
2443
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2444

    
2445
		// DisplayName
2446
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2447

    
2448
		// FossilStatus (Fk, Cache)
2449
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2450
		mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2451

    
2452
		//handled by name mapping
2453
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2454
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2455

    
2456
		//experts
2457
		ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtSpeciesExpertName);
2458
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2459

    
2460
		ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.uuidExtExpertName);
2461
		mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2462

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

    
2466
		addNameMappers(mapping);
2467

    
2468
		return mapping;
2469
	}
2470

    
2471
	/**
2472
	 * Returns the CDM to PESI specific export mappings.
2473
	 * @param state
2474
	 * @return The {@link PesiExportMapping PesiExportMapping}.
2475
	 * @throws UndefinedTransformerMethodException
2476
	 */
2477
	private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2478
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2479

    
2480
		mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2481

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

    
2484
		mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonName.class));
2485
		mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonName.class));
2486
		mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonName.class, PesiExportState.class));
2487
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
2488
		mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2489
		mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
2490
		mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonName.class));
2491
		mapping.addMapper(MethodMapper.NewInstance("GUID", this, TaxonName.class));
2492

    
2493
		// DisplayName
2494
		mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonName.class));
2495

    
2496
		mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2497
		mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2498

    
2499
		addNameMappers(mapping);
2500
		//TODO add author mapper, TypeNameFk
2501

    
2502
		return mapping;
2503
	}
2504

    
2505

    
2506
	private void addNameMappers(PesiExportMapping mapping) {
2507
		mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2508
		mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2509
		mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2510
		mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2511

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

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

    
2518

    
2519
		mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonName.class));
2520

    
2521
		mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonName.class));
2522
		mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonName.class, PesiExportState.class));
2523
		mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonName.class));
2524
		//TODO TypeNameFk
2525

    
2526
		//quality status
2527
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonName.class));
2528
		mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonName.class, PesiExportState.class));
2529

    
2530
		mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2531
		mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2532

    
2533
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2534

    
2535
	}
2536

    
2537
    /**
2538
     * Returns the <code>TaxonFk1</code> attribute. It corresponds to a CDM <code>TaxonRelationship</code>.
2539
     * @param relationship The {@link RelationshipBase Relationship}.
2540
     * @param state The {@link PesiExportState PesiExportState}.
2541
     * @return The <code>TaxonFk1</code> attribute.
2542
     * @see MethodMapper
2543
     */
2544
    @SuppressWarnings("unused")
2545
    private static Integer getSynonym(Synonym synonym, PesiExportState state) {
2546
         return state.getDbId(synonym);
2547
    }
2548

    
2549

    
2550
	private PesiExportMapping getSynRelMapping() {
2551
		PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2552
		logger.warn("SynRelMapping currently not implemented. Needs to be checked");
2553

    
2554
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getSynonym", Synonym.class, PesiExportState.class));
2555
		mapping.addMapper(DbObjectMapper.NewInstance("acceptedTaxon", "TaxonFk2"));
2556
		mapping.addMapper(DbObjectMapper.NewInstance("type", "RelTaxonQualifierFk"));
2557
		mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this.getClass(), "getSynonymTypeCache", Synonym.class, PesiExportState.class));
2558
		// TODO
2559
//		mapping.addMapper(MethodMapper.NewInstance("Notes", this,  RelationshipBase.class));
2560

    
2561
		return mapping;
2562
	}
2563

    
2564
	private PesiExportMapping getAdditionalSourceMapping(PesiExportState state) {
2565
		PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2566

    
2567
		mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2568
		mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonName.class, "Name"));
2569

    
2570
		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2571
//		mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2572
		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this, TaxonName.class));
2573

    
2574
		//we have only nomenclatural references here
2575
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2576
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2577

    
2578
		mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2579

    
2580
		return mapping;
2581
	}
2582

    
2583

    
2584
    @Override
2585
    protected boolean doCheck(PesiExportState state) {
2586
        return true;
2587
    }
2588

    
2589
    @Override
2590
    protected boolean isIgnore(PesiExportState state) {
2591
        return ! state.getConfig().isDoTaxa();
2592
    }
2593
}
(13-13/14)