finally refactor also rank caching
[cdmlib-apps.git] / 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
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.log4j.Logger;
28 import org.joda.time.DateTime;
29 import org.joda.time.format.DateTimeFormat;
30 import org.joda.time.format.DateTimeFormatter;
31 import org.springframework.stereotype.Component;
32 import org.springframework.transaction.TransactionStatus;
33
34 import eu.etaxonomy.cdm.api.service.TaxonServiceImpl;
35 import eu.etaxonomy.cdm.common.CdmUtils;
36 import eu.etaxonomy.cdm.io.common.Source;
37 import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
38 import eu.etaxonomy.cdm.io.common.mapping.out.DbConstantMapper;
39 import eu.etaxonomy.cdm.io.common.mapping.out.DbExtensionMapper;
40 import eu.etaxonomy.cdm.io.common.mapping.out.DbLastActionMapper;
41 import eu.etaxonomy.cdm.io.common.mapping.out.DbObjectMapper;
42 import eu.etaxonomy.cdm.io.common.mapping.out.DbStringMapper;
43 import eu.etaxonomy.cdm.io.common.mapping.out.IdMapper;
44 import eu.etaxonomy.cdm.io.common.mapping.out.MethodMapper;
45 import eu.etaxonomy.cdm.io.common.mapping.out.ObjectChangeMapper;
46 import eu.etaxonomy.cdm.io.pesi.erms.ErmsTransformer;
47 import eu.etaxonomy.cdm.io.profiler.ProfilerController;
48 import eu.etaxonomy.cdm.model.common.Annotation;
49 import eu.etaxonomy.cdm.model.common.AnnotationType;
50 import eu.etaxonomy.cdm.model.common.CdmBase;
51 import eu.etaxonomy.cdm.model.common.Extension;
52 import eu.etaxonomy.cdm.model.common.ExtensionType;
53 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
54 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
55 import eu.etaxonomy.cdm.model.common.Language;
56 import eu.etaxonomy.cdm.model.common.Marker;
57 import eu.etaxonomy.cdm.model.common.MarkerType;
58 import eu.etaxonomy.cdm.model.common.RelationshipBase;
59 import eu.etaxonomy.cdm.model.name.BacterialName;
60 import eu.etaxonomy.cdm.model.name.BotanicalName;
61 import eu.etaxonomy.cdm.model.name.HybridRelationship;
62 import eu.etaxonomy.cdm.model.name.NameRelationship;
63 import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
64 import eu.etaxonomy.cdm.model.name.NameTypeDesignationStatus;
65 import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
66 import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
67 import eu.etaxonomy.cdm.model.name.NonViralName;
68 import eu.etaxonomy.cdm.model.name.Rank;
69 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
70 import eu.etaxonomy.cdm.model.name.ZoologicalName;
71 import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
72 import eu.etaxonomy.cdm.model.reference.Reference;
73 import eu.etaxonomy.cdm.model.taxon.Classification;
74 import eu.etaxonomy.cdm.model.taxon.Synonym;
75 import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
76 import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
77 import eu.etaxonomy.cdm.model.taxon.Taxon;
78 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
79 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
80 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
81 import eu.etaxonomy.cdm.strategy.cache.HTMLTagRules;
82 import eu.etaxonomy.cdm.strategy.cache.TagEnum;
83 import eu.etaxonomy.cdm.strategy.cache.name.BacterialNameDefaultCacheStrategy;
84 import eu.etaxonomy.cdm.strategy.cache.name.BotanicNameDefaultCacheStrategy;
85 import eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy;
86 import eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy;
87 import eu.etaxonomy.cdm.strategy.cache.name.ZooNameNoMarkerCacheStrategy;
88
89 /**
90 * The export class for {@link eu.etaxonomy.cdm.model.name.TaxonNameBase TaxonNames}.<p>
91 * Inserts into DataWarehouse database table <code>Taxon</code>.
92 * It is divided into four phases:<p><ul>
93 * <li>Phase 1: Export of all {@link eu.etaxonomy.cdm.model.name.TaxonNameBase TaxonNames} except some data exported in the following phases.
94 * <li>Phase 2: Export of additional data: ParenTaxonFk and TreeIndex.
95 * <li>Phase 3: Export of additional data: Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk.
96 * <li>Phase 4: Export of Inferred Synonyms.</ul>
97 * @author e.-m.lee
98 * @date 23.02.2010
99 *
100 */
101 @Component
102 public class PesiTaxonExport extends PesiExportBase {
103 private static final Logger logger = Logger.getLogger(PesiTaxonExport.class);
104 private static final Class<? extends CdmBase> standardMethodParameter = TaxonBase.class;
105
106 private static int modCount = 1000;
107 private static final String dbTableName = "Taxon";
108 private static final String dbTableNameSynRel = "RelTaxon";
109 private static final String dbTableAdditionalSourceRel = "AdditionalTaxonSource";
110 private static final String pluralString = "Taxa";
111 private static final String parentPluralString = "Taxa";
112 private PreparedStatement parentTaxonFk_TreeIndex_KingdomFkStmt;
113 private PreparedStatement parentTaxonFkStmt;
114 private PreparedStatement rankTypeExpertsUpdateStmt;
115 private PreparedStatement rankUpdateStmt;
116 private NomenclaturalCode nomenclaturalCode;
117 private Integer kingdomFk;
118 private HashMap<Rank, Rank> rank2endRankMap = new HashMap<Rank, Rank>();
119 private List<Rank> rankList = new ArrayList<Rank>();
120 private static final UUID uuidTreeIndex = UUID.fromString("28f4e205-1d02-4d3a-8288-775ea8413009");
121 private AnnotationType treeIndexAnnotationType;
122 private static ExtensionType lastActionExtensionType;
123 private static ExtensionType lastActionDateExtensionType;
124 private static ExtensionType expertNameExtensionType;
125 private static ExtensionType speciesExpertNameExtensionType;
126 private static ExtensionType cacheCitationExtensionType;
127 public static NonViralNameDefaultCacheStrategy<?> zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
128 public static NonViralNameDefaultCacheStrategy<?> botanicalNameStrategy = BotanicNameDefaultCacheStrategy.NewInstance();
129 public static NonViralNameDefaultCacheStrategy<?> nonViralNameStrategy = NonViralNameDefaultCacheStrategy.NewInstance();
130 public static NonViralNameDefaultCacheStrategy<?> bacterialNameStrategy = BacterialNameDefaultCacheStrategy.NewInstance();
131 private static int currentTaxonId;
132
133
134 /**
135 * @return the treeIndexAnnotationType
136 */
137 protected AnnotationType getTreeIndexAnnotationType() {
138 return treeIndexAnnotationType;
139 }
140
141 /**
142 * @param treeIndexAnnotationType the treeIndexAnnotationType to set
143 */
144 protected void setTreeIndexAnnotationType(AnnotationType treeIndexAnnotationType) {
145 this.treeIndexAnnotationType = treeIndexAnnotationType;
146 }
147
148 enum NamePosition {
149 beginning,
150 end,
151 between,
152 alone,
153 nowhere
154 }
155
156 public PesiTaxonExport() {
157 super();
158 }
159
160 /* (non-Javadoc)
161 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getStandardMethodParameter()
162 */
163 @Override
164 public Class<? extends CdmBase> getStandardMethodParameter() {
165 return standardMethodParameter;
166 }
167
168 /* (non-Javadoc)
169 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
170 */
171 @Override
172 protected boolean doCheck(PesiExportState state) {
173 boolean result = true;
174 return result;
175 }
176
177
178 /* (non-Javadoc)
179 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
180 */
181 @Override
182 protected void doInvoke(PesiExportState state) {
183 try {
184 logger.info("*** Started Making " + pluralString + " ...");
185
186 initPreparedStatements(state);
187
188 // Stores whether this invoke was successful or not.
189 boolean success = true;
190
191 // PESI: Clear the database table Taxon.
192 doDelete(state);
193
194 // Get specific mappings: (CDM) Taxon -> (PESI) Taxon
195 PesiExportMapping mapping = getMapping();
196 PesiExportMapping synonymRelMapping = getSynRelMapping();
197 PesiExportMapping additionalSourceMapping = getAdditionalSourceMapping(state);
198
199 // Initialize the db mapper
200 mapping.initialize(state);
201 synonymRelMapping.initialize(state);
202 additionalSourceMapping.initialize(state);
203
204 // Find extensionTypes
205 lastActionExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionUuid);
206 lastActionDateExtensionType = (ExtensionType)getTermService().find(PesiTransformer.lastActionDateUuid);
207 expertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
208 speciesExpertNameExtensionType = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
209 cacheCitationExtensionType = (ExtensionType)getTermService().find(PesiTransformer.cacheCitationUuid);
210
211 //Export Taxa..
212 success &= doPhase01(state, mapping, additionalSourceMapping);
213
214 //"PHASE 1b: Handle names without taxa ...
215 success &= doNames(state, additionalSourceMapping);
216
217
218 // 2nd Round: Add ParentTaxonFk to each taxon
219 success &= doPhase02(state);
220
221 //PHASE 3: Add Rank data, KingdomFk, TypeNameFk ...
222 success &= doPhase03(state);
223
224 // 4nd Round: Add TreeIndex to each taxon
225 success &= doPhase04(state);
226
227
228 //"PHASE 4: Creating Inferred Synonyms...
229 success &= doPhase05(state, mapping, synonymRelMapping);
230
231 logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
232
233 if (!success){
234 state.setUnsuccessfull();
235 }
236 return;
237 } catch (Exception e) {
238 e.printStackTrace();
239 logger.error(e.getMessage());
240 state.setUnsuccessfull();
241 }
242 }
243
244
245 private void initPreparedStatements(PesiExportState state) throws SQLException {
246 initTreeIndexStatement(state);
247 initRankExpertsUpdateStmt(state);
248 initRankUpdateStatement(state);
249
250 initParentFkStatement(state);
251 }
252
253 // Prepare TreeIndex-And-KingdomFk-Statement
254 private void initTreeIndexStatement(PesiExportState state) throws SQLException {
255 Connection connection = state.getConfig().getDestination().getConnection();
256 String parentTaxonFk_TreeIndex_KingdomFkSql = "UPDATE Taxon SET ParentTaxonFk = ?, TreeIndex = ? WHERE TaxonId = ?";
257 parentTaxonFk_TreeIndex_KingdomFkStmt = connection.prepareStatement(parentTaxonFk_TreeIndex_KingdomFkSql);
258 }
259
260 // Prepare TreeIndex-And-KingdomFk-Statement
261 private void initParentFkStatement(PesiExportState state) throws SQLException {
262 Connection connection = state.getConfig().getDestination().getConnection();
263 String parentTaxonFkSql = "UPDATE Taxon SET ParentTaxonFk = ? WHERE TaxonId = ?";
264 parentTaxonFkStmt = connection.prepareStatement(parentTaxonFkSql);
265 }
266
267 private void initRankUpdateStatement(PesiExportState state) throws SQLException {
268 Connection connection = state.getConfig().getDestination().getConnection();
269 String rankSql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, KingdomFk = ? WHERE TaxonId = ?";
270 rankUpdateStmt = connection.prepareStatement(rankSql);
271 }
272
273 private void initRankExpertsUpdateStmt(PesiExportState state) throws SQLException {
274 // String sql_old = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ?, " +
275 // "ExpertFk = ?, SpeciesExpertFk = ? WHERE TaxonId = ?";
276 //TODO handle experts GUIDs
277 Connection connection = state.getConfig().getDestination().getConnection();
278
279 String sql = "UPDATE Taxon SET RankFk = ?, RankCache = ?, TypeNameFk = ?, KingdomFk = ? " +
280 " WHERE TaxonId = ?";
281 rankTypeExpertsUpdateStmt = connection.prepareStatement(sql);
282 }
283
284 private boolean doPhase01(PesiExportState state, PesiExportMapping mapping, PesiExportMapping additionalSourceMapping) throws SQLException {
285 int count = 0;
286 int pastCount = 0;
287 List<TaxonBase> list;
288 boolean success = true;
289 // Get the limit for objects to save within a single transaction.
290 int limit = state.getConfig().getLimitSave();
291
292
293 logger.info("PHASE 1: Export Taxa...limit is " + limit);
294 // Start transaction
295 TransactionStatus txStatus = startTransaction(true);
296 logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
297
298
299
300 int partitionCount = 0;
301
302 logger.info("Taking snapshot at the beginning of phase 1 of taxonExport");
303 ProfilerController.memorySnapshot();
304 while ((list = getNextTaxonPartition(null, limit, partitionCount++, null)) != null ) {
305
306 logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
307 for (TaxonBase<?> taxon : list) {
308 doCount(count++, modCount, pluralString);
309 TaxonNameBase<?,?> taxonName = taxon.getName();
310 NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
311
312 if (! nvn.isProtectedTitleCache()){
313 nvn.setTitleCache(null, false);
314 }
315 if (! nvn.isProtectedNameCache()){
316 nvn.setNameCache(null, false);
317 }
318 if (! nvn.isProtectedFullTitleCache()){
319 nvn.setFullTitleCache(null, false);
320 }
321 if (! nvn.isProtectedAuthorshipCache()){
322 nvn.setAuthorshipCache(null, false);
323 }
324
325 //core mapping
326 success &= mapping.invoke(taxon);
327 //additional source
328 if (nvn.getNomenclaturalReference() != null || StringUtils.isNotBlank(nvn.getNomenclaturalMicroReference() )){
329 additionalSourceMapping.invoke(taxon);
330 }
331
332 validatePhaseOne(taxon, nvn);
333 taxon = null;
334 nvn = null;
335 taxonName = null;
336
337
338
339 }
340
341
342 // Commit transaction
343 commitTransaction(txStatus);
344 logger.debug("Committed transaction.");
345 logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
346 pastCount = count;
347 /*logger.warn("Taking snapshot at the end of the loop of phase 1 of taxonExport");
348 ProfilerController.memorySnapshot();
349 */
350 // Start transaction
351 txStatus = startTransaction(true);
352 logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
353
354 }
355 if (list == null ) {
356 logger.info("No " + pluralString + " left to fetch.");
357 }
358
359
360
361 // Commit transaction
362 commitTransaction(txStatus);
363 txStatus = null;
364 logger.debug("Committed transaction.");
365 list = null;
366 logger.warn("Taking snapshot at the end of phase 1 of taxonExport");
367 ProfilerController.memorySnapshot();
368 return success;
369 }
370
371
372 private void validatePhaseOne(TaxonBase<?> taxon, NonViralName taxonName) {
373 // Check whether some rules are violated
374 nomenclaturalCode = taxonName.getNomenclaturalCode();
375 String genusOrUninomial = taxonName.getGenusOrUninomial();
376 String specificEpithet = taxonName.getSpecificEpithet();
377 String infraSpecificEpithet = taxonName.getInfraSpecificEpithet();
378 String infraGenericEpithet = taxonName.getInfraGenericEpithet();
379 Integer rank = getRankFk(taxonName, nomenclaturalCode);
380
381 if (rank == null) {
382 logger.error("Rank was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
383 } else {
384
385 // Check whether infraGenericEpithet is set correctly
386 // 1. Childs of an accepted taxon of rank subgenus that are accepted taxa of rank species have to have an infraGenericEpithet
387 // 2. Grandchilds of an accepted taxon of rank subgenus that are accepted taxa of rank subspecies have to have an infraGenericEpithet
388
389 int ancestorLevel = 0;
390 if (taxonName.getRank().equals(Rank.SUBSPECIES())) {
391 // The accepted taxon two rank levels above should be of rank subgenus
392 ancestorLevel = 2;
393 }
394 if (taxonName.getRank().equals(Rank.SPECIES())) {
395 // The accepted taxon one rank level above should be of rank subgenus
396 ancestorLevel = 1;
397 }
398 if (ancestorLevel > 0) {
399 if (validateAncestorOfSpecificRank(taxon, ancestorLevel, Rank.SUBGENUS())) {
400 // The child (species or subspecies) of this parent (subgenus) has to have an infraGenericEpithet
401 if (infraGenericEpithet == null) {
402 logger.warn("InfraGenericEpithet does not exist even though it should for: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
403 // maybe the taxon could be named here
404 }
405 }
406 }
407
408 if (infraGenericEpithet == null && rank.intValue() == 190) {
409 logger.warn("InfraGenericEpithet was not determined although it should exist for rank 190: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
410 }
411 if (specificEpithet != null && rank.intValue() < 216) {
412 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() + ")");
413 }
414 if (infraSpecificEpithet != null && rank.intValue() < 225) {
415 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() + ")";
416 if (StringUtils.isNotBlank(infraSpecificEpithet)){
417 logger.warn(message);
418 }else{
419 logger.warn(message);
420 }
421 }
422 }
423 if (infraSpecificEpithet != null && specificEpithet == null) {
424 logger.warn("An infraSpecificEpithet was determined, but a specificEpithet was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
425 }
426 if (genusOrUninomial == null) {
427 logger.warn("GenusOrUninomial was not determined: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
428 }
429 }
430
431
432
433 /**
434 * 2nd Round: Add ParentTaxonFk to each taxon and add Biota if not exists
435 * @param state
436 * @return
437 */
438 private boolean doPhase02(PesiExportState state) {
439 int count = 0;
440 int pastCount = 0;
441 List<Taxon> list;
442 boolean success = true;
443 // Get the limit for objects to save within a single transaction.
444 int limit = state.getConfig().getLimitSave();
445
446 insertBiota(state);
447
448 logger.info("PHASE 2: Make ParentFk and TreeIndex ... limit is " + limit);
449 // Start transaction
450 TransactionStatus txStatus = startTransaction(true);
451 int partitionCount = 0;
452
453 // ProfilerController.memorySnapshot();
454 while ((list = getNextTaxonPartition(Taxon.class, limit, partitionCount++, null)) != null ) {
455
456 logger.info("Fetched " + list.size() + " " + pluralString + ". Exporting...");
457 for (Taxon taxon : list) {
458 for (TaxonNode node : taxon.getTaxonNodes()){
459 doCount(count++, modCount, pluralString);
460 TaxonNode parentNode = node.getParent();
461 if (parentNode != null){
462 int childId = state.getDbId( taxon);
463 int parentId = state.getDbId(parentNode.getTaxon());
464 success &= invokeParentTaxonFk(parentId, childId);
465 }
466 }
467
468 }
469
470 // Commit transaction
471 commitTransaction(txStatus);
472 logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
473 pastCount = count;
474 // Start transaction
475 txStatus = startTransaction(true);
476 logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
477
478 }
479 if (list == null ) {
480 logger.info("No " + pluralString + " left to fetch.");
481 }
482
483 // Commit transaction
484 commitTransaction(txStatus);
485
486 return success;
487
488 }
489
490 /**
491 * Inserts the Biota Taxon if not yet exists.
492 * @param state
493 * @throws SQLException
494 */
495 private void insertBiota(PesiExportState state) {
496 try {
497 ResultSet rs = state.getConfig().getDestination().getResultSet("SELECT * FROM Taxon WHERE GenusOrUninomial = 'Biota' ");
498 if (rs.next() == false){
499 int biotaId = state.getConfig().getNameIdStart() -1 ;
500 String sqlInsertBiota = "INSERT INTO Taxon (TaxonId, KingdomFk, RankFk, RankCache, GenusOrUninomial, WebSearchName, WebShowName, FullName, DisplayName, TaxonStatusFk, TaxonStatusCache) " +
501 " VALUES (" + biotaId + ", 0, 0, 'Superdomain', 'Biota', 'Biota', '<i>Biota</i>', 'Biota', '<i>Biota</i>', 1 , 'accepted')";
502 state.getConfig().getDestination().update(sqlInsertBiota);
503 }
504 } catch (SQLException e) {
505 logger.warn ("Biota could not be requested or inserted");
506 }
507 }
508
509 // 4th round: Add TreeIndex to each taxon
510 private boolean doPhase04(PesiExportState state) {
511 boolean success = true;
512
513 logger.info("PHASE 4: Make TreeIndex ... ");
514
515 //TODO test if possible to move to phase 02
516 String sql = " UPDATE Taxon SET ParentTaxonFk = (Select TaxonId from Taxon where RankFk = 0) " +
517 " WHERE (RankFk = 10) and TaxonStatusFk = 1 ";
518 state.getConfig().getDestination().update(sql);
519
520 state.getConfig().getDestination().update("EXEC dbo.recalculateallstoredpaths");
521
522 return success;
523
524 }
525
526
527 // 2nd Round: Add ParentTaxonFk, TreeIndex to each Taxon
528 private boolean doPhase02_OLD(PesiExportState state) {
529 boolean success = true;
530 if (! state.getConfig().isDoTreeIndex()){
531 logger.info ("Ignore PHASE 2: ParentTaxonFk and TreeIndex");
532 return success;
533 }
534
535 List<Classification> classificationList = null;
536 logger.info("PHASE 2: Add ParenTaxonFk and TreeIndex...");
537
538 // Specify starting ranks for tree traversing
539 rankList.add(Rank.KINGDOM());
540 rankList.add(Rank.GENUS());
541
542 // Specify where to stop traversing (value) when starting at a specific Rank (key)
543 rank2endRankMap.put(Rank.GENUS(), null); // Since NULL does not match an existing Rank, traverse all the way down to the leaves
544 rank2endRankMap.put(Rank.KINGDOM(), Rank.GENUS()); // excludes rank genus
545
546 StringBuffer treeIndex = new StringBuffer();
547
548 // Retrieve list of classifications
549 TransactionStatus txStatus = startTransaction(true);
550 logger.info("Started transaction for parentFk and treeIndex. Fetching all classifications...");
551 classificationList = getClassificationService().listClassifications(null, 0, null, null);
552 commitTransaction(txStatus);
553 logger.debug("Committed transaction.");
554
555 logger.info("Fetched " + classificationList.size() + " classification(s).");
556
557 setTreeIndexAnnotationType(getAnnotationType(uuidTreeIndex, "TreeIndex", "TreeIndex", "TI"));
558 List<TaxonNode> rankSpecificRootNodes;
559 for (Classification classification : classificationList) {
560 for (Rank rank : rankList) {
561
562 txStatus = startTransaction(true);
563 logger.info("Started transaction to fetch all rootNodes specific to Rank " + rank.getLabel() + " ...");
564
565 rankSpecificRootNodes = getClassificationService().loadRankSpecificRootNodes(classification, rank, null);
566 logger.info("Fetched " + rankSpecificRootNodes.size() + " RootNodes for Rank " + rank.getLabel());
567
568 commitTransaction(txStatus);
569 logger.debug("Committed transaction.");
570
571 for (TaxonNode rootNode : rankSpecificRootNodes) {
572 txStatus = startTransaction(false);
573 Rank endRank = rank2endRankMap.get(rank);
574 if (endRank != null) {
575 logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till Rank " + endRank.getLabel() + " ...");
576 } else {
577 logger.debug("Started transaction to traverse childNodes of rootNode (" + rootNode.getUuid() + ") till leaves are reached ...");
578 }
579
580 TaxonNode newNode = getTaxonNodeService().load(rootNode.getUuid());
581
582 if (isPesiTaxon(newNode.getTaxon())){
583 TaxonNode parentNode = newNode.getParent();
584 if (rank.equals(Rank.KINGDOM())) {
585 treeIndex = new StringBuffer();
586 treeIndex.append("#");
587 } else {
588 // Get treeIndex from parentNode
589 if (parentNode != null) {
590 boolean annotationFound = false;
591 Set<Annotation> annotations = parentNode.getAnnotations();
592 for (Annotation annotation : annotations) {
593 AnnotationType annotationType = annotation.getAnnotationType();
594 if (annotationType != null && annotationType.equals(getTreeIndexAnnotationType())) {
595 treeIndex = new StringBuffer(CdmUtils.Nz(annotation.getText()));
596 annotationFound = true;
597 // logger.error("treeIndex: " + treeIndex);
598 break;
599 }
600 }
601 if (!annotationFound) {
602 // This should not happen because it means that the treeIndex was not set correctly as an annotation to parentNode
603 logger.error("TreeIndex could not be read from annotation of TaxonNode: " + parentNode.getUuid() + ", Taxon: " + parentNode.getTaxon().getUuid());
604 treeIndex = new StringBuffer();
605 treeIndex.append("#");
606 }
607 } else {
608 // TreeIndex could not be determined, but it's unclear how to proceed to generate a correct treeIndex if the parentNode is NULL
609 logger.error("ParentNode for RootNode is NULL. TreeIndex could not be determined: " + newNode.getUuid());
610 treeIndex = new StringBuffer(); // This just prevents growing of the treeIndex in a wrong manner
611 treeIndex.append("#");
612 }
613 }
614 nomenclaturalCode = newNode.getTaxon().getName().getNomenclaturalCode();
615 kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
616 traverseTree(newNode, parentNode, treeIndex, endRank, state);
617 parentNode =null;
618 }else{
619 logger.debug("Taxon is not a PESI taxon: " + newNode.getTaxon().getUuid());
620 }
621
622 newNode = null;
623
624 try {
625 commitTransaction(txStatus);
626 logger.debug("Committed transaction.");
627 } catch (Exception e) {
628 logger.error(e.getMessage());
629 e.printStackTrace();
630 }
631
632 }
633 rankSpecificRootNodes = null;
634 }
635
636 }
637
638 logger.warn("Taking snapshot at the end of phase 2 of taxonExport");
639 ProfilerController.memorySnapshot();
640 return success;
641 }
642
643 //PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...
644 private boolean doPhase03(PesiExportState state) {
645 int count = 0;
646 int pastCount = 0;
647 boolean success = true;
648 if (! state.getConfig().isDoTreeIndex()){
649 logger.info ("Ignore PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
650 return success;
651 }
652 // Get the limit for objects to save within a single transaction.
653 int limit = state.getConfig().getLimitSave();
654
655 List<TaxonBase> list;
656 logger.info("PHASE 3: Add Rank data, KingdomFk, TypeNameFk, expertFk and speciesExpertFk...");
657 // Be sure to add rank information, KingdomFk, TypeNameFk, expertFk and speciesExpertFk to every taxonName
658
659 // Start transaction
660 TransactionStatus txStatus = startTransaction(true);
661 logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
662 int partitionCount = 0;
663 while ((list = getNextTaxonPartition(TaxonBase.class, limit, partitionCount++, null)) != null) {
664
665 logger.debug("Fetched " + list.size() + " " + pluralString + ". Exporting...");
666 for (TaxonBase<?> taxon : list) {
667 TaxonNameBase<?,?> taxonName = taxon.getName();
668 // Determine expertFk
669 // Integer expertFk = makeExpertFk(state, taxonName);
670 //
671 // // Determine speciesExpertFk
672 // Integer speciesExpertFk = makeSpeciesExpertFk(state, taxonName);
673
674 doCount(count++, modCount, pluralString);
675 Integer typeNameFk = getTypeNameFk(taxonName, state);
676
677 //TODO why are expertFks needed? (Andreas M.)
678 // if (expertFk != null || speciesExpertFk != null) {
679 invokeRankDataAndTypeNameFkAndKingdomFk(taxonName, nomenclaturalCode, state.getDbId(taxon),
680 typeNameFk, kingdomFk, state);
681 // }
682
683 taxon = null;
684 taxonName = null;
685 }
686
687 // Commit transaction
688 commitTransaction(txStatus);
689 logger.debug("Committed transaction.");
690 logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
691 pastCount = count;
692
693 // Start transaction
694 txStatus = startTransaction(true);
695 logger.info("Started new transaction for rank, kingdom, typeName, expertFk and speciesExpertFK. Fetching some " + pluralString + " (max: " + limit + ") ...");
696 }
697 if (list == null) {
698 logger.info("No " + pluralString + " left to fetch.");
699 }
700
701 list = null;
702
703 // Commit transaction
704 commitTransaction(txStatus);
705
706 logger.debug("Committed transaction.");
707 logger.debug("Try to take snapshot at the end of phase 3 of taxonExport, number of partitions: " + partitionCount);
708 ProfilerController.memorySnapshot();
709 return success;
710 }
711
712 // "PHASE 5: Creating Inferred Synonyms..."
713 private boolean doPhase05(PesiExportState state, PesiExportMapping mapping, PesiExportMapping synRelMapping) throws SQLException {
714 int count;
715 int pastCount;
716 boolean success = true;
717 // Get the limit for objects to save within a single transaction.
718 if (! state.getConfig().isDoInferredSynonyms()){
719 logger.info ("Ignore PHASE 5: Creating Inferred Synonyms...");
720 return success;
721 }
722
723 int limit = state.getConfig().getLimitSave();
724 // Create inferred synonyms for accepted taxa
725 logger.info("PHASE 4: Creating Inferred Synonyms...");
726
727 // Determine the count of elements in datawarehouse database table Taxon
728 currentTaxonId = determineTaxonCount(state);
729 currentTaxonId++;
730
731 count = 0;
732 pastCount = 0;
733 int pageSize = limit;
734 int pageNumber = 1;
735 String inferredSynonymPluralString = "Inferred Synonyms";
736
737 // Start transaction
738 TransactionStatus txStatus = startTransaction(true);
739 logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
740 List<TaxonBase> taxonList = null;
741
742
743
744 while ((taxonList = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", Rank.SPECIES(), pageSize, pageNumber)).size() > 0) {
745 HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
746
747 logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
748 inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
749 synRelMapping, taxonList));
750
751 doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
752 // Commit transaction
753 commitTransaction(txStatus);
754 logger.debug("Committed transaction.");
755 logger.info("Exported " + (taxonList.size()) + " " + inferredSynonymPluralString + ". Total: " + count);
756 //pastCount = count;
757
758 // Save Rank Data and KingdomFk for inferred synonyms
759 for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
760 invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomenclaturalCode, taxonFk, kingdomFk, state);
761 }
762
763 // Start transaction
764 txStatus = startTransaction(true);
765 logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
766
767 // Increment pageNumber
768 pageNumber++;
769 }
770
771 while ((taxonList = getTaxonService().listTaxaByName(Taxon.class, "*", "*", "*", "*", Rank.SUBSPECIES(), pageSize, pageNumber)).size() > 0) {
772 HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
773
774 logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
775 inferredSynonymsDataToBeSaved.putAll(createInferredSynonymsForTaxonList(state, mapping,
776 synRelMapping, taxonList));
777 ;
778 doCount(count += taxonList.size(), modCount, inferredSynonymPluralString);
779 // Commit transaction
780 commitTransaction(txStatus);
781 logger.debug("Committed transaction.");
782 logger.info("Exported " + taxonList.size()+ " " + inferredSynonymPluralString + ". Total: " + count);
783 //pastCount = count;
784
785 // Save Rank Data and KingdomFk for inferred synonyms
786 for (Integer taxonFk : inferredSynonymsDataToBeSaved.keySet()) {
787 invokeRankDataAndKingdomFk(inferredSynonymsDataToBeSaved.get(taxonFk), nomenclaturalCode, taxonFk, kingdomFk, state);
788 }
789
790 // Start transaction
791 txStatus = startTransaction(true);
792 logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
793
794 // Increment pageNumber
795 pageNumber++;
796 }
797 if (taxonList.size() == 0) {
798 logger.info("No " + parentPluralString + " left to fetch.");
799 }
800
801 taxonList = null;
802 logger.warn("Taking snapshot at the end of phase 4 of taxonExport");
803 ProfilerController.memorySnapshot();
804
805 // Commit transaction
806 commitTransaction(txStatus);
807 logger.warn("Taking snapshot at the end of phase 4 after commit of taxonExport");
808 ProfilerController.memorySnapshot();
809 System.gc();
810 logger.warn("Taking snapshot at the end of phase 4 after gc() of taxonExport");
811 ProfilerController.memorySnapshot();
812 logger.debug("Committed transaction.");
813 return success;
814 }
815
816 /**
817 * @param state
818 * @param mapping
819 * @param synRelMapping
820 * @param currentTaxonId
821 * @param taxonList
822 * @param inferredSynonymsDataToBeSaved
823 * @return
824 */
825 private HashMap<Integer, TaxonNameBase<?, ?>> createInferredSynonymsForTaxonList(PesiExportState state,
826 PesiExportMapping mapping, PesiExportMapping synRelMapping, List<TaxonBase> taxonList) {
827
828 Taxon acceptedTaxon;
829 Classification classification = null;
830 List<Synonym> inferredSynonyms = null;
831 boolean localSuccess = true;
832
833 HashMap<Integer, TaxonNameBase<?,?>> inferredSynonymsDataToBeSaved = new HashMap<Integer, TaxonNameBase<?,?>>();
834
835 for (TaxonBase<?> taxonBase : taxonList) {
836
837 if (taxonBase.isInstanceOf(Taxon.class)) { // this should always be the case since we should have fetched accepted taxon only, but you never know...
838 acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
839 TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();
840
841 if (taxonName.isInstanceOf(ZoologicalName.class)) {
842 nomenclaturalCode = taxonName.getNomenclaturalCode();
843 kingdomFk = PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode);
844
845 Set<TaxonNode> taxonNodes = acceptedTaxon.getTaxonNodes();
846 TaxonNode singleNode = null;
847
848 if (taxonNodes.size() > 0) {
849 // Determine the classification of the current TaxonNode
850
851 singleNode = taxonNodes.iterator().next();
852 if (singleNode != null) {
853 classification = singleNode.getClassification();
854 } else {
855 logger.error("A TaxonNode belonging to this accepted Taxon is NULL: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() +")");
856 }
857 } else {
858 // Classification could not be determined directly from this TaxonNode
859 // The stored classification from another TaxonNode is used. It's a simple, but not a failsafe fallback solution.
860 if (taxonNodes.size() == 0) {
861 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");
862
863 }
864 }
865
866 if (classification != null) {
867 try{
868 TaxonNameBase name = acceptedTaxon.getName();
869 //if (name.isSpecies() || name.isInfraSpecific()){
870 inferredSynonyms = getTaxonService().createAllInferredSynonyms(acceptedTaxon, classification, true);
871 //}
872 // inferredSynonyms = getTaxonService().createInferredSynonyms(classification, acceptedTaxon, SynonymRelationshipType.INFERRED_GENUS_OF());
873 if (inferredSynonyms != null) {
874 for (Synonym synonym : inferredSynonyms) {
875 // TaxonNameBase<?,?> synonymName = synonym.getName();
876 MarkerType markerType =getUuidMarkerType(PesiTransformer.uuidMarkerGuidIsMissing, state);
877 synonym.addMarker(Marker.NewInstance(markerType, true));
878 // Both Synonym and its TaxonName have no valid Id yet
879 synonym.setId(currentTaxonId++);
880
881
882 localSuccess &= mapping.invoke(synonym);
883 //get SynonymRelationship and export
884 if (synonym.getSynonymRelations().isEmpty() ){
885 SynonymRelationship synRel;
886 IdentifiableSource source = synonym.getSources().iterator().next();
887 if (source.getIdNamespace().contains("Potential combination")){
888 synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
889 logger.warn(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to potential combination");
890 } else if (source.getIdNamespace().contains("Inferred Genus")){
891 synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.INFERRED_GENUS_OF());
892 logger.warn(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to inferred genus");
893 } else if (source.getIdNamespace().contains("Inferred Epithet")){
894 synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.INFERRED_EPITHET_OF());
895 logger.warn(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to inferred epithet");
896 } else{
897 synRel = acceptedTaxon.addSynonym(synonym, SynonymRelationshipType.INFERRED_SYNONYM_OF());
898 logger.warn(synonym.getTitleCache() + " has no synonym relationship to " + acceptedTaxon.getTitleCache() + " type is set to inferred synonym");
899 }
900
901 localSuccess &= synRelMapping.invoke(synRel);
902 if (!localSuccess) {
903 logger.warn("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
904 }
905 synRel = null;
906 } else {
907 for (SynonymRelationship synRel: synonym.getSynonymRelations()){
908 localSuccess &= synRelMapping.invoke(synRel);
909 if (!localSuccess) {
910 logger.warn("Synonym relationship export failed " + synonym.getTitleCache() + " accepted taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache()+")");
911 }
912 synRel = null;
913 }
914 }
915
916 inferredSynonymsDataToBeSaved.put(synonym.getId(), synonym.getName());
917 }
918 }
919 }catch(Exception e){
920 logger.error(e.getMessage());
921 e.printStackTrace();
922 }
923 } else {
924 logger.error("Classification is NULL. Inferred Synonyms could not be created for this Taxon: " + acceptedTaxon.getUuid() + " (" + acceptedTaxon.getTitleCache() + ")");
925 }
926 } else {
927 // logger.error("TaxonName is not a ZoologicalName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
928 }
929 } else {
930 logger.error("This TaxonBase is not a Taxon even though it should be: " + taxonBase.getUuid() + " (" + taxonBase.getTitleCache() + ")");
931 }
932 }
933 return inferredSynonymsDataToBeSaved;
934 }
935
936
937 /**
938 * Handles names that do not appear in taxa
939 * @param state
940 * @param mapping
941 * @return
942 */
943 private boolean doNames(PesiExportState state, PesiExportMapping additionalSourceMapping) throws SQLException {
944
945 boolean success = true;
946 if (! state.getConfig().isDoPureNames()){
947 logger.info ("Ignore PHASE 1b: PureNames");
948 return success;
949 }
950
951 try {
952 PesiExportMapping mapping = getPureNameMapping(state);
953 mapping.initialize(state);
954 int count = 0;
955 int pastCount = 0;
956 List<NonViralName<?>> list;
957 success = true;
958 // Get the limit for objects to save within a single transaction.
959 int limit = state.getConfig().getLimitSave();
960
961
962 logger.info("PHASE 1b: Export Pure Names ...");
963 // Start transaction
964 TransactionStatus txStatus = startTransaction(true);
965 logger.info("Started new transaction for Pure Names. Fetching some " + pluralString + " (max: " + limit + ") ...");
966
967 int partitionCount = 0;
968 while ((list = getNextPureNamePartition(null, limit, partitionCount++)) != null ) {
969
970 logger.info("Fetched " + list.size() + " names without taxa. Exporting...");
971 for (TaxonNameBase<?,?> taxonName : list) {
972 doCount(count++, modCount, pluralString);
973 success &= mapping.invoke(taxonName);
974 //additional source
975 if (taxonName.getNomenclaturalReference() != null || StringUtils.isNotBlank(taxonName.getNomenclaturalMicroReference() )){
976 additionalSourceMapping.invoke(taxonName);
977 }
978 }
979
980 // Commit transaction
981 commitTransaction(txStatus);
982 logger.debug("Committed transaction.");
983 logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
984 pastCount = count;
985
986 // Start transaction
987 txStatus = startTransaction(true);
988 logger.info("Started new transaction for PureNames. Fetching some " + pluralString + " (max: " + limit + ") ...");
989 }
990 if (list == null) {
991 logger.info("No " + pluralString + " left to fetch.");
992 }
993 // Commit transaction
994 commitTransaction(txStatus);
995 logger.debug("Committed transaction.");
996 } catch (Exception e) {
997 logger.error("Error occurred in pure name export");
998 e.printStackTrace();
999 success = false;
1000 }
1001 return success;
1002 }
1003
1004 /**
1005 * Determines the current number of entries in the DataWarehouse database table <code>Taxon</code>.
1006 * @param state The {@link PesiExportState PesiExportState}.
1007 * @return The count.
1008 */
1009 private Integer determineTaxonCount(PesiExportState state) {
1010 Integer result = null;
1011 PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
1012
1013 String sql;
1014 Source destination = pesiConfig.getDestination();
1015 sql = "SELECT max(taxonId) FROM Taxon";
1016 destination.setQuery(sql);
1017 ResultSet resultSet = destination.getResultSet();
1018 try {
1019 resultSet.next();
1020 result = resultSet.getInt(1);
1021 } catch (SQLException e) {
1022 logger.error("TaxonCount could not be determined: " + e.getMessage());
1023 e.printStackTrace();
1024 }
1025 return result;
1026 }
1027
1028 /**
1029 * Checks whether a parent at specific level has a specific Rank.
1030 * @param taxonName A {@link TaxonNameBase TaxonName}.
1031 * @param level The ancestor level.
1032 * @param ancestorRank The ancestor rank.
1033 * @return Whether a parent at a specific level has a specific Rank.
1034 */
1035 private boolean validateAncestorOfSpecificRank(TaxonBase<?> taxonBase, int level, Rank ancestorRank) {
1036 boolean result = false;
1037 TaxonNode parentNode = null;
1038 if (taxonBase.isInstanceOf(Taxon.class)){
1039 Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1040 // Get ancestor Taxon via TaxonNode
1041 Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();
1042 if (taxonNodes.size() == 1) {
1043 TaxonNode taxonNode = taxonNodes.iterator().next();
1044 if (taxonNode != null) {
1045 for (int i = 0; i < level; i++) {
1046 if (taxonNode != null) {
1047 taxonNode = taxonNode.getParent();
1048 }
1049 }
1050 parentNode = taxonNode;
1051 }
1052 } else if (taxonNodes.size() > 1) {
1053 logger.error("This taxon has " + taxonNodes.size() + " taxonNodes: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1054 }
1055 }
1056 //compare
1057 if (parentNode != null) {
1058 TaxonNode node = CdmBase.deproxy(parentNode, TaxonNode.class);
1059 Taxon parentTaxon = node.getTaxon();
1060 if (parentTaxon != null) {
1061 TaxonNameBase<?,?> parentTaxonName = parentTaxon.getName();
1062 if (parentTaxonName != null && parentTaxonName.getRank().equals(ancestorRank)) {
1063 result = true;
1064 }
1065 } else {
1066 logger.error("This TaxonNode has no Taxon: " + node.getUuid());
1067 }
1068 }
1069 return result;
1070 }
1071
1072 /**
1073 * Returns the AnnotationType for a given UUID.
1074 * @param uuid The Annotation UUID.
1075 * @param label The Annotation label.
1076 * @param text The Annotation text.
1077 * @param labelAbbrev The Annotation label abbreviation.
1078 * @return The AnnotationType.
1079 */
1080 protected AnnotationType getAnnotationType(UUID uuid, String label, String text, String labelAbbrev){
1081 AnnotationType annotationType = (AnnotationType)getTermService().find(uuid);
1082 if (annotationType == null) {
1083 annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
1084 annotationType.setUuid(uuid);
1085 // annotationType.setVocabulary(AnnotationType.EDITORIAL().getVocabulary());
1086 getTermService().save(annotationType);
1087 }
1088 return annotationType;
1089 }
1090
1091 /**
1092 * Traverses the classification recursively and stores determined values for every Taxon.
1093 * @param childNode The {@link TaxonNode TaxonNode} to process.
1094 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1095 * @param treeIndex The TreeIndex at the current level.
1096 * @param fetchLevel Rank to stop fetching at.
1097 * @param state The {@link PesiExportState PesiExportState}.
1098 */
1099 private void traverseTree(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, Rank fetchLevel, PesiExportState state) {
1100 // Traverse all branches from this childNode until specified fetchLevel is reached.
1101 StringBuffer localTreeIndex = new StringBuffer(treeIndex);
1102 Taxon childTaxon = childNode.getTaxon();
1103 if (childTaxon != null) {
1104 if (isPesiTaxon(childTaxon)){
1105 Integer taxonId = state.getDbId(childTaxon);
1106 TaxonNameBase<?,?> childName = childTaxon.getName();
1107 if (taxonId != null) {
1108 Rank childRank = childName.getRank();
1109 if (childRank != null) {
1110 if (! childRank.equals(fetchLevel)) {
1111
1112 localTreeIndex.append(taxonId + "#");
1113
1114 saveData(childNode, parentNode, localTreeIndex, state, taxonId);
1115
1116 // Store treeIndex as annotation for further use
1117 Annotation annotation = Annotation.NewInstance(localTreeIndex.toString(), getTreeIndexAnnotationType(), Language.DEFAULT());
1118 childNode.addAnnotation(annotation);
1119
1120 for (TaxonNode newNode : childNode.getChildNodes()) {
1121 if (newNode.getTaxon() != null && isPesiTaxon(newNode.getTaxon())){
1122 traverseTree(newNode, childNode, localTreeIndex, fetchLevel, state);
1123 }
1124 }
1125
1126 } else {
1127 // logger.debug("Target Rank " + fetchLevel.getLabel() + " reached");
1128 return;
1129 }
1130 } else {
1131 logger.error("Rank is NULL. FetchLevel can not be checked: " + childName.getUuid() + " (" + childName.getTitleCache() + ")");
1132 }
1133 } else {
1134 logger.error("Taxon can not be found in state: " + childTaxon.getUuid() + " (" + childTaxon.getTitleCache() + ")");
1135 }
1136 }else{
1137 if (logger.isDebugEnabled()){
1138 logger.debug("Taxon is not a PESI taxon: " + childTaxon.getUuid());
1139 }
1140 }
1141
1142 } else {
1143 logger.error("Taxon is NULL for TaxonNode: " + childNode.getUuid());
1144 }
1145 }
1146
1147 /**
1148 * Stores values in database for every recursive round.
1149 * @param childNode The {@link TaxonNode TaxonNode} to process.
1150 * @param parentNode The parent {@link TaxonNode TaxonNode} of the childNode.
1151 * @param treeIndex The TreeIndex at the current level.
1152 * @param state The {@link PesiExportState PesiExportState}.
1153 * @param currentTaxonFk The TaxonFk to store the values for.
1154 */
1155 private void saveData(TaxonNode childNode, TaxonNode parentNode, StringBuffer treeIndex, PesiExportState state, Integer currentTaxonFk) {
1156 // We are differentiating kingdoms by the nomenclatural code for now.
1157 // This needs to be handled in a better way as soon as we know how to differentiate between more kingdoms.
1158 Taxon childTaxon = childNode.getTaxon();
1159 if (isPesiTaxon(childTaxon)) {
1160 TaxonBase<?> parentTaxon = null;
1161 if (parentNode != null) {
1162 parentTaxon = parentNode.getTaxon();
1163
1164 }
1165
1166 invokeParentTaxonFkAndTreeIndex(state.getDbId(parentTaxon), currentTaxonFk, treeIndex);
1167 }
1168
1169 }
1170
1171 /**
1172 * Inserts values into the Taxon database table.
1173 * @param taxonName The {@link TaxonNameBase TaxonName}.
1174 * @param state The {@link PesiExportState PesiExportState}.
1175 * @param stmt The prepared statement.
1176 * @return Whether save was successful or not.
1177 */
1178 protected boolean invokeParentTaxonFkAndTreeIndex(Integer parentTaxonFk, Integer currentTaxonFk, StringBuffer treeIndex) {
1179 try {
1180 if (parentTaxonFk != null) {
1181 parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(1, parentTaxonFk);
1182 } else {
1183 parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(1, null);
1184 }
1185
1186 if (treeIndex != null) {
1187 parentTaxonFk_TreeIndex_KingdomFkStmt.setString(2, treeIndex.toString());
1188 } else {
1189 parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(2, null);
1190 }
1191
1192 if (currentTaxonFk != null) {
1193 parentTaxonFk_TreeIndex_KingdomFkStmt.setInt(3, currentTaxonFk);
1194 } else {
1195 parentTaxonFk_TreeIndex_KingdomFkStmt.setObject(3, null);
1196 }
1197
1198 parentTaxonFk_TreeIndex_KingdomFkStmt.executeUpdate();
1199 return true;
1200 } catch (SQLException e) {
1201 logger.error("ParentTaxonFk (" + parentTaxonFk ==null? "-":parentTaxonFk + ") and TreeIndex could not be inserted into database for taxon "+ (currentTaxonFk == null? "-" :currentTaxonFk) + ": " + e.getMessage());
1202 e.printStackTrace();
1203 return false;
1204 }
1205 }
1206
1207 protected boolean invokeParentTaxonFk(Integer parentId, Integer childId) {
1208 try {
1209 parentTaxonFkStmt.setInt(1, parentId);
1210 parentTaxonFkStmt.setInt(2, childId);
1211 parentTaxonFkStmt.executeUpdate();
1212 return true;
1213 } catch (SQLException e) {
1214 logger.warn("ParentTaxonFk (" + parentId ==null? "-":parentId + ") could not be inserted into database for taxon "+ (childId == null? "-" :childId) + ": " + e.getMessage());
1215 e.printStackTrace();
1216 return false;
1217 }
1218 }
1219
1220
1221 /**
1222 * Inserts Rank data and KingdomFk into the Taxon database table.
1223 * @param taxonName The {@link TaxonNameBase TaxonName}.
1224 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1225 * @param taxonFk The TaxonFk to store the values for.
1226 * @param state
1227 * @param kindomFk The KingdomFk.
1228 * @return Whether save was successful or not.
1229 */
1230 private boolean invokeRankDataAndKingdomFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, Integer taxonFk, Integer kingdomFk, PesiExportState state) {
1231 try {
1232 Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1233 if (rankFk != null) {
1234 rankUpdateStmt.setInt(1, rankFk);
1235 } else {
1236 rankUpdateStmt.setObject(1, null);
1237 }
1238
1239 String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
1240 if (rankCache != null) {
1241 rankUpdateStmt.setString(2, rankCache);
1242 } else {
1243 rankUpdateStmt.setObject(2, null);
1244 }
1245
1246 if (kingdomFk != null) {
1247 rankUpdateStmt.setInt(3, kingdomFk);
1248 } else {
1249 rankUpdateStmt.setObject(3, null);
1250 }
1251
1252 if (taxonFk != null) {
1253 rankUpdateStmt.setInt(4, taxonFk);
1254 } else {
1255 rankUpdateStmt.setObject(4, null);
1256 }
1257
1258 rankUpdateStmt.executeUpdate();
1259 return true;
1260 } catch (SQLException e) {
1261 logger.error("Data (RankFk, RankCache, KingdomFk) could not be inserted into database: " + e.getMessage());
1262 e.printStackTrace();
1263 return false;
1264 }
1265 }
1266
1267 /**
1268 * Inserts Rank data, TypeNameFk, KingdomFk, expertFk and speciesExpertFk into the Taxon database table.
1269 * @param taxonName The {@link TaxonNameBase TaxonName}.
1270 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1271 * @param taxonFk The TaxonFk to store the values for.
1272 * @param typeNameFk The TypeNameFk.
1273 * @param state
1274 * @param kindomFk The KingdomFk.
1275 * @param expertFk The ExpertFk.
1276 * @param speciesExpertFk The SpeciesExpertFk.
1277 * @return Whether save was successful or not.
1278 */
1279 private boolean invokeRankDataAndTypeNameFkAndKingdomFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode,
1280 Integer taxonFk, Integer typeNameFk, Integer kingdomFkk, PesiExportState state) {
1281 try {
1282 int index = 1;
1283 Integer rankFk = getRankFk(taxonName, nomenclaturalCode);
1284 if (rankFk != null) {
1285 rankTypeExpertsUpdateStmt.setInt(index++, rankFk);
1286 } else {
1287 rankTypeExpertsUpdateStmt.setObject(index++, null);
1288 }
1289
1290 String rankCache = getRankCache(taxonName, nomenclaturalCode, state);
1291 if (rankCache != null) {
1292 rankTypeExpertsUpdateStmt.setString(index++, rankCache);
1293 } else {
1294 rankTypeExpertsUpdateStmt.setObject(index++, null);
1295 }
1296
1297 if (typeNameFk != null) {
1298 rankTypeExpertsUpdateStmt.setInt(index++, typeNameFk);
1299 } else {
1300 rankTypeExpertsUpdateStmt.setObject(index++, null);
1301 }
1302
1303 if (kingdomFk != null) {
1304 rankTypeExpertsUpdateStmt.setInt(index++, kingdomFk);
1305 } else {
1306 rankTypeExpertsUpdateStmt.setObject(index++, null);
1307 }
1308
1309 // if (expertFk != null) {
1310 // rankTypeExpertsUpdateStmt.setInt(5, expertFk);
1311 // } else {
1312 // rankTypeExpertsUpdateStmt.setObject(5, null);
1313 // }
1314 //
1315 // //TODO handle experts GUIDS
1316 // if (speciesExpertFk != null) {
1317 // rankTypeExpertsUpdateStmt.setInt(6, speciesExpertFk);
1318 // } else {
1319 // rankTypeExpertsUpdateStmt.setObject(6, null);
1320 // }
1321 //
1322 if (taxonFk != null) {
1323 rankTypeExpertsUpdateStmt.setInt(index++, taxonFk);
1324 } else {
1325 rankTypeExpertsUpdateStmt.setObject(index++, null);
1326 }
1327
1328 rankTypeExpertsUpdateStmt.executeUpdate();
1329 return true;
1330 } catch (SQLException e) {
1331 logger.error("Data could not be inserted into database: " + e.getMessage());
1332 e.printStackTrace();
1333 return false;
1334 } catch (Exception e) {
1335 logger.error("Some exception occurred: " + e.getMessage());
1336 e.printStackTrace();
1337 return false;
1338 }
1339 }
1340
1341 /**
1342 * Deletes all entries of database tables related to <code>Taxon</code>.
1343 * @param state The {@link PesiExportState PesiExportState}.
1344 * @return Whether the delete operation was successful or not.
1345 */
1346 protected boolean doDelete(PesiExportState state) {
1347 PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
1348
1349 String sql;
1350 Source destination = pesiConfig.getDestination();
1351
1352 // Clear Taxon
1353 sql = "DELETE FROM " + dbTableName;
1354 destination.setQuery(sql);
1355 destination.update(sql);
1356 return true;
1357 }
1358
1359 /* (non-Javadoc)
1360 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
1361 */
1362 @Override
1363 protected boolean isIgnore(PesiExportState state) {
1364 return ! state.getConfig().isDoTaxa();
1365 }
1366
1367
1368 /**
1369 * Creates the kingdom fk.
1370 * @param taxonName
1371 * @return
1372 */
1373 @SuppressWarnings("unused") //used by mapper
1374 private static Integer getKingdomFk(TaxonNameBase taxonName){
1375 return PesiTransformer.nomenClaturalCode2Kingdom(taxonName.getNomenclaturalCode());
1376 }
1377
1378 /**
1379 * Creates the parent fk.
1380 * @param taxonName
1381 * @return
1382 */
1383 @SuppressWarnings("unused") //used by mapper
1384 private static Integer getParentTaxonFk(TaxonBase<?> taxonBase, PesiExportState state){
1385 if (taxonBase.isInstanceOf(Taxon.class)){
1386 Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1387 if (! isMisappliedName(taxon)){
1388 Set<TaxonNode> nodes = taxon.getTaxonNodes();
1389 if (nodes.size() == 0){
1390 if (taxon.getName().getRank().isLower(Rank.KINGDOM())){
1391 logger.warn("Accepted taxon has no parent. " + taxon.getTitleCache() + ", " + taxon.getUuid());
1392 }
1393 }else if (nodes.size() > 1){
1394 logger.warn("Taxon has more than 1 node attached. This is not supported by PESI export." + taxon.getTitleCache() + ", " + taxon.getUuid());
1395 }else{
1396 Taxon parent =nodes.iterator().next().getParent().getTaxon();
1397 return state.getDbId(parent);
1398 }
1399 }
1400 }
1401 return null;
1402 }
1403
1404 /**
1405 * Returns the rankFk for the taxon name based on the names nomenclatural code.
1406 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1407 * @param taxonName
1408 * @return
1409 */
1410 @SuppressWarnings("unused") //used by mapper
1411 private static Integer getRankFk(TaxonNameBase<?,?> taxonName) {
1412 return getRankFk(taxonName, taxonName.getNomenclaturalCode());
1413 }
1414
1415
1416 /**
1417 * Returns the <code>RankFk</code> attribute.
1418 * @param taxonName The {@link TaxonNameBase TaxonName}.
1419 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1420 * @return The <code>RankFk</code> attribute.
1421 * @see MethodMapper
1422 */
1423 private static Integer getRankFk(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode) {
1424 Integer result = null;
1425 try {
1426 if (nomenclaturalCode != null) {
1427 if (taxonName != null) {
1428 if (taxonName.getRank() == null) {
1429 logger.warn("Rank is null: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1430 } else {
1431 result = PesiTransformer.rank2RankId(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1432 }
1433 if (result == null) {
1434 logger.warn("Rank could not be determined for PESI-Kingdom-Id " + PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode) + " and TaxonName " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1435 }
1436 }
1437 }
1438 } catch (Exception e) {
1439 e.printStackTrace();
1440 }
1441 return result;
1442 }
1443
1444 /**
1445 * Returns the rank cache for the taxon name based on the names nomenclatural code.
1446 * You may not use this method for kingdoms other then Animalia, Plantae and Bacteria.
1447 * @param taxonName
1448 * @return
1449 */
1450 @SuppressWarnings("unused") //used by mapper
1451 private static String getRankCache(TaxonNameBase<?,?> taxonName, PesiExportState state) {
1452 return getRankCache(taxonName, taxonName.getNomenclaturalCode(), state);
1453 }
1454
1455
1456 /**
1457 * Returns the <code>RankCache</code> attribute.
1458 * @param taxonName The {@link TaxonNameBase TaxonName}.
1459 * @param nomenclaturalCode The {@link NomenclaturalCode NomenclaturalCode}.
1460 * @param state
1461 * @return The <code>RankCache</code> attribute.
1462 * @see MethodMapper
1463 */
1464 private static String getRankCache(TaxonNameBase<?,?> taxonName, NomenclaturalCode nomenclaturalCode, PesiExportState state) {
1465 if (nomenclaturalCode != null) {
1466 return state.getTransformer().rank2RankCache(taxonName.getRank(), PesiTransformer.nomenClaturalCode2Kingdom(nomenclaturalCode));
1467 }else{
1468 logger.warn("No nomenclatural code defined for name " + taxonName.getUuid());
1469 return null;
1470 }
1471
1472 }
1473
1474
1475 /**
1476 * Returns the <code>DisplayName</code> attribute.
1477 * @param taxon The {@link TaxonBase Taxon}.
1478 * @return The <code>DisplayName</code> attribute.
1479 * @see MethodMapper
1480 */
1481 @SuppressWarnings("unused") //used by Mapper
1482 private static String getDisplayName(TaxonBase<?> taxon) {
1483 TaxonNameBase<?,?> taxonName = taxon.getName();
1484 String result = getDisplayName(taxonName);
1485 if (isMisappliedName(taxon)){
1486 result = result + " " + getAuthorString(taxon);
1487 }
1488 return result;
1489 }
1490
1491 /**
1492 * Returns the <code>AuthorString</code> attribute.
1493 * @param taxonName The {@link TaxonNameBase TaxonName}.
1494 * @return The <code>AuthorString</code> attribute.
1495 * @see MethodMapper
1496 */
1497 @SuppressWarnings("unused") //used by mapper
1498 protected static String getAuthorString(TaxonBase<?> taxon) {
1499 try {
1500 String result = null;
1501 boolean isNonViralName = false;
1502 String authorshipCache = null;
1503 TaxonNameBase<?,?> taxonName = taxon.getName();
1504 if (taxonName != null && taxonName.isInstanceOf(NonViralName.class)){
1505 authorshipCache = CdmBase.deproxy(taxonName, NonViralName.class).getAuthorshipCache();
1506 isNonViralName = true;
1507 }
1508 result = authorshipCache;
1509
1510 // For a misapplied names there are special rules
1511 if (isMisappliedName(taxon)){
1512 if (taxon.getSec() != null){
1513 String secTitle = taxon.getSec().getTitleCache();
1514 if (! secTitle.startsWith("auct")){
1515 secTitle = "sensu " + secTitle;
1516 }else if (secTitle.equals("auct")){ //may be removed once the title cache is generated correctly for references with title auct. #
1517 secTitle = "auct.";
1518 }
1519 return secTitle;
1520 }else if (StringUtils.isBlank(authorshipCache)) {
1521 // Set authorshipCache to "auct."
1522 result = PesiTransformer.AUCT_STRING;
1523 }else{
1524 result = PesiTransformer.AUCT_STRING;
1525 // result = authorshipCache;
1526 }
1527 }
1528
1529 if (taxonName == null){
1530 logger.warn("TaxonName does not exist for taxon: " + taxon.getUuid() + " (" + taxon.getTitleCache() + ")");
1531 }else if (! isNonViralName){
1532 logger.warn("TaxonName is not of instance NonViralName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1533 }
1534
1535 if (StringUtils.isBlank(result)) {
1536 return null;
1537 } else {
1538 return result;
1539 }
1540 } catch (Exception e) {
1541 e.printStackTrace();
1542 return null;
1543 }
1544
1545 }
1546
1547
1548 /**
1549 * Returns the <code>DisplayName</code> attribute.
1550 * @param taxonName The {@link TaxonNameBase TaxonName}.
1551 * @return The <code>DisplayName</code> attribute.
1552 * @see MethodMapper
1553 */
1554 @SuppressWarnings("unused") //used by Mapper
1555 private static String getDisplayName(TaxonNameBase<?,?> taxonName) {
1556 // TODO: extension?
1557 if (taxonName == null) {
1558 return null;
1559 }else{
1560 INonViralNameCacheStrategy<NonViralName<?>> cacheStrategy = getCacheStrategy(taxonName);
1561 HTMLTagRules tagRules = new HTMLTagRules().
1562 addRule(TagEnum.name, "i").
1563 addRule(TagEnum.nomStatus, "@status@");
1564
1565 NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1566 String result = cacheStrategy.getFullTitleCache(nvn, tagRules);
1567 cacheStrategy = null;
1568 nvn = null;
1569 return result.replaceAll(",?\\<@status@\\>.*\\</@status@\\>", "");
1570 }
1571 }
1572
1573
1574 /**
1575 * Returns the <code>WebShowName</code> attribute for a taxon.
1576 * @param taxonName The {@link TaxonNameBase TaxonName}.
1577 * @return The <code>WebShowName</code> attribute.
1578 * @see MethodMapper
1579 */
1580 @SuppressWarnings("unused")
1581 private static String getWebShowName(TaxonBase<?> taxon) {
1582 TaxonNameBase<?,?> taxonName = taxon.getName();
1583 String result = getWebShowName(taxonName);
1584 if (isMisappliedName(taxon)){
1585 result = result + " " + getAuthorString(taxon);
1586 }
1587 return result;
1588 }
1589
1590 /**
1591 * Returns the <code>WebShowName</code> attribute.
1592 * @param taxonName The {@link TaxonNameBase TaxonName}.
1593 * @return The <code>WebShowName</code> attribute.
1594 * @see MethodMapper
1595 */
1596 private static String getWebShowName(TaxonNameBase<?,?> taxonName) {
1597 //TODO extensions?
1598 if (taxonName == null) {
1599 return null;
1600 }else{
1601 INonViralNameCacheStrategy<NonViralName<?>> cacheStrategy = getCacheStrategy(taxonName);
1602
1603 HTMLTagRules tagRules = new HTMLTagRules().addRule(TagEnum.name, "i");
1604 NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1605 String result = cacheStrategy.getTitleCache(nvn, tagRules);
1606 cacheStrategy = null;
1607 nvn = null;
1608 return result;
1609 }
1610 }
1611
1612
1613 /**
1614 * Returns the <code>WebSearchName</code> attribute.
1615 * @param taxonName The {@link NonViralName NonViralName}.
1616 * @return The <code>WebSearchName</code> attribute.
1617 * @see MethodMapper
1618 */
1619 @SuppressWarnings("unused")
1620 private static String getWebSearchName(TaxonNameBase<?,?> taxonName) {
1621 //TODO extensions?
1622 NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1623 NonViralNameDefaultCacheStrategy<NonViralName<?>> strategy = getCacheStrategy(nvn);
1624 String result = strategy.getNameCache(nvn);
1625 strategy = null;
1626 nvn = null;
1627 return result;
1628 }
1629
1630
1631 /**
1632 * Returns the <code>FullName</code> attribute.
1633 * @param taxonName The {@link NonViralName NonViralName}.
1634 * @return The <code>FullName</code> attribute.
1635 * @see MethodMapper
1636 */
1637 @SuppressWarnings("unused")
1638 private static String getFullName(TaxonNameBase taxonName) {
1639 //TODO extensions?
1640 NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
1641 String result = getCacheStrategy(nvn).getTitleCache(nvn);
1642 Iterator<TaxonBase> taxa = taxonName.getTaxa().iterator();
1643 if (taxonName.getTaxa().size() >0){
1644 if (taxonName.getTaxa().size() == 1){
1645 TaxonBase taxon = taxa.next();
1646 if (isMisappliedName(taxon)){
1647 result = result + " " + getAuthorString(taxon);
1648 }
1649 taxon = null;
1650 }
1651 }
1652 taxa = null;
1653 nvn = null;
1654 return result;
1655 }
1656
1657 /**
1658 * Returns the <code>FullName</code> attribute.
1659 * @param taxon The {@link TaxonBase taxon}.
1660 * @return The <code>FullName</code> attribute.
1661 * @see MethodMapper
1662 */
1663 /*@SuppressWarnings("unused")
1664 private static String getFullName(TaxonBase taxon) {
1665 //TODO extensions?
1666 TaxonNameBase name = taxon.getName();
1667 String result = getFullName(name);
1668 if (isMisappliedName(taxon)){
1669 result = result + " " + getAuthorString(taxon);
1670 }
1671
1672 return result;
1673 }
1674 */
1675
1676 /**
1677 * Returns the nomenclatural reference which is the reference
1678 * including the detail (microreference).
1679 * @param taxonName The {@link TaxonNameBase TaxonName}.
1680 * @return The <code>AuthorString</code> attribute.
1681 * @see MethodMapper
1682 */
1683 @SuppressWarnings("unused")
1684 private static String getNomRefString(TaxonNameBase<?,?> taxonName) {
1685 INomenclaturalReference ref = taxonName.getNomenclaturalReference();
1686 return ref == null ? null : ref.getNomenclaturalCitation(taxonName.getNomenclaturalMicroReference());
1687 }
1688
1689
1690 /**
1691 * Returns the <code>NameStatusFk</code> attribute.
1692 * @param taxonName The {@link TaxonNameBase TaxonName}.
1693 * @return The <code>NameStatusFk</code> attribute.
1694 * @see MethodMapper
1695 */
1696 @SuppressWarnings("unused")
1697 private static Integer getNameStatusFk(TaxonNameBase<?,?> taxonName) {
1698 Integer result = null;
1699
1700 NomenclaturalStatus state = getNameStatus(taxonName);
1701 if (state != null) {
1702 result = PesiTransformer.nomStatus2nomStatusFk(state.getType());
1703 }
1704 return result;
1705 }
1706
1707 /**
1708 * Returns the <code>NameStatusCache</code> attribute.
1709 * @param taxonName The {@link TaxonNameBase TaxonName}.
1710 * @return The <code>NameStatusCache</code> attribute.
1711 * @throws UndefinedTransformerMethodException
1712 * @see MethodMapper
1713 */
1714 @SuppressWarnings("unused")
1715 private static String getNameStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1716 String result = null;
1717 NomenclaturalStatus status = getNameStatus(taxonName);
1718 if (status != null) {
1719 result = state.getTransformer().getCacheByNomStatus(status.getType());
1720 }
1721 return result;
1722 }
1723
1724
1725 private static NomenclaturalStatus getNameStatus(TaxonNameBase<?,?> taxonName) {
1726 try {
1727 if (taxonName != null && (taxonName.isInstanceOf(NonViralName.class))) {
1728 NonViralName<?> nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1729 Set<NomenclaturalStatus> states = nonViralName.getStatus();
1730 if (states.size() == 1) {
1731 NomenclaturalStatus status = states.iterator().next();
1732 return status;
1733 } else if (states.size() > 1) {
1734 logger.error("This TaxonName has more than one Nomenclatural Status: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1735 }
1736 }
1737
1738 } catch (Exception e) {
1739 e.printStackTrace();
1740 }
1741 return null;
1742 }
1743 /**
1744 * Returns the <code>TaxonStatusFk</code> attribute.
1745 * @param taxonName The {@link TaxonNameBase TaxonName}.
1746 * @param state The {@link PesiExportState PesiExportState}.
1747 * @return The <code>TaxonStatusFk</code> attribute.
1748 * @see MethodMapper
1749 */
1750 private static Integer getTaxonStatusFk(TaxonBase<?> taxon, PesiExportState state) {
1751 Integer result = null;
1752
1753 try {
1754 if (isMisappliedName(taxon)) {
1755 Synonym synonym = Synonym.NewInstance(null, null);
1756
1757 // This works as long as only the instance is important to differentiate between TaxonStatus.
1758 result = PesiTransformer.taxonBase2statusFk(synonym); // Auct References are treated as Synonyms in Datawarehouse now.
1759 } else {
1760 result = PesiTransformer.taxonBase2statusFk(taxon);
1761 }
1762
1763 } catch (Exception e) {
1764 e.printStackTrace();
1765 }
1766 return result;
1767 }
1768
1769 /**
1770 * Returns the <code>TaxonStatusCache</code> attribute.
1771 * @param taxonName The {@link TaxonNameBase TaxonName}.
1772 * @param state The {@link PesiExportState PesiExportState}.
1773 * @return The <code>TaxonStatusCache</code> attribute.
1774 * @throws UndefinedTransformerMethodException
1775 * @see MethodMapper
1776 */
1777 @SuppressWarnings("unused")
1778 private static String getTaxonStatusCache(TaxonBase<?> taxon, PesiExportState state) throws UndefinedTransformerMethodException {
1779 return state.getTransformer().getTaxonStatusCacheByKey(getTaxonStatusFk(taxon, state));
1780 }
1781
1782 /**
1783 * Returns the <code>TypeNameFk</code> attribute.
1784 * @param taxonName The {@link TaxonNameBase TaxonName}.
1785 * @param state The {@link PesiExportState PesiExportState}.
1786 * @return The <code>TypeNameFk</code> attribute.
1787 * @see MethodMapper
1788 */
1789 private static Integer getTypeNameFk(TaxonNameBase<?,?> taxonNameBase, PesiExportState state) {
1790 Integer result = null;
1791 if (taxonNameBase != null) {
1792 Set<NameTypeDesignation> nameTypeDesignations = taxonNameBase.getNameTypeDesignations();
1793 if (nameTypeDesignations.size() == 1) {
1794 NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1795 if (nameTypeDesignation != null) {
1796 TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1797 if (typeName != null) {
1798 result = state.getDbId(typeName);
1799 }
1800 }
1801 } else if (nameTypeDesignations.size() > 1) {
1802 logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonNameBase.getUuid() + " (" + taxonNameBase.getTitleCache() + ")");
1803 }
1804 }
1805 return result;
1806 }
1807
1808 /**
1809 * Returns the <code>TypeFullnameCache</code> attribute.
1810 * @param taxonName The {@link TaxonNameBase TaxonName}.
1811 * @return The <code>TypeFullnameCache</code> attribute.
1812 * @see MethodMapper
1813 */
1814 @SuppressWarnings("unused")
1815 private static String getTypeFullnameCache(TaxonNameBase<?,?> taxonName) {
1816 String result = null;
1817
1818 try {
1819 if (taxonName != null) {
1820 Set<NameTypeDesignation> nameTypeDesignations = taxonName.getNameTypeDesignations();
1821 if (nameTypeDesignations.size() == 1) {
1822 NameTypeDesignation nameTypeDesignation = nameTypeDesignations.iterator().next();
1823 if (nameTypeDesignation != null) {
1824 TaxonNameBase<?,?> typeName = nameTypeDesignation.getTypeName();
1825 if (typeName != null) {
1826 result = typeName.getTitleCache();
1827 }
1828 }
1829 } else if (nameTypeDesignations.size() > 1) {
1830 logger.warn("This TaxonName has " + nameTypeDesignations.size() + " NameTypeDesignations: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1831 }
1832 }
1833
1834 } catch (Exception e) {
1835 e.printStackTrace();
1836 }
1837 return result;
1838 }
1839
1840
1841 /**
1842 * Returns the <code>QualityStatusFk</code> attribute.
1843 * @param taxonName The {@link TaxonNameBase TaxonName}.
1844 * @return The <code>QualityStatusFk</code> attribute.
1845 * @see MethodMapper
1846 */
1847 private static Integer getQualityStatusFk(TaxonNameBase taxonName) {
1848 BitSet sources = getSources(taxonName);
1849 return PesiTransformer.getQualityStatusKeyBySource(sources, taxonName);
1850 }
1851
1852
1853 /**
1854 * Returns the <code>QualityStatusCache</code> attribute.
1855 * @param taxonName The {@link TaxonNameBase TaxonName}.
1856 * @return The <code>QualityStatusCache</code> attribute.
1857 * @throws UndefinedTransformerMethodException
1858 * @see MethodMapper
1859 */
1860 @SuppressWarnings("unused")
1861 private static String getQualityStatusCache(TaxonNameBase taxonName, PesiExportState state) throws UndefinedTransformerMethodException {
1862 return state.getTransformer().getQualityStatusCacheByKey(getQualityStatusFk(taxonName));
1863 }
1864
1865
1866 /**
1867 * Returns the <code>TypeDesignationStatusFk</code> attribute.
1868 * @param taxonName The {@link TaxonNameBase TaxonName}.
1869 * @return The <code>TypeDesignationStatusFk</code> attribute.
1870 * @see MethodMapper
1871 */
1872 @SuppressWarnings("unused")
1873 private static Integer getTypeDesignationStatusFk(TaxonNameBase<?,?> taxonName) {
1874 Integer result = null;
1875
1876 try {
1877 if (taxonName != null) {
1878 Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1879 if (typeDesignations.size() == 1) {
1880 Object obj = typeDesignations.iterator().next().getTypeStatus();
1881 NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1882 result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusId(designationStatus);
1883 } else if (typeDesignations.size() > 1) {
1884 logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1885 }
1886 }
1887
1888 } catch (Exception e) {
1889 e.printStackTrace();
1890 }
1891 return result;
1892 }
1893
1894 /**
1895 * Returns the <code>TypeDesignationStatusCache</code> attribute.
1896 * @param taxonName The {@link TaxonNameBase TaxonName}.
1897 * @return The <code>TypeDesignationStatusCache</code> attribute.
1898 * @see MethodMapper
1899 */
1900 @SuppressWarnings("unused")
1901 private static String getTypeDesignationStatusCache(TaxonNameBase<?,?> taxonName) {
1902 String result = null;
1903
1904 try {
1905 if (taxonName != null) {
1906 Set<NameTypeDesignation> typeDesignations = taxonName.getNameTypeDesignations();
1907 if (typeDesignations.size() == 1) {
1908 Object obj = typeDesignations.iterator().next().getTypeStatus();
1909 NameTypeDesignationStatus designationStatus = CdmBase.deproxy(obj, NameTypeDesignationStatus.class);
1910 result = PesiTransformer.nameTypeDesignationStatus2TypeDesignationStatusCache(designationStatus);
1911 } else if (typeDesignations.size() > 1) {
1912 logger.error("Found a TaxonName with more than one NameTypeDesignation: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
1913 }
1914 }
1915
1916 } catch (Exception e) {
1917 e.printStackTrace();
1918 }
1919 return result;
1920 }
1921
1922 /**
1923 * Returns the <code>FossilStatusFk</code> attribute.
1924 * @param taxonName The {@link TaxonNameBase TaxonName}.
1925 * @return The <code>FossilStatusFk</code> attribute.
1926 * @see MethodMapper
1927 */
1928 @SuppressWarnings("unused")
1929 private static Integer getFossilStatusFk(IdentifiableEntity<?> identEntity, PesiExportState state) {
1930 Integer result = null;
1931
1932 Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
1933 if (fossilStatuus.size() == 0){
1934 return null;
1935 }else if (fossilStatuus.size() > 1){
1936 logger.warn("More than 1 fossil status given for " + identEntity.getTitleCache() + " " + identEntity.getUuid());
1937 }
1938 String fossilStatus = fossilStatuus.iterator().next();
1939
1940 int statusFk = state.getTransformer().FossilStatusCache2FossilStatusFk(fossilStatus);
1941 return statusFk;
1942 }
1943
1944 /**
1945 * Returns the <code>FossilStatusCache</code> attribute.
1946 * @param taxonName The {@link TaxonNameBase TaxonName}.
1947 * @return The <code>FossilStatusCache</code> attribute.
1948 * @see MethodMapper
1949 */
1950 @SuppressWarnings("unused")
1951 private static String getFossilStatusCache(IdentifiableEntity<?> identEntity, PesiExportState state) {
1952 String result = null;
1953 Set<String> fossilStatuus = identEntity.getExtensions(ErmsTransformer.uuidFossilStatus);
1954 if (fossilStatuus.size() == 0){
1955 return null;
1956 }
1957 for (String strFossilStatus : fossilStatuus){
1958 result = CdmUtils.concat(";", result, strFossilStatus);
1959 }
1960 return result;
1961 }
1962
1963 /**
1964 * Returns the <code>IdInSource</code> attribute.
1965 * @param taxonName The {@link TaxonNameBase TaxonName}.
1966 * @return The <code>IdInSource</code> attribute.
1967 * @see MethodMapper
1968 */
1969 @SuppressWarnings("unused")
1970 private static String getIdInSource(IdentifiableEntity taxonName) {
1971 String result = null;
1972
1973 try {
1974 Set<IdentifiableSource> sources = getPesiSources(taxonName);
1975 if (sources.size() > 1){
1976 logger.warn("There is > 1 Pesi source. This is not yet handled.");
1977 }
1978 if (sources.size() == 0){
1979 logger.warn("There is no Pesi source!" +taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
1980 }
1981 for (IdentifiableSource source : sources) {
1982 Reference<?> ref = source.getCitation();
1983 UUID refUuid = ref.getUuid();
1984 String idInSource = source.getIdInSource();
1985 if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed)){
1986 result = idInSource != null ? ("NameId: " + source.getIdInSource()) : null;
1987 }else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
1988 result = idInSource != null ? ("TAX_ID: " + source.getIdInSource()) : null;
1989 }else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
1990 result = idInSource != null ? ("tu_id: " + source.getIdInSource()) : null;
1991 }else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){ //Index Fungorum
1992 result = idInSource != null ? ("if_id: " + source.getIdInSource()) : null;
1993 }else{
1994 if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
1995 }
1996
1997 String sourceIdNameSpace = source.getIdNamespace();
1998 if (sourceIdNameSpace != null) {
1999 if (sourceIdNameSpace.equals(PesiTransformer.STR_NAMESPACE_NOMINAL_TAXON)) {
2000 result = idInSource != null ? ("Nominal Taxon from TAX_ID: " + source.getIdInSource()):null;
2001 } else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_EPITHET_NAMESPACE)) {
2002 result = idInSource != null ? ("Inferred epithet from TAX_ID: " + source.getIdInSource()) : null;
2003 } else if (sourceIdNameSpace.equals(TaxonServiceImpl.INFERRED_GENUS_NAMESPACE)) {
2004 result = idInSource != null ? ("Inferred genus from TAX_ID: " + source.getIdInSource()):null;
2005 } else if (sourceIdNameSpace.equals(TaxonServiceImpl.POTENTIAL_COMBINATION_NAMESPACE)) {
2006 result = idInSource != null ? ("Potential combination from TAX_ID: " + source.getIdInSource()):null;
2007 }
2008 }
2009 if (result == null) {
2010 logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +", sourceIdNameSpace: " + source.getIdNamespace()+")");
2011 }
2012 }
2013 } catch (Exception e) {
2014 e.printStackTrace();
2015 logger.error("An error occurs while creating idInSource..." + taxonName.getUuid() + " (" + taxonName.getTitleCache()+ e.getMessage());
2016 }
2017
2018 if (result == null) {
2019 logger.warn("IdInSource is NULL for this taxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() +")");
2020 }
2021 return result;
2022 }
2023
2024 /**
2025 * Returns the idInSource for a given TaxonName only.
2026 * @param taxonName The {@link TaxonNameBase TaxonName}.
2027 * @return The idInSource.
2028 */
2029 private static String getIdInSourceOnly(IdentifiableEntity identEntity) {
2030 String result = null;
2031
2032 // Get the sources first
2033 Set<IdentifiableSource> sources = getPesiSources(identEntity);
2034
2035 // Determine the idInSource
2036 if (sources.size() == 1) {
2037 IdentifiableSource source = sources.iterator().next();
2038 if (source != null) {
2039 result = source.getIdInSource();
2040 }
2041 } else if (sources.size() > 1) {
2042 int count = 1;
2043 result = "";
2044 for (IdentifiableSource source : sources) {
2045 result += source.getIdInSource();
2046 if (count < sources.size()) {
2047 result += "; ";
2048 }
2049 count++;
2050 }
2051
2052 }
2053
2054 return result;
2055 }
2056
2057 /**
2058 * Returns the Sources for a given TaxonName only.
2059 * @param taxonName The {@link TaxonNameBase TaxonName}.
2060 * @return The Sources.
2061 */
2062 private static Set<IdentifiableSource> getPesiSources(IdentifiableEntity identEntity) {
2063 Set<IdentifiableSource> sources = new java.util.HashSet<IdentifiableSource>();
2064
2065 //Taxon Names
2066 if (identEntity.isInstanceOf(TaxonNameBase.class)){
2067 // Sources from TaxonName
2068 TaxonNameBase taxonName = CdmBase.deproxy(identEntity, TaxonNameBase.class);
2069 Set<IdentifiableSource> testSources = identEntity.getSources();
2070 sources = filterPesiSources(identEntity.getSources());
2071
2072 if (sources.size() == 0 && testSources.size()>0){
2073 IdentifiableSource source = testSources.iterator().next();
2074 logger.warn("There are sources, but they are no pesi sources!!!" + source.getIdInSource() + " - " + source.getIdNamespace() + " - "+source.getCitation().generateTitle());
2075 }
2076 if (sources.size() > 1) {
2077 logger.warn("This TaxonName has more than one Source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() + ")");
2078 }
2079
2080 // name has no PESI source, take sources from TaxonBase
2081 if (sources == null || sources.isEmpty()) {
2082 Set<TaxonBase> taxa = taxonName.getTaxonBases();
2083 for (TaxonBase taxonBase: taxa){
2084 sources.addAll(filterPesiSources(taxonBase.getSources()));
2085 }
2086 }
2087
2088 //for TaxonBases
2089 }else if (identEntity.isInstanceOf(TaxonBase.class)){
2090 sources = filterPesiSources(identEntity.getSources());
2091 }
2092
2093 /*TODO: deleted only for testing the inferred synonyms
2094 if (sources == null || sources.isEmpty()) {
2095 logger.warn("This TaxonName has no PESI Sources: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2096 }else if (sources.size() > 1){
2097 logger.warn("This Taxon(Name) has more than 1 PESI source: " + identEntity.getUuid() + " (" + identEntity.getTitleCache() +")");
2098 }
2099 */
2100 return sources;
2101 }
2102
2103 // return all sources with a PESI reference
2104 private static Set<IdentifiableSource> filterPesiSources(Set<? extends IdentifiableSource> sources) {
2105 Set<IdentifiableSource> result = new HashSet<IdentifiableSource>();
2106 for (IdentifiableSource source : sources){
2107 Reference ref = source.getCitation();
2108 UUID refUuid = ref.getUuid();
2109 if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed) ||
2110 refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)||
2111 refUuid.equals(PesiTransformer.uuidSourceRefErms)||
2112 refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum) ||
2113 refUuid.equals(PesiTransformer.uuidSourceRefAuct)){
2114 result.add(source);
2115 }
2116 }
2117 return result;
2118 }
2119
2120 /**
2121 * Returns the <code>GUID</code> attribute.
2122 * @param taxonName The {@link TaxonNameBase TaxonName}.
2123 * @return The <code>GUID</code> attribute.
2124 * @see MethodMapper
2125 */
2126 private static String getGUID(TaxonBase<?> taxon) {
2127 if (taxon.getLsid() != null ){
2128 return taxon.getLsid().getLsid();
2129 }else if (taxon.hasMarker(PesiTransformer.uuidMarkerGuidIsMissing, true)){
2130 return null;
2131 }else{
2132 return taxon.getUuid().toString();
2133 }
2134 }
2135
2136
2137
2138
2139 /**
2140 * Returns the <code>DerivedFromGuid</code> attribute.
2141 * @param taxonName The {@link TaxonNameBase TaxonName}.
2142 * @return The <code>DerivedFromGuid</code> attribute.
2143 * @see MethodMapper
2144 */
2145 @SuppressWarnings("unused")
2146 private static String getDerivedFromGuid(TaxonBase<?> taxon) {
2147 String result = null;
2148 try {
2149 // The same as GUID for now
2150 result = getGUID(taxon);
2151 } catch (Exception e) {
2152 e.printStackTrace();
2153 }
2154 return result;
2155 }
2156
2157 /**
2158 * Returns the <code>CacheCitation</code> attribute.
2159 * @param taxonName The {@link TaxonNameBase TaxonName}.
2160 * @return The CacheCitation.
2161 * @see MethodMapper
2162 */
2163 @SuppressWarnings("unused")
2164 private static String getCacheCitation(TaxonBase taxon) {
2165 // !!! See also doPhaseUpdates
2166
2167 TaxonNameBase<?,?> taxonName = taxon.getName();
2168 String result = "";
2169 //TODO implement anew for taxa
2170 try {
2171 BitSet sources = getSources(taxonName);
2172 if (sources.isEmpty()) {
2173 // logger.error("OriginalDB is NULL for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2174 } else if (sources.get(PesiTransformer.SOURCE_ERMS)) {
2175 // TODO: 19.08.2010: An import of CacheCitation does not exist in the ERMS import yet or it will be imported in a different way...
2176 // So the following code is some kind of harmless assumption.
2177 Set<Extension> extensions = taxonName.getExtensions();
2178 for (Extension extension : extensions) {
2179 if (extension.getType().equals(cacheCitationExtensionType)) {
2180 result = extension.getValue();
2181 }
2182 }
2183 } else {
2184 String expertName = getExpertName(taxon);
2185 String webShowName = getWebShowName(taxonName);
2186
2187 // idInSource only
2188 String idInSource = getIdInSourceOnly(taxonName);
2189
2190 // build the cacheCitation
2191 if (expertName != null) {
2192 result += expertName + ". ";
2193 } else {
2194 if (logger.isDebugEnabled()){logger.debug("ExpertName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");}
2195 }
2196 if (webShowName != null) {
2197 result += webShowName + ". ";
2198 } else {
2199 logger.warn("WebShowName could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2200 }
2201
2202 if (getOriginalDB(taxonName).equals("FaEu")) {
2203 result += "Accessed through: Fauna Europaea at http://faunaeur.org/full_results.php?id=";
2204 } else if (getOriginalDB(taxonName).equals("EM")) {
2205 result += "Accessed through: Euro+Med PlantBase at http://ww2.bgbm.org/euroPlusMed/PTaxonDetail.asp?UUID=";
2206 }
2207
2208 if (idInSource != null) {
2209 result += idInSource;
2210 } else {
2211 logger.warn("IdInSource could not be determined for this TaxonName: " + taxonName.getUuid() + " (" + taxonName.getTitleCache() + ")");
2212 }
2213 }
2214 } catch (Exception e) {
2215 e.printStackTrace();
2216 }
2217
2218 if (StringUtils.isBlank(result)) {
2219 return null;
2220 } else {
2221 return result;
2222 }
2223 }
2224
2225 /**
2226 * Returns the <code>OriginalDB</code> attribute.
2227 * @param taxonName The {@link TaxonNameBase TaxonName}.
2228 * @return The <code>OriginalDB</code> attribute.
2229 * @see MethodMapper
2230 */
2231 private static String getOriginalDB(IdentifiableEntity identEntity) {
2232 // Sources from TaxonName
2233 BitSet sources = getSources(identEntity);
2234 return PesiTransformer.getOriginalDbBySources(sources);
2235 }
2236
2237 /**
2238 * Returns the <code>LastAction</code> attribute.
2239 * @param taxonName The {@link TaxonNameBase TaxonName}.
2240 * @return The <code>LastAction</code> attribute.
2241 * @see MethodMapper
2242 */
2243 @SuppressWarnings("unused")
2244 private static String getLastAction(IdentifiableEntity<?> identEntity) {
2245 String result = null;
2246 try {
2247 Set<Extension> extensions = identEntity.getExtensions();
2248 for (Extension extension : extensions) {
2249 if (extension.getType().equals(lastActionExtensionType)) {
2250 result = extension.getValue();
2251 }
2252 }
2253 } catch (Exception e) {
2254 e.printStackTrace();
2255 }
2256 return result;
2257 }
2258
2259 /**
2260 * Returns the <code>LastActionDate</code> attribute.
2261 * @param taxonName The {@link TaxonNameBase TaxonName}.
2262 * @return The <code>LastActionDate</code> attribute.
2263 * @see MethodMapper
2264 */
2265 @SuppressWarnings({ "unused" })
2266 private static DateTime getLastActionDate(IdentifiableEntity identEntity) {
2267 DateTime result = null;
2268 try {
2269 Set<Extension> extensions = identEntity.getExtensions();
2270 for (Extension extension : extensions) {
2271 if (extension.getType().equals(lastActionDateExtensionType)) {
2272 String dateTime = extension.getValue();
2273 if (dateTime != null) {
2274 DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
2275 result = formatter.parseDateTime(dateTime);
2276 }
2277 }
2278 }
2279 } catch (Exception e) {
2280 e.printStackTrace();
2281 }
2282 return result;
2283 }
2284
2285 /**
2286 * Returns the <code>ExpertName</code> attribute.
2287 * @param taxonName The {@link TaxonNameBase TaxonName}.
2288 * @return The <code>ExpertName</code> attribute.
2289 * @see MethodMapper
2290 */
2291 @SuppressWarnings("unused")
2292 private static String getExpertName(TaxonBase<?> taxonName) {
2293 String result = null;
2294 try {
2295 Set<Extension> extensions = taxonName.getExtensions();
2296 for (Extension extension : extensions) {
2297 if (extension.getType().equals(expertNameExtensionType)) {
2298 result = extension.getValue();
2299 }
2300 }
2301 } catch (Exception e) {
2302 e.printStackTrace();
2303 }
2304 return result;
2305 }
2306
2307 /**
2308 * Returns the <code>ExpertFk</code> attribute.
2309 * @param taxonName The {@link TaxonNameBase TaxonName}.
2310 * @param state The {@link PesiExportState PesiExportState}.
2311 * @return The <code>ExpertFk</code> attribute.
2312 * @see MethodMapper
2313 */
2314 private static Integer getExpertFk(Reference<?> reference, PesiExportState state) {
2315 Integer result = state.getDbId(reference);
2316 return result;
2317 }
2318
2319 /**
2320 * Returns the <code>SpeciesExpertName</code> attribute.
2321 * @param taxonName The {@link TaxonNameBase TaxonName}.
2322 * @return The <code>SpeciesExpertName</code> attribute.
2323 * @see MethodMapper
2324 */
2325 @SuppressWarnings("unused")
2326 private static String getSpeciesExpertName(TaxonBase<?> taxonName) {
2327 String result = null;
2328 try {
2329 Set<Extension> extensions = taxonName.getExtensions();
2330 for (Extension extension : extensions) {
2331 if (extension.getType().equals(speciesExpertNameExtensionType)) {
2332 result = extension.getValue();
2333 }
2334 }
2335 } catch (Exception e) {
2336 e.printStackTrace();
2337 }
2338 return result;
2339 }
2340
2341 /**
2342 * Returns the <code>SpeciesExpertFk</code> attribute.
2343 * @param reference The {@link Reference Reference}.
2344 * @param state The {@link PesiExportState PesiExportState}.
2345 * @return The <code>SpeciesExpertFk</code> attribute.
2346 * @see MethodMapper
2347 */
2348 private static Integer getSpeciesExpertFk(Reference<?> reference, PesiExportState state) {
2349 Integer result = state.getDbId(reference);
2350 return result;
2351 }
2352
2353
2354 /**
2355 * Returns the source (E+M, Fauna Europaea, Index Fungorum, ERMS) of a given
2356 * Identifiable Entity as a BitSet
2357 * @param identEntity
2358 * @return
2359 */
2360 private static BitSet getSources(IdentifiableEntity<?> identEntity){
2361 BitSet bitSet = new BitSet();
2362 Set<IdentifiableSource> sources = getPesiSources(identEntity);
2363 for (IdentifiableSource source : sources) {
2364 Reference<?> ref = source.getCitation();
2365 UUID refUuid = ref.getUuid();
2366 if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed)){
2367 bitSet.set(PesiTransformer.SOURCE_EM);
2368 }else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
2369 bitSet.set(PesiTransformer.SOURCE_FE);
2370 }else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
2371 bitSet.set(PesiTransformer.SOURCE_ERMS);
2372 }else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){
2373 bitSet.set(PesiTransformer.SOURCE_IF);
2374 }else{
2375 if (logger.isDebugEnabled()){logger.debug("Not a PESI source");};
2376 }
2377 }
2378 return bitSet;
2379
2380 }
2381
2382 protected static NonViralNameDefaultCacheStrategy getCacheStrategy(TaxonNameBase<?, ?> taxonName) {
2383 taxonName = CdmBase.deproxy(taxonName, TaxonNameBase.class);
2384 NonViralNameDefaultCacheStrategy<?> cacheStrategy;
2385 if (taxonName.isInstanceOf(ZoologicalName.class)){
2386 cacheStrategy = zooNameStrategy;
2387 }else if (taxonName.isInstanceOf(BotanicalName.class)) {
2388 cacheStrategy = botanicalNameStrategy;
2389 }else if (taxonName.getClass().equals(NonViralName.class)) {
2390 cacheStrategy = nonViralNameStrategy;
2391 }else if (taxonName.getClass().equals(BacterialName.class)) {
2392 cacheStrategy = bacterialNameStrategy;
2393 }else{
2394 logger.error("Unhandled taxon name type. Can't define strategy class");
2395 cacheStrategy = botanicalNameStrategy;
2396 }
2397 return cacheStrategy;
2398 }
2399
2400 /**
2401 * Returns the <code>TaxonFk1</code> attribute. It corresponds to a CDM <code>TaxonRelationship</code>.
2402 * @param relationship The {@link RelationshipBase Relationship}.
2403 * @param state The {@link PesiExportState PesiExportState}.
2404 * @return The <code>TaxonFk1</code> attribute.
2405 * @see MethodMapper
2406 */
2407 private static Integer getTaxonFk1(RelationshipBase<?, ?, ?> relationship, PesiExportState state) {
2408
2409 return getObjectFk(relationship, state, true);
2410 }
2411
2412 /**
2413 * Returns the <code>TaxonFk2</code> attribute. It corresponds to a CDM <code>SynonymRelationship</code>.
2414 * @param relationship The {@link RelationshipBase Relationship}.
2415 * @param state The {@link PesiExportState PesiExportState}.
2416 * @return The <code>TaxonFk2</code> attribute.
2417 * @see MethodMapper
2418 */
2419 private static Integer getTaxonFk2(RelationshipBase<?, ?, ?> relationship, PesiExportState state) {
2420 return getObjectFk(relationship, state, false);
2421 }
2422
2423 /**
2424 * Returns the database key of an object in the given relationship.
2425 * @param relationship {@link RelationshipBase RelationshipBase}.
2426 * @param state {@link PesiExportState PesiExportState}.
2427 * @param isFrom A boolean value indicating whether the database key of the parent or child in this relationship is searched. <code>true</code> means the child is searched. <code>false</code> means the parent is searched.
2428 * @return The database key of an object in the given relationship.
2429 */
2430 private static Integer getObjectFk(RelationshipBase<?, ?, ?> relationship, PesiExportState state, boolean isFrom) {
2431 TaxonBase<?> taxonBase = null;
2432 if (relationship.isInstanceOf(TaxonRelationship.class)) {
2433 TaxonRelationship tr = (TaxonRelationship)relationship;
2434 taxonBase = (isFrom) ? tr.getFromTaxon(): tr.getToTaxon();
2435 } else if (relationship.isInstanceOf(SynonymRelationship.class)) {
2436 SynonymRelationship sr = (SynonymRelationship)relationship;
2437 taxonBase = (isFrom) ? sr.getSynonym() : sr.getAcceptedTaxon();
2438 } else if (relationship.isInstanceOf(NameRelationship.class) || relationship.isInstanceOf(HybridRelationship.class)) {
2439 if (isFrom){
2440 return state.getDbId(state.getCurrentFromObject());
2441 }else{
2442 return state.getDbId(state.getCurrentToObject());
2443 }
2444 }
2445 if (taxonBase != null) {
2446 if (! isPesiTaxon(taxonBase)){
2447 logger.warn("Related taxonBase is not a PESI taxon. Taxon: " + taxonBase.getId() + "/" + taxonBase.getUuid() + "; TaxonRel: " + relationship.getId() + "(" + relationship.getType().getTitleCache() + ")");
2448 return null;
2449 }else{
2450 return state.getDbId(taxonBase);
2451 }
2452
2453 }
2454 logger.warn("No taxon found in state for relationship: " + relationship.toString());
2455 return null;
2456 }
2457
2458 /**
2459 * Returns the <code>RelQualifierCache</code> attribute.
2460 * @param relationship The {@link RelationshipBase Relationship}.
2461 * @return The <code>RelQualifierCache</code> attribute.
2462 * @see MethodMapper
2463 */
2464 @SuppressWarnings("unused")
2465 private static String getRelQualifierCache(RelationshipBase<?, ?, ?> relationship, PesiExportState state) {
2466 String result = null;
2467 NomenclaturalCode code = null;
2468 if (relationship.isInstanceOf(TaxonRelationship.class)){
2469 code = CdmBase.deproxy(relationship, TaxonRelationship.class).getToTaxon().getName().getNomenclaturalCode();
2470 }else if (relationship.isInstanceOf(SynonymRelationship.class)){
2471 code = CdmBase.deproxy(relationship, SynonymRelationship.class).getAcceptedTaxon().getName().getNomenclaturalCode();
2472 }else if (relationship.isInstanceOf(NameRelationship.class)){
2473 code = CdmBase.deproxy(relationship, NameRelationship.class).getFromName().getNomenclaturalCode();
2474 }else if (relationship.isInstanceOf(HybridRelationship.class)){
2475 code = CdmBase.deproxy(relationship, HybridRelationship.class).getParentName().getNomenclaturalCode();
2476 }
2477 if (code != null) {
2478 result = state.getConfig().getTransformer().getCacheByRelationshipType(relationship, code);
2479 } else {
2480 logger.error("NomenclaturalCode is NULL while creating the following relationship: " + relationship.getUuid());
2481 }
2482 return result;
2483 }
2484
2485 /**
2486 * Returns the <code>RelTaxonQualifierFk</code> attribute.
2487 * @param relationship The {@link RelationshipBase Relationship}.
2488 * @return The <code>RelTaxonQualifierFk</code> attribute.
2489 * @see MethodMapper
2490 */
2491 @SuppressWarnings("unused")
2492 private static Integer getRelTaxonQualifierFk(RelationshipBase<?, ?, ?> relationship) {
2493 return PesiTransformer.taxonRelation2RelTaxonQualifierFk(relationship);
2494 }
2495 /**
2496 * Returns the <code>Notes</code> attribute.
2497 * @param relationship The {@link RelationshipBase Relationship}.
2498 * @return The <code>Notes</code> attribute.
2499 * @see MethodMapper
2500 */
2501 @SuppressWarnings("unused")
2502 private static String getNotes(RelationshipBase<?, ?, ?> relationship) {
2503 // TODO
2504 return null;
2505 }
2506
2507
2508 /**
2509 * Returns the CDM to PESI specific export mappings.
2510 * @return The {@link PesiExportMapping PesiExportMapping}.
2511 */
2512 private PesiExportMapping getMapping() {
2513 PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2514
2515 mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2516 mapping.addMapper(DbObjectMapper.NewInstance("sec", "sourceFk")); //OLD:mapping.addMapper(MethodMapper.NewInstance("SourceFK", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
2517 mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2518 mapping.addMapper(MethodMapper.NewInstance("TaxonStatusCache", this.getClass(), "getTaxonStatusCache", standardMethodParameter, PesiExportState.class));
2519
2520 mapping.addMapper(MethodMapper.NewInstance("GUID", this));
2521
2522 mapping.addMapper(MethodMapper.NewInstance("DerivedFromGuid", this));
2523 mapping.addMapper(MethodMapper.NewInstance("CacheCitation", this));
2524 mapping.addMapper(MethodMapper.NewInstance("AuthorString", this)); //For Taxon because Misallied Names are handled differently
2525 mapping.addMapper(MethodMapper.NewInstance("WebShowName", this));
2526
2527 // DisplayName
2528 mapping.addMapper(MethodMapper.NewInstance("DisplayName", this));
2529
2530 // FossilStatus (Fk, Cache)
2531 mapping.addMapper(MethodMapper.NewInstance("FossilStatusCache", this, IdentifiableEntity.class, PesiExportState.class));
2532 mapping.addMapper(MethodMapper.NewInstance("FossilStatusFk", this, IdentifiableEntity.class, PesiExportState.class)); // PesiTransformer.FossilStatusCache2FossilStatusFk?
2533
2534 //handled by name mapping
2535 mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2536 mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2537
2538 //experts
2539 ExtensionType extensionTypeSpeciesExpertName = (ExtensionType)getTermService().find(PesiTransformer.speciesExpertNameUuid);
2540 mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeSpeciesExpertName, "SpeciesExpertName"));
2541 ExtensionType extensionTypeExpertName = (ExtensionType)getTermService().find(PesiTransformer.expertNameUuid);
2542 mapping.addMapper(DbExtensionMapper.NewInstance(extensionTypeExpertName, "ExpertName"));
2543
2544 // mapping.addMapper(MethodMapper.NewInstance("ParentTaxonFk", this, TaxonBase.class, PesiExportState.class)); //by AM, doesn't work, FK exception
2545 mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonNameBase.class, "Name"));
2546
2547 addNameMappers(mapping);
2548
2549 return mapping;
2550 }
2551
2552 /**
2553 * Returns the CDM to PESI specific export mappings.
2554 * @param state
2555 * @return The {@link PesiExportMapping PesiExportMapping}.
2556 * @throws UndefinedTransformerMethodException
2557 */
2558 private PesiExportMapping getPureNameMapping(PesiExportState state) throws UndefinedTransformerMethodException {
2559 PesiExportMapping mapping = new PesiExportMapping(dbTableName);
2560
2561 mapping.addMapper(IdMapper.NewInstance("TaxonId"));
2562
2563 // mapping.addMapper(MethodMapper.NewInstance("TaxonStatusFk", this.getClass(), "getTaxonStatusFk", standardMethodParameter, PesiExportState.class));
2564
2565 mapping.addMapper(MethodMapper.NewInstance("KingdomFk", this, TaxonNameBase.class));
2566 mapping.addMapper(MethodMapper.NewInstance("RankFk", this, TaxonNameBase.class));
2567 mapping.addMapper(MethodMapper.NewInstance("RankCache", this, TaxonNameBase.class, PesiExportState.class));
2568 mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusFk", Types.INTEGER , PesiTransformer.T_STATUS_UNACCEPTED));
2569 mapping.addMapper(DbConstantMapper.NewInstance("TaxonStatusCache", Types.VARCHAR , state.getTransformer().getTaxonStatusCacheByKey( PesiTransformer.T_STATUS_UNACCEPTED)));
2570 mapping.addMapper(DbStringMapper.NewInstance("AuthorshipCache", "AuthorString").setBlankToNull(true));
2571 mapping.addMapper(MethodMapper.NewInstance("WebShowName", this, TaxonNameBase.class));
2572
2573 // DisplayName
2574 mapping.addMapper(MethodMapper.NewInstance("DisplayName", this, TaxonNameBase.class));
2575
2576 mapping.addMapper(DbLastActionMapper.NewInstance("LastActionDate", false));
2577 mapping.addMapper(DbLastActionMapper.NewInstance("LastAction", true));
2578
2579 addNameMappers(mapping);
2580 //TODO add author mapper, TypeNameFk
2581
2582 return mapping;
2583 }
2584
2585 private void addNameMappers(PesiExportMapping mapping) {
2586 mapping.addMapper(DbStringMapper.NewInstance("GenusOrUninomial", "GenusOrUninomial"));
2587 mapping.addMapper(DbStringMapper.NewInstance("InfraGenericEpithet", "InfraGenericEpithet"));
2588 mapping.addMapper(DbStringMapper.NewInstance("SpecificEpithet", "SpecificEpithet"));
2589 mapping.addMapper(DbStringMapper.NewInstance("InfraSpecificEpithet", "InfraSpecificEpithet"));
2590
2591 // mapping.addMapper(DbStringMapper.NewInstance("NameCache", "WebSearchName")); //does not work as we need other cache strategy
2592 mapping.addMapper(MethodMapper.NewInstance("WebSearchName", this, TaxonNameBase.class));
2593
2594 // mapping.addMapper(DbStringMapper.NewInstance("TitleCache", "FullName")); //does not work as we need other cache strategy
2595 mapping.addMapper(MethodMapper.NewInstance("FullName", this, TaxonNameBase.class));
2596
2597
2598 mapping.addMapper(MethodMapper.NewInstance("NomRefString", this, TaxonNameBase.class));
2599
2600 mapping.addMapper(MethodMapper.NewInstance("NameStatusFk", this, TaxonNameBase.class));
2601 mapping.addMapper(MethodMapper.NewInstance("NameStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2602 mapping.addMapper(MethodMapper.NewInstance("TypeFullnameCache", this, TaxonNameBase.class));
2603 //TODO TypeNameFk
2604
2605 //quality status
2606 mapping.addMapper(MethodMapper.NewInstance("QualityStatusFk", this, TaxonNameBase.class));
2607 mapping.addMapper(MethodMapper.NewInstance("QualityStatusCache", this, TaxonNameBase.class, PesiExportState.class));
2608
2609 mapping.addMapper(MethodMapper.NewInstance("IdInSource", this, IdentifiableEntity.class));
2610 mapping.addMapper(MethodMapper.NewInstance("OriginalDB", this, IdentifiableEntity.class) );
2611
2612 //mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
2613
2614 }
2615
2616 private PesiExportMapping getSynRelMapping() {
2617 PesiExportMapping mapping = new PesiExportMapping(dbTableNameSynRel);
2618
2619 mapping.addMapper(MethodMapper.NewInstance("TaxonFk1", this.getClass(), "getTaxonFk1", RelationshipBase.class, PesiExportState.class));
2620 mapping.addMapper(MethodMapper.NewInstance("TaxonFk2", this.getClass(), "getTaxonFk2", RelationshipBase.class, PesiExportState.class));
2621 mapping.addMapper(MethodMapper.NewInstance("RelTaxonQualifierFk", this, RelationshipBase.class));
2622 mapping.addMapper(MethodMapper.NewInstance("RelQualifierCache", this, RelationshipBase.class, PesiExportState.class));
2623 mapping.addMapper(MethodMapper.NewInstance("Notes", this, RelationshipBase.class));
2624
2625 return mapping;
2626 }
2627
2628 private PesiExportMapping getAdditionalSourceMapping(PesiExportState state) throws UndefinedTransformerMethodException{
2629 PesiExportMapping mapping = new PesiExportMapping(dbTableAdditionalSourceRel);
2630
2631 mapping.addMapper(IdMapper.NewInstance("TaxonFk"));
2632 mapping.addMapper(ObjectChangeMapper.NewInstance(TaxonBase.class, TaxonNameBase.class, "Name"));
2633
2634 mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceFk"));
2635 mapping.addMapper(DbObjectMapper.NewInstance("NomenclaturalReference", "SourceNameCache", IS_CACHE));
2636
2637 //we have only nomenclatural references here
2638 mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER , PesiTransformer.NOMENCLATURAL_REFERENCE));
2639 mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, state.getTransformer().getSourceUseCacheByKey( PesiTransformer.NOMENCLATURAL_REFERENCE)));
2640
2641 mapping.addMapper(DbStringMapper.NewInstance("NomenclaturalMicroReference", "SourceDetail"));
2642
2643 return mapping;
2644 }
2645
2646 }