Project

General

Profile

« Previous | Next » 

Revision 9bfdad13

Added by Andreas Müller over 8 years ago

use listRankSpecificRootNodes instead of deprecated methods

View differences:

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

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

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

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

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

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

  
136

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

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

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

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

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

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

  
180

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

  
189
			initPreparedStatements(state);
190

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

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

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

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

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

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

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

  
220

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

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

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

  
230

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

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

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

  
247

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

  
253
		initParentFkStatement(state);
254
	}
255

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

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

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

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

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

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

  
295

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

  
301

  
302

  
303
		int partitionCount = 0;
304

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

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

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

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

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

  
350

  
351

  
352
			}
353

  
354

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

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

  
372

  
373

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

  
386

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

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

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

  
404
			int ancestorLevel = 0;
405
			if (taxonName.getRank().equals(Rank.SUBSPECIES())) {
406
				// The accepted taxon two rank levels above should be of rank subgenus
407
				ancestorLevel  = 2;
408
			}
409
			if (taxonName.getRank().equals(Rank.SPECIES())) {
410
				// The accepted taxon one rank level above should be of rank subgenus
411
				ancestorLevel = 1;
412
			}
413
			if (ancestorLevel > 0) {
414
				if (validateAncestorOfSpecificRank(taxon, ancestorLevel, Rank.SUBGENUS())) {
415
					// The child (species or subspecies) of this parent (subgenus) has to have an infraGenericEpithet
416
					if (infraGenericEpithet == null) {
417
						logger.warn("InfraGenericEpithet for (sub)species of infrageneric taxon does not exist even though it should (also valid for Botanical Names?) for: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
418
						// maybe the taxon could be named here
419
					}
420
				}
421
			}
422

  
423
			if (infraGenericEpithet == null && rank.intValue() == 190) {
424
				logger.warn("InfraGenericEpithet was not determined although it should exist for rank 190: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
425
			}
426
			if (specificEpithet != null && rank.intValue() < 216) {
427
				logger.warn("SpecificEpithet was determined for rank " + rank + " although it should only exist for ranks higher or equal to 220: TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
428
			}
429
			if (infraSpecificEpithet != null && rank.intValue() < 225) {
430
				String message = "InfraSpecificEpithet '" +infraSpecificEpithet + "' was determined for rank " + rank + " although it should only exist for ranks higher or equal to 230: "  + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")";
431
				if (StringUtils.isNotBlank(infraSpecificEpithet)){
432
					logger.warn(message);
433
				}else{
434
					logger.warn(message);
435
				}
436
			}
437
		}
438
		if (infraSpecificEpithet != null && specificEpithet == null) {
439
			logger.warn("An infraSpecificEpithet was determined, but a specificEpithet was not determined: "  + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
440
		}
441
		if (genusOrUninomial == null) {
442
			logger.warn("GenusOrUninomial was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
443
		}
444
	}
445

  
446

  
447

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

  
462
		List<Taxon> list;
463

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

  
467
		insertBiota(state);
468

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

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

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

  
489
			}
490

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

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

  
507
		return success;
508

  
509
	}
510

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

  
530
	}
531

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

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

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

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

  
545
		return success;
546

  
547
	}
548

  
549

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

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

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

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

  
569
		StringBuffer treeIndex = new StringBuffer();
570

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

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

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

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

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

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

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

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

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

  
645
					newNode = null;
646

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

  
655
				}
656
				rankSpecificRootNodes = null;
657
			}
658

  
659
		}
660

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

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

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

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

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

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

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

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

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

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

  
726
		list = null;
727

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

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

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

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

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

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

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

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

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

  
793

  
794

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  
913
						}
914
					}
915

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

  
931

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

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

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

  
989

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

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

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

  
1014

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  
1221
			}
1222

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

  
1226
	}
1227

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

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

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

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

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

  
1277

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

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

  
1303
			if (kingdomFk != null) {
1304

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

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

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

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

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

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

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

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

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

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

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

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

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

  
1425

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

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

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

  
1473

  
1474
	/**
1475
	 * Returns the <code>RankFk</code> attribute.
1476
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
1477
	 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1478
	 * @return The <code>RankFk</code> attribute.
1479
	 * @see MethodMapper
1480
	 */
1481
	private static Integer getRankFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode) {
1482
		Integer result = null;
1483
		try {
1484
			if (nomenclaturalCode != null) {
1485
				if (taxonName != null) {
1486
					if (taxonName.getRank() == null) {
1487
						logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1488
					} else {
1489
						result = PesiTransformer.rank2RankId(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1490
					}
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff