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