ref #8509 fix missing non-default language notes
[cdmlib-apps.git] / cdm-pesi / src / main / java / eu / etaxonomy / cdm / io / pesi / out / PesiDescriptionExport.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.SQLException;
14 import java.sql.Types;
15 import java.util.Arrays;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Set;
20
21 import org.apache.log4j.Logger;
22 import org.springframework.stereotype.Component;
23 import org.springframework.transaction.TransactionStatus;
24
25 import eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer;
26 import eu.etaxonomy.cdm.io.common.DbExportStateBase;
27 import eu.etaxonomy.cdm.io.common.Source;
28 import eu.etaxonomy.cdm.io.common.TdwgAreaProvider;
29 import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
30 import eu.etaxonomy.cdm.io.common.mapping.out.CollectionExportMapping;
31 import eu.etaxonomy.cdm.io.common.mapping.out.DbAnnotationMapper;
32 import eu.etaxonomy.cdm.io.common.mapping.out.DbAreaMapper;
33 import eu.etaxonomy.cdm.io.common.mapping.out.DbConstantMapper;
34 import eu.etaxonomy.cdm.io.common.mapping.out.DbDescriptionElementTaxonMapper;
35 import eu.etaxonomy.cdm.io.common.mapping.out.DbDistributionStatusMapper;
36 import eu.etaxonomy.cdm.io.common.mapping.out.DbExportIgnoreMapper;
37 import eu.etaxonomy.cdm.io.common.mapping.out.DbLanguageMapper;
38 import eu.etaxonomy.cdm.io.common.mapping.out.DbObjectMapper;
39 import eu.etaxonomy.cdm.io.common.mapping.out.DbOriginalNameMapper;
40 import eu.etaxonomy.cdm.io.common.mapping.out.DbSimpleFilterMapper;
41 import eu.etaxonomy.cdm.io.common.mapping.out.DbStringMapper;
42 import eu.etaxonomy.cdm.io.common.mapping.out.DbTextDataMapper;
43 import eu.etaxonomy.cdm.io.common.mapping.out.IdMapper;
44 import eu.etaxonomy.cdm.io.common.mapping.out.MethodMapper;
45 import eu.etaxonomy.cdm.io.pesi.erms.ErmsTransformer;
46 import eu.etaxonomy.cdm.model.common.CdmBase;
47 import eu.etaxonomy.cdm.model.common.Extension;
48 import eu.etaxonomy.cdm.model.common.ExtensionType;
49 import eu.etaxonomy.cdm.model.common.Language;
50 import eu.etaxonomy.cdm.model.common.LanguageString;
51 import eu.etaxonomy.cdm.model.common.Marker;
52 import eu.etaxonomy.cdm.model.common.MarkerType;
53 import eu.etaxonomy.cdm.model.description.CommonTaxonName;
54 import eu.etaxonomy.cdm.model.description.DescriptionBase;
55 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
56 import eu.etaxonomy.cdm.model.description.Distribution;
57 import eu.etaxonomy.cdm.model.description.Feature;
58 import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
59 import eu.etaxonomy.cdm.model.description.TaxonInteraction;
60 import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
61 import eu.etaxonomy.cdm.model.description.TextData;
62 import eu.etaxonomy.cdm.model.location.NamedArea;
63 import eu.etaxonomy.cdm.model.media.Media;
64 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
65 import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
66 import eu.etaxonomy.cdm.model.name.TaxonName;
67 import eu.etaxonomy.cdm.model.taxon.Taxon;
68 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
69 import eu.etaxonomy.cdm.profiler.ProfilerController;
70 /**
71 * The export class for {@link eu.etaxonomy.cdm.model.description.DescriptionElementBase DescriptionElements}.<p>
72 * Inserts into DataWarehouse database table <code>Note</code>.<p>
73 * It is divided into two phases:<ul>
74 * <li>Phase 1: Export of DescriptionElements as Notes.
75 * <li>Phase 2: Export of TaxonName extensions <code>taxComment</code>, <code>fauComment</code> and <code>fauExtraCodes</code> as Notes.</ul>
76 * @author e.-m.lee
77 * @since 23.02.2010
78 * @author a.mueller
79 */
80 @Component
81 public class PesiDescriptionExport extends PesiExportBase {
82
83 private static final long serialVersionUID = -1486235807814098217L;
84 private static final Logger logger = Logger.getLogger(PesiDescriptionExport.class);
85
86 private static final Class<? extends CdmBase> standardMethodParameter = DescriptionElementBase.class;
87
88 private static int modCount = 1000;
89 private static final String dbNoteTableName = "Note";
90 private static final String dbOccurrenceTableName = "Occurrence";
91 private static final String dbVernacularTableName = "CommonName";
92 private static final String dbImageTableName = "Image";
93 private static final String dbAdditionalSourceTableName = "AdditionalTaxonSource";
94 private static final String pluralString = "descriptions";
95 private static final String parentPluralString = "Taxa";
96
97 //decide where to handle them best (configurator, transformer, single method, ...)
98 private static Set<Integer> excludedNoteCategories = new HashSet<>(Arrays.asList(new Integer[]{250,251,252,253,10,11,13}));
99
100 //debugging
101 private static int countDescriptions;
102 private static int countTaxa;
103 private static int countDistribution;
104 private static int countAdditionalSources;
105 private static int countImages;
106 private static int countNotes;
107 private static int countSourceOfSynonymy;
108
109 private static int countCommonName;
110 private static int countOccurrence;
111 private static int countOthers;
112
113 public PesiDescriptionExport() {
114 super();
115 }
116
117 @Override
118 public Class<? extends CdmBase> getStandardMethodParameter() {
119 return standardMethodParameter;
120 }
121
122 @Override
123 protected void doInvoke(PesiExportState state) {
124 try {
125 logger.info("*** Started Making " + pluralString + " ...");
126
127 // Stores whether this invoke was successful or not.
128 boolean success = true;
129
130 success &= doDelete(state);
131
132 // Get specific mappings: (CDM) DescriptionElement -> (PESI) Note
133 PesiExportMapping notesMapping = getNotesMapping();
134 notesMapping.initialize(state);
135
136 // Get specific mappings: (CDM) DescriptionElement -> (PESI) Occurrence
137 PesiExportMapping occurrenceMapping = getOccurrenceMapping();
138 occurrenceMapping.initialize(state);
139
140 // Get specific mappings: (CDM) DescriptionElement -> (PESI) Additional taxon source
141 PesiExportMapping addSourceSourceMapping = getAddTaxonSourceSourceMapping();
142 addSourceSourceMapping.initialize(state);
143 PesiExportMapping additionalSourceMapping = getAdditionalTaxonSourceMapping();
144 additionalSourceMapping.initialize(state);
145
146 // Get specific mappings: (CDM) DescriptionElement -> (PESI) Common name
147
148 PesiExportMapping vernacularMapping = getCommonNamesMapping();
149 vernacularMapping.initialize(state);
150
151 // Get specific mappings: (CDM) DescriptionElement -> (PESI) Image
152 PesiExportMapping imageMapping = getImageMapping();
153 imageMapping.initialize(state);
154
155 // Taxon Descriptions
156 success &= doPhase01(state, notesMapping, occurrenceMapping, addSourceSourceMapping, additionalSourceMapping, vernacularMapping, imageMapping);
157
158 // Name Descriptions
159 success &= doPhase01b(state, notesMapping, occurrenceMapping, addSourceSourceMapping, additionalSourceMapping, vernacularMapping, imageMapping);
160
161 logger.info("PHASE 2...");
162 success &= doPhase02(state);
163
164 logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
165
166 if (!success){
167 state.getResult().addError("An unknown problem occurred");
168 }
169 return;
170 } catch (SQLException e) {
171 e.printStackTrace();
172 logger.error(e.getMessage());
173 state.getResult().addException(e, e.getMessage());
174 }
175 }
176
177 //PHASE 01: Description Elements
178 private boolean doPhase01(PesiExportState state, PesiExportMapping notesMapping, PesiExportMapping occurrenceMapping, PesiExportMapping addSourceSourceMapping,
179 PesiExportMapping additionalSourceMapping, PesiExportMapping vernacularMapping, PesiExportMapping imageMapping) throws SQLException {
180
181 // System.out.println("PHASE 1 of description import");
182 logger.info("PHASE 1...");
183 int count = 0;
184 int pastCount = 0;
185 boolean success = true;
186 //int limit = state.getConfig().getLimitSave();
187 int limit = 1000;
188
189 List<Taxon> taxonList = null;
190
191 TransactionStatus txStatus = startTransaction(true);
192
193 if (logger.isDebugEnabled()){
194 logger.info("Started new transaction. Fetching some " + parentPluralString + " (max: " + limit + ") ...");
195 logger.debug("Start snapshot, before starting loop");
196 ProfilerController.memorySnapshot();
197 }
198
199 List<String> propPath = Arrays.asList(new String[]{"descriptions.elements.*"});
200 int partitionCount = 0;
201 while ((taxonList = getNextTaxonPartition(Taxon.class, limit, partitionCount++, propPath )) != null ) {
202
203 if (logger.isDebugEnabled()) {
204 logger.info("Fetched " + taxonList.size() + " " + parentPluralString + ". Exporting...");
205 }
206
207 for (Taxon taxon : taxonList) {
208 countTaxa++;
209 doCount(count++, modCount, parentPluralString);
210 state.setCurrentTaxon(taxon);
211 if (!taxon.getDescriptions().isEmpty()){
212 success &= handleSingleTaxon(taxon, state, notesMapping, occurrenceMapping, addSourceSourceMapping,
213 additionalSourceMapping, vernacularMapping, imageMapping);
214 }
215 }
216 taxonList = null;
217 state.setCurrentTaxon(null);
218
219 // Commit transaction
220 commitTransaction(txStatus);
221 logger.info("Exported " + (count - pastCount) + " " + parentPluralString + ". Total: " + count + " (Phase 01)");
222 pastCount = count;
223 ProfilerController.memorySnapshot();
224 // Start transaction
225 txStatus = startTransaction(true);
226 if(logger.isDebugEnabled()) {
227 logger.info("Started new transaction. Fetching some " + parentPluralString + " (max: " + limit + ") for description import ...");
228 }
229 }
230
231 logger.info("No " + parentPluralString + " left to fetch.");
232 logger.info("Partition: " + partitionCount);
233 logger.info("Taxa: " + countTaxa);
234 logger.info("Desc: " + countDescriptions);
235 logger.info("Distr: " + countDistribution);
236 logger.info("Occur: " + countOccurrence);
237 logger.info("Commons: " + countCommonName);
238 logger.info("AddSrc: " + countAdditionalSources);
239 logger.info("Images: " + countImages);
240 logger.info("Notes: " + countNotes);
241 logger.info("Source of Synonymy: " + countSourceOfSynonymy);
242 logger.info("Others: " + countOthers);
243
244 // Commit transaction
245 commitTransaction(txStatus);
246 logger.debug("Committed transaction.");
247 return success;
248 }
249
250 //PHASE 01b: Name Descriptions
251 private boolean doPhase01b(PesiExportState state, PesiExportMapping notesMapping, PesiExportMapping occurrenceMapping, PesiExportMapping addSourceSourceMapping,
252 PesiExportMapping additionalSourceMapping, PesiExportMapping vernacularMapping, PesiExportMapping imageMapping) throws SQLException {
253 logger.info("PHASE 1b...");
254 int count = 0;
255 int pastCount = 0;
256 boolean success = true;
257 //int limit = state.getConfig().getLimitSave();
258 int limit = 2000;
259 List<TaxonNameDescription> nameDescList = null;
260
261 TransactionStatus txStatus = startTransaction(true);
262 logger.info("Started new transaction. Fetching some name descriptions (max: " + limit + ") ...");
263 List<String> propPath = Arrays.asList(new String[]{"descriptions.elements.*"});
264
265 //name descriptions
266 int partitionCount = 0;
267 while ((nameDescList = getNextNameDescriptionPartition( limit, partitionCount++, propPath )) != null ) {
268
269 logger.info("Fetched " + nameDescList.size() + " name descriptions. Exporting...");
270
271 for (TaxonNameDescription desc : nameDescList) {
272 countTaxa++;
273 doCount(count++, modCount, "name descriptions");
274 boolean isImageGallery = desc.isImageGallery();
275
276 TaxonName name = desc.getTaxonName();
277
278 for (DescriptionElementBase element : desc.getElements()){
279 if (isPurePesiName(name)){
280 success &= handleDescriptionElement(state, notesMapping, occurrenceMapping, vernacularMapping, imageMapping,
281 addSourceSourceMapping, additionalSourceMapping, isImageGallery, element);
282 }else{
283 for (TaxonBase<?> taxonBase : name.getTaxonBases()){
284 if (isPesiTaxon(taxonBase)){
285 state.setCurrentTaxon(taxonBase);
286 success &= handleDescriptionElement(state, notesMapping, occurrenceMapping, vernacularMapping, imageMapping,
287 addSourceSourceMapping, additionalSourceMapping, isImageGallery, element);
288 state.setSourceForAdditionalSourceCreated(true);
289 }
290 }
291 state.setSourceForAdditionalSourceCreated(false);
292 }
293 }
294 }
295 nameDescList = null;
296 state.setCurrentTaxon(null);
297
298 // Commit transaction
299 commitTransaction(txStatus);
300 logger.info("Exported " + (count - pastCount) + " name descriptions. Total: " + count);
301 pastCount = count;
302
303 // Start transaction
304 txStatus = startTransaction(true);
305 logger.info("Started new transaction. Fetching some name descriptions (max: " + limit + ") for description import ...");
306 }
307
308 logger.info("No " + parentPluralString + " left to fetch.");
309 logger.info("Partition: " + partitionCount);
310 logger.info("Taxa: " + countTaxa);
311 logger.info("Desc: " + countDescriptions);
312 logger.info("Distr: " + countDistribution);
313 logger.info("Occur: " + countOccurrence);
314 logger.info("Commons: " + countCommonName);
315 logger.info("AddSrc: " + countAdditionalSources);
316 logger.info("Images: " + countImages);
317 logger.info("Notes: " + countNotes);
318 logger.info("Source of Synonymy: " + countSourceOfSynonymy);
319 logger.info("Others: " + countOthers);
320
321 // Commit transaction
322 commitTransaction(txStatus);
323 logger.debug("Committed transaction.");
324 return success;
325 }
326
327 private boolean handleSingleTaxon(Taxon taxon, PesiExportState state, PesiExportMapping notesMapping, PesiExportMapping occurrenceMapping,
328 PesiExportMapping addSourceSourceMapping, PesiExportMapping additionalSourceMapping,
329 PesiExportMapping vernacularMapping, PesiExportMapping imageMapping) throws SQLException {
330
331 boolean success = true;
332
333 Set<DescriptionBase<?>> descriptions = new HashSet<>();
334 descriptions.addAll(taxon.getDescriptions());
335
336 for (DescriptionBase<?> desc : descriptions){
337 boolean isImageGallery = desc.isImageGallery();
338 for (DescriptionElementBase element : desc.getElements()){
339 success &= handleDescriptionElement(state, notesMapping, occurrenceMapping, vernacularMapping, imageMapping,
340 addSourceSourceMapping, additionalSourceMapping, isImageGallery, element);
341 countDescriptions++;
342 }
343 }
344 if (logger.isDebugEnabled()) {
345 logger.info("number of handled decriptionelements " + countDescriptions);
346 }
347 descriptions = null;
348 return success;
349 }
350
351 private boolean handleDescriptionElement(PesiExportState state, PesiExportMapping notesMapping,
352 PesiExportMapping occurrenceMapping, PesiExportMapping vernacularMapping, PesiExportMapping imageMapping,
353 PesiExportMapping addSourceSourceMapping, PesiExportMapping additionalSourceMapping, boolean isImageGallery, DescriptionElementBase element) throws SQLException {
354
355 try {
356 boolean success = true;
357 if (isImageGallery){
358 for (Media media : element.getMedia()){
359 countImages++;
360 success &= imageMapping.invoke(media);
361 }
362 }else if (isCommonName(element)){
363 countCommonName++;
364 if (element.isInstanceOf(TextData.class)){
365 //we do not import text data common names
366 }else{
367 success &= vernacularMapping.invoke(element);
368 }
369 }else if (isOccurrence(element)){
370 countOccurrence++;
371 Distribution distribution = CdmBase.deproxy(element, Distribution.class);
372 MarkerType markerType = getUuidMarkerType(PesiTransformer.uuidMarkerTypeHasNoLastAction, state);
373 distribution.addMarker(Marker.NewInstance(markerType, true));
374 if (isPesiDistribution(state, distribution)){
375 countDistribution++;
376 try{
377 success &=occurrenceMapping.invoke(distribution);
378 }catch(Exception e){
379 System.err.println(distribution.getInDescription().getTitleCache());
380 e.printStackTrace();
381 }
382 }else{
383 logger.warn("Distribution is not PESI distribution");
384 }
385 }else if (isAdditionalTaxonSource(element)){
386 countAdditionalSources++;
387 if (! state.isSourceForAdditionalSourceCreated()){
388 success &= addSourceSourceMapping.invoke(element);
389 }
390 success &= additionalSourceMapping.invoke(element);
391 }else if (isExcludedNote(element)){
392 //do nothing
393 }else if (isPesiNote(element)){
394 countNotes++;
395 success &= notesMapping.invoke(element);
396
397 }else if (isSourceOfSynonymy(element)){
398 countSourceOfSynonymy++;
399 if(countSourceOfSynonymy < 2){
400 logger.warn("Source of synonymy not yet implemented!");
401 }
402 }else{
403 countOthers++;
404 String featureTitle = element.getFeature() == null ? "no feature" :element.getFeature().getTitleCache();
405 logger.warn("Description element type not yet handled by PESI export: " + element.getUuid() + ", " + element.getClass() + ", " + featureTitle);
406 }
407 return success;
408 } catch (Exception e) {
409 logger.warn("Exception appeared in description element handling: " + e);
410 e.printStackTrace();
411 return false;
412 }
413 }
414
415 private boolean isSourceOfSynonymy(DescriptionElementBase element) {
416 if (element.getFeature() == null){
417 return false;
418 }else {
419 return element.getFeature().getUuid().equals(ErmsTransformer.uuidSourceOfSynonymy);
420 }
421 }
422
423 private boolean isExcludedNote(DescriptionElementBase element) {
424 Integer categoryFk = PesiTransformer.feature2NoteCategoryFk(element.getFeature());
425 //TODO decide where to handle them best (configurator, transformer, single method, ...)
426 return (excludedNoteCategories.contains(categoryFk));
427 }
428
429 private boolean isPesiDistribution(PesiExportState state, Distribution distribution) {
430 //currently we use the E+M summary status to decide if a distribution should be exported
431 if (distribution.getStatus() == null){
432 return false;
433 }
434
435 //...this may change in future so we keep the following code
436 Integer key;
437 //area filter
438 NamedArea area = distribution.getArea();
439 if (area == null){
440 logger.warn("Area is null for distribution " + distribution.getUuid());
441 return false;
442 }else if (area.getUuid().equals(BerlinModelTransformer.euroMedUuid)){
443 //E+M area only holds endemic status information and therefore is not exported to PESI
444 return false;
445 }else if (area.equals(TdwgAreaProvider.getAreaByTdwgAbbreviation("1"))){
446 //Europe area never holds status information (may probably be deleted in E+M)
447 return false;
448 // }else if (area.equals(TdwgArea.getAreaByTdwgAbbreviation("21"))){
449 // //Macaronesia records should not be exported to PESI
450 // return false;
451 // //TODO exclude Russion areas Rs*, and maybe ohters
452
453 } else {
454 try {
455 if (state.getTransformer().getKeyByNamedArea(area) == null){
456 String warning = "Area (%s,%s) not available in PESI transformer for taxon %S: ";
457 TaxonBase<?> taxon = state.getCurrentTaxon();
458 warning = String.format(warning, area.getTitleCache(), area.getRepresentation(Language.ENGLISH()).getAbbreviatedLabel(),taxon ==null? "-" : taxon.getTitleCache());
459 logger.warn(warning);
460 return false;
461 }
462 } catch (UndefinedTransformerMethodException e1) {
463 logger.warn("Area not available in PESI transformer " + area.getTitleCache());
464 return false;
465 }
466 }
467 return true;
468 }
469
470 private boolean isPesiNote(DescriptionElementBase element) {
471 return (getNoteCategoryFk(element) != null);
472 }
473
474 private boolean isAdditionalTaxonSource(DescriptionElementBase element) {
475 Feature feature = element.getFeature();
476 if (feature == null){
477 return false;
478 }
479 return (feature.equals(Feature.CITATION()) || feature.equals(Feature.ADDITIONAL_PUBLICATION()));
480 }
481
482 private boolean isOccurrence(DescriptionElementBase element) {
483 Feature feature = element.getFeature();
484 if (element.isInstanceOf(Distribution.class)){
485 if (!Feature.DISTRIBUTION().equals(feature)){
486 logger.warn("Description element has class 'Distribution' but has no feature 'Distribution'");
487 }
488 return true;
489 }else if (Feature.DISTRIBUTION().equals(feature)){
490 logger.debug("Description element has feature Distribtuion but is not of class 'Distribution'");
491 return false;
492 }else{
493 return false;
494 }
495 }
496
497 private boolean isCommonName(DescriptionElementBase element) {
498 Feature feature = element.getFeature();
499 if (feature == null){
500 return false;
501 }
502 return (feature.equals(Feature.COMMON_NAME()));
503 }
504
505 //PHASE 02: Name extensions
506 private boolean doPhase02(PesiExportState state) {
507 TransactionStatus txStatus;
508 boolean success = true;
509
510 // Get the limit for objects to save within a single transaction.
511 //int limit = state.getConfig().getLimitSave();
512 int limit = 2000;
513 txStatus = startTransaction(true);
514 ExtensionType taxCommentExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtTaxComment);
515 ExtensionType fauCommentExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtFauComment);
516 ExtensionType fauExtraCodesExtensionType = (ExtensionType)getTermService().find(PesiTransformer.uuidExtFauExtraCodes);
517 List<TaxonName> taxonNameList;
518
519 int count = 0;
520 int pastCount = 0;
521 Connection connection = state.getConfig().getDestination().getConnection();
522 if (logger.isDebugEnabled()) {
523 logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
524 }
525 logger.warn("TODO handle extensions on taxon level, not name level (");
526 while ((taxonNameList = getNameService().list(null, limit, count, null, null)).size() > 0) {
527
528 if(logger.isDebugEnabled()) {
529 logger.info("Fetched " + taxonNameList.size() + " names. Exporting...");
530 }
531 for (TaxonName taxonName : taxonNameList) {
532 Set<Extension> extensions = taxonName.getExtensions();
533 for (Extension extension : extensions) {
534 if (extension.getType().equals(taxCommentExtensionType)) {
535 String taxComment = extension.getValue();
536 invokeNotes(taxComment,
537 PesiTransformer.getNoteCategoryFk(PesiTransformer.uuidExtTaxComment),
538 PesiTransformer.getNoteCategoryCache(PesiTransformer.uuidExtTaxComment),
539 null, null, getTaxonKey(taxonName, state),connection);
540 } else if (extension.getType().equals(fauCommentExtensionType)) {
541 String fauComment = extension.getValue();
542 invokeNotes(fauComment,
543 PesiTransformer.getNoteCategoryFk(PesiTransformer.uuidExtFauComment),
544 PesiTransformer.getNoteCategoryCache(PesiTransformer.uuidExtFauComment),
545 null, null, getTaxonKey(taxonName, state),connection);
546 } else if (extension.getType().equals(fauExtraCodesExtensionType)) {
547 String fauExtraCodes = extension.getValue();
548 invokeNotes(fauExtraCodes,
549 PesiTransformer.getNoteCategoryFk(PesiTransformer.uuidExtFauExtraCodes),
550 PesiTransformer.getNoteCategoryCache(PesiTransformer.uuidExtFauExtraCodes),
551 null, null, getTaxonKey(taxonName, state),connection);
552 }
553 }
554
555 doCount(count++, modCount, parentPluralString);
556 }
557
558 // Commit transaction
559 commitTransaction(txStatus);
560 logger.debug("Committed transaction.");
561 logger.info("Exported " + (count - pastCount) + " names. Total: " + count + " (Phase 02)");
562 pastCount = count;
563
564 // Start transaction
565 txStatus = startTransaction(true);
566 if (logger.isDebugEnabled()) {
567 logger.info("Started new transaction. Fetching some names first (max: " + limit + ") ...");
568 }
569 }
570 // Commit transaction
571 commitTransaction(txStatus);
572 logger.debug("Committed transaction.");
573 return success;
574 }
575
576 private void invokeNotes(String note, Integer noteCategoryFk,
577 String noteCategoryCache, Integer languageFk, String languageCache,
578 Integer taxonFk, Connection connection) {
579
580 String notesSql = "UPDATE Note SET Note_1 = ?, NoteCategoryFk = ?, NoteCategoryCache = ?, LanguageFk = ?, LanguageCache = ? WHERE TaxonFk = ?";
581 try {
582 PreparedStatement notesStmt = connection.prepareStatement(notesSql);
583
584 if (note != null) {
585 notesStmt.setString(1, note);
586 } else {
587 notesStmt.setObject(1, null);
588 }
589
590 if (noteCategoryFk != null) {
591 notesStmt.setInt(2, noteCategoryFk);
592 } else {
593 notesStmt.setObject(2, null);
594 }
595
596 if (noteCategoryCache != null) {
597 notesStmt.setString(3, noteCategoryCache);
598 } else {
599 notesStmt.setObject(3, null);
600 }
601
602 if (languageFk != null) {
603 notesStmt.setInt(4, languageFk);
604 } else {
605 notesStmt.setObject(4, null);
606 }
607
608 if (languageCache != null) {
609 notesStmt.setString(5, languageCache);
610 } else {
611 notesStmt.setObject(5, null);
612 }
613
614 if (taxonFk != null) {
615 notesStmt.setInt(6, taxonFk);
616 } else {
617 notesStmt.setObject(6, null);
618 }
619
620 notesStmt.executeUpdate();
621 } catch (SQLException e) {
622 logger.error("Note could not be created: " + note);
623 e.printStackTrace();
624 }
625 }
626
627 /**
628 * Deletes all entries of database tables related to <code>Note</code>.
629 * @param state The PesiExportState
630 * @return Whether the delete operation was successful or not.
631 */
632 protected boolean doDelete(PesiExportState state) {
633 Source destination = state.getConfig().getDestination();
634
635 // Clear NoteSource
636 String sql = "DELETE FROM NoteSource";
637 destination.update(sql);
638 // Clear Note
639 sql = "DELETE FROM Note "; // + dbNoteTableName;
640 destination.update(sql);
641
642 // Clear OccurrenceSource
643 sql = "DELETE FROM OccurrenceSource ";
644 destination.update(sql);
645 // Clear Occurrence
646 sql = "DELETE FROM Occurrence ";
647 destination.update(sql);
648
649 // Clear Image
650 sql = "DELETE FROM Image ";
651 destination.update(sql);
652
653 // Clear CommonName
654 sql = "DELETE FROM CommonNameSource ";
655 destination.update(sql);
656 sql = "DELETE FROM CommonName ";
657 destination.update(sql);
658
659 // Clear AdditionalTaxonSource
660 sql = "DELETE FROM AdditionalTaxonSource WHERE SourceFk >= 2000000 ";
661 destination.update(sql);
662
663 // Clear Sources for AdditionalTaxonSource
664 sql = "DELETE FROM Source WHERE SourceId >= 2000000 ";
665 destination.update(sql);
666
667 return true;
668 }
669
670 /**
671 * Returns the <code>Note_2</code> attribute.
672 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
673 * @return The <code>Note_2</code> attribute.
674 * @see MethodMapper
675 */
676 @SuppressWarnings("unused") //used for mapper
677 private static String getNote_2(DescriptionElementBase element) {
678 //E+M map links -> medium
679 if (element.getFeature() != null && element.getFeature().getUuid().equals(BerlinModelTransformer.uuidFeatureMaps)){
680 String text = CdmBase.deproxy(element, TextData.class).getText(Language.ENGLISH());
681 if (text.contains("medium")){
682 return "medium";
683 }
684 }
685 return null;
686 }
687
688 /**
689 * Returns the <code>NoteCategoryFk</code> attribute.
690 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
691 * @return The <code>NoteCategoryFk</code> attribute.
692 * @see MethodMapper
693 */
694 private static Integer getNoteCategoryFk(DescriptionElementBase descriptionElement) {
695 Integer result = null;
696 result = PesiTransformer.feature2NoteCategoryFk(descriptionElement.getFeature());
697 //TODO decide where to handle them best (configurator, transformer, single method, ...)
698 if (excludedNoteCategories.contains(result)){
699 result = null;
700 }
701 return result;
702 }
703
704 /**
705 * Returns the <code>NoteCategoryCache</code> attribute.
706 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
707 * @return The <code>NoteCategoryCache</code> attribute.
708 * @see MethodMapper
709 */
710 @SuppressWarnings("unused")
711 private static String getNoteCategoryCache(DescriptionElementBase descriptionElement, PesiExportState state) {
712 return state.getTransformer().getCacheByFeature(descriptionElement.getFeature());
713 }
714
715 /**
716 * Returns the <code>LanguageFk</code> attribute.
717 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
718 * @return The <code>LanguageFk</code> attribute.
719 * @see MethodMapper
720 */
721 @SuppressWarnings("unused")
722 private static Integer getLanguageFk(DescriptionElementBase descriptionElement) {
723 Language language = getLanguage(descriptionElement);
724
725 return PesiTransformer.language2LanguageId(language);
726 }
727
728 /**
729 * Returns the <code>LanguageCache</code> attribute.
730 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
731 * @return The <code>LanguageCache</code> attribute.
732 * @throws UndefinedTransformerMethodException
733 * @see MethodMapper
734 */
735 @SuppressWarnings("unused")
736 private static String getLanguageCache(DescriptionElementBase descriptionElement, PesiExportState state) throws UndefinedTransformerMethodException {
737 Language language = getLanguage(descriptionElement);
738 return state.getTransformer().getCacheByLanguage(language);
739 }
740
741 private static Language getLanguage(DescriptionElementBase descriptionElement) {
742 Language language = null;
743
744 Map<Language, LanguageString> multilanguageText = null;
745 if (descriptionElement.isInstanceOf(CommonTaxonName.class)) {
746 CommonTaxonName commonTaxonName = CdmBase.deproxy(descriptionElement, CommonTaxonName.class);
747 language = commonTaxonName.getLanguage();
748 } else if (descriptionElement.isInstanceOf(TextData.class)) {
749 TextData textData = CdmBase.deproxy(descriptionElement, TextData.class);
750 multilanguageText = textData.getMultilanguageText();
751 } else if (descriptionElement.isInstanceOf(IndividualsAssociation.class)) {
752 IndividualsAssociation individualsAssociation = CdmBase.deproxy(descriptionElement, IndividualsAssociation.class);
753 multilanguageText = individualsAssociation.getDescription();
754 } else if (descriptionElement.isInstanceOf(TaxonInteraction.class)) {
755 TaxonInteraction taxonInteraction = CdmBase.deproxy(descriptionElement, TaxonInteraction.class);
756 multilanguageText = taxonInteraction.getDescription();
757 } else {
758 logger.debug("Given descriptionElement does not support languages. Hence LanguageCache could not be determined: " + descriptionElement.getUuid());
759 }
760
761 if (multilanguageText != null) {
762 Set<Language> languages = multilanguageText.keySet();
763
764 // TODO: Think of something more sophisticated than this
765 if (languages.size() > 0) {
766 language = languages.iterator().next();
767 }
768 if (languages.size() > 1){
769 logger.warn("There is more than 1 language for a given description (" + descriptionElement.getClass().getSimpleName() + "):" + descriptionElement.getUuid());
770 }
771 }
772 return language;
773 }
774
775 // /**
776 // * Returns the <code>Region</code> attribute.
777 // * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
778 // * @return The <code>Region</code> attribute.
779 // * @see MethodMapper
780 // */
781 // @SuppressWarnings("unused")
782 // private static String getRegion(DescriptionElementBase descriptionElement) {
783 // String result = null;
784 // DescriptionBase<?> inDescription = descriptionElement.getInDescription();
785 //
786 // // Area information are associated to TaxonDescriptions and Distributions.
787 // if (descriptionElement.isInstanceOf(Distribution.class)) {
788 // Distribution distribution = CdmBase.deproxy(descriptionElement, Distribution.class);
789 // result = PesiTransformer.area2AreaCache(distribution.getArea());
790 // } else if (inDescription != null && inDescription.isInstanceOf(TaxonDescription.class)) {
791 // TaxonDescription taxonDescription = CdmBase.deproxy(inDescription, TaxonDescription.class);
792 // Set<NamedArea> namedAreas = taxonDescription.getGeoScopes();
793 // if (namedAreas.size() == 1) {
794 // result = PesiTransformer.area2AreaCache(namedAreas.iterator().next());
795 // } else if (namedAreas.size() > 1) {
796 // logger.warn("This TaxonDescription contains more than one NamedArea: " + taxonDescription.getTitleCache());
797 // }
798 // }
799 // return result;
800 // }
801
802
803 /**
804 * @param state The {@link DbExportStateBase DbExportState}.
805 * @return
806 */
807 @SuppressWarnings("unused") //used by mapper
808 private static Integer getTaxonFk(DescriptionElementBase deb, PesiExportState state) {
809 TaxonBase<?> entity = state.getCurrentTaxon();
810 return state.getDbId(entity);
811 }
812
813 /**
814 * Returns the TaxonFk for a given TaxonName.
815 * @param taxonName The {@link TaxonNameBase TaxonName}.
816 * @param state The {@link DbExportStateBase DbExportState}.
817 * @return
818 */
819 private static Integer getTaxonKey(TaxonName taxonName, DbExportStateBase<?, PesiTransformer> state) {
820 return state.getDbId(taxonName);
821 }
822
823 /**
824 * Returns the <code>FullName</code> attribute.
825 * @param taxonName The {@link NonViralName NonViralName}.
826 * @return The <code>FullName</code> attribute.
827 * @see MethodMapper
828 */
829 @SuppressWarnings("unused")
830 private static String getTaxonFullNameCache(DescriptionElementBase deb, PesiExportState state) {
831
832 TaxonBase<?> taxon = state.getCurrentTaxon();
833 TaxonName taxonName = taxon.getName();
834 TaxonName nvn = CdmBase.deproxy(taxonName);
835 String result = getCacheStrategy(nvn).getTitleCache(nvn);
836 return result;
837 }
838
839 @SuppressWarnings("unused") //used by mapper
840 private static Integer getCurrentTaxonFk(Media media, PesiExportState state) {
841 return state.getDbId(state.getCurrentTaxon());
842 }
843
844 @SuppressWarnings("unused") //used by mapper
845 private static String getMediaThumb(Media media) {
846 String startsWith = "http://images.vliz.be/thumbs/";
847 String result = null;
848 for (MediaRepresentation rep : media.getRepresentations()){
849 for (MediaRepresentationPart part : rep.getParts()){
850 String strUrl = part.getUri().toString();
851 if (strUrl.startsWith(startsWith)){
852 result = part.getUri().toString();
853 }
854 }
855 }
856 return result;
857 }
858
859 @SuppressWarnings("unused") //used by mapper
860 private static String getMediaUrl(Media media) {
861 String startsWith = "http://www.marbef.org/data/aphia.php?p=image&pic=";
862 String result = null;
863 for (MediaRepresentation rep : media.getRepresentations()){
864 for (MediaRepresentationPart part : rep.getParts()){
865 String strUrl = part.getUri().toString();
866 if (strUrl.startsWith(startsWith)){
867 result = part.getUri().toString();
868 }
869 }
870 }
871 return result;
872 }
873
874 //******************************* MAPPINGS ********************************************
875
876 /**
877 * Returns the CDM to PESI specific export mappings for PESI notes.
878 * @return The {@link PesiExportMapping PesiExportMapping}.
879 */
880 private PesiExportMapping getNotesMapping() {
881 PesiExportMapping mapping = new PesiExportMapping(dbNoteTableName);
882
883 mapping.addMapper(IdMapper.NewInstance("NoteId"));
884 mapping.addMapper(DbTextDataMapper.NewDefaultInstance("Note_1"));
885 //TODO
886 mapping.addMapper(MethodMapper.NewInstance("Note_2", this, standardMethodParameter));
887 mapping.addMapper(MethodMapper.NewInstance("NoteCategoryFk", this, standardMethodParameter ));
888
889 mapping.addMapper(MethodMapper.NewInstance("NoteCategoryCache", this, standardMethodParameter, PesiExportState.class ));
890 mapping.addMapper(MethodMapper.NewInstance("LanguageFk", this));
891 mapping.addMapper(MethodMapper.NewInstance("LanguageCache", this, standardMethodParameter, PesiExportState.class));
892
893 // mapping.addMapper(MethodMapper.NewInstance("Region", this));
894 mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("taxonFk"));
895 mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
896 mapping.addCollectionMapping(getNoteSourceMapping());
897 return mapping;
898 }
899
900 private CollectionExportMapping<PesiExportState, PesiExportConfigurator,PesiTransformer> getNoteSourceMapping() {
901 String tableName = "NoteSource";
902 String collectionAttribute = "sources";
903 IdMapper parentMapper = IdMapper.NewInstance("NoteFk");
904 @SuppressWarnings("unchecked")
905 CollectionExportMapping<PesiExportState, PesiExportConfigurator, PesiTransformer> mapping
906 = CollectionExportMapping.NewInstance(tableName, collectionAttribute, parentMapper);
907 mapping.addMapper(DbSimpleFilterMapper.NewSingleNullAttributeInstance("idInSource", "Sources with idInSource currently handle data lineage"));
908 mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceFk"));
909 mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceNameCache", IS_CACHE));
910 mapping.addMapper(DbStringMapper.NewInstance("CitationMicroReference", "SourceDetail"));
911 return mapping;
912 }
913
914 /**
915 * Returns the CDM to PESI specific export mappings for occurrences.
916 * @return The {@link PesiExportMapping PesiExportMapping}.
917 */
918 private PesiExportMapping getOccurrenceMapping() {
919 PesiExportMapping mapping = new PesiExportMapping(dbOccurrenceTableName);
920
921 mapping.addMapper(IdMapper.NewInstance("OccurrenceId"));
922 mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("taxonFk"));
923 mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("TaxonFullNameCache", true, true, null));
924
925 mapping.addMapper(DbAreaMapper.NewInstance(Distribution.class, "Area", "AreaFk", ! IS_CACHE));
926 mapping.addMapper(DbAreaMapper.NewInstance(Distribution.class, "Area", "AreaNameCache", IS_CACHE));
927 mapping.addMapper(DbDistributionStatusMapper.NewInstance("OccurrenceStatusFk", ! IS_CACHE));
928 mapping.addMapper(DbDistributionStatusMapper.NewInstance("OccurrenceStatusCache", IS_CACHE));
929
930 // Use OccurrenceSource table instead
931 mapping.addMapper(DbExportIgnoreMapper.NewInstance("SourceFk", "Use OccurrenceSource table for sources instead"));
932 mapping.addMapper(DbExportIgnoreMapper.NewInstance("SourceNameCache", "Use OccurrenceSource table for sources instead"));
933
934 mapping.addMapper(DbAnnotationMapper.NewExludedInstance(getLastActionAnnotationTypes(), "Notes"));
935 mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
936 mapping.addCollectionMapping(getOccurrenceSourceMapping());
937
938 return mapping;
939 }
940
941 private CollectionExportMapping<PesiExportState, PesiExportConfigurator, PesiTransformer> getOccurrenceSourceMapping() {
942 String tableName = "OccurrenceSource";
943 String collectionAttribute = "sources";
944 IdMapper parentMapper = IdMapper.NewInstance("OccurrenceFk");
945 @SuppressWarnings("unchecked")
946 CollectionExportMapping<PesiExportState, PesiExportConfigurator, PesiTransformer> mapping
947 = CollectionExportMapping.NewInstance(tableName, collectionAttribute, parentMapper);
948 mapping.addMapper(DbSimpleFilterMapper.NewSingleNullAttributeInstance("idInSource",
949 "Sources with idInSource currently handle data lineage"));
950 mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceFk"));
951 mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceNameCache", IS_CACHE));
952 mapping.addMapper(DbStringMapper.NewInstance("CitationMicroReference", "SourceDetail"));
953 mapping.addMapper(DbOriginalNameMapper.NewInstance("OldTaxonName", IS_CACHE, null));
954
955 return mapping;
956 }
957
958 /**
959 * Returns the CDM to PESI specific export mappings for additional taxon sources to create a new
960 * source for the additional taxon source
961 * @see #{@link PesiDescriptionExport#getAdditionalTaxonSourceMapping()}
962 * @return The {@link PesiExportMapping PesiExportMapping}.
963 */
964 private PesiExportMapping getAddTaxonSourceSourceMapping() {
965 PesiExportMapping sourceMapping = new PesiExportMapping(PesiSourceExport.dbTableName);
966
967 sourceMapping.addMapper(IdMapper.NewInstance("SourceId"));
968 sourceMapping.addMapper(DbConstantMapper.NewInstance("SourceCategoryFk", Types.INTEGER, PesiTransformer.REF_UNRESOLVED));
969 sourceMapping.addMapper(DbConstantMapper.NewInstance("SourceCategoryCache", Types.VARCHAR, PesiTransformer.REF_STR_UNRESOLVED));
970
971 // sourceMapping.addMapper(MethodMapper.NewInstance("NomRefCache", PesiSourceExport.class, "getNomRefCache", Reference.class));
972
973 sourceMapping.addMapper(DbTextDataMapper.NewDefaultInstance("NomRefCache"));
974
975 return sourceMapping;
976 }
977
978 /**
979 * Returns the CDM to PESI specific export mappings for additional taxon sources.
980 * @see #{@link PesiDescriptionExport#getAddTaxonSourceSourceMapping()}
981 * @return The {@link PesiExportMapping PesiExportMapping}.
982 */
983 private PesiExportMapping getAdditionalTaxonSourceMapping() {
984
985 PesiExportMapping mapping = new PesiExportMapping(dbAdditionalSourceTableName);
986
987 mapping.addMapper(MethodMapper.NewInstance("TaxonFk", this, DescriptionElementBase.class, PesiExportState.class));
988
989 mapping.addMapper(IdMapper.NewInstance("SourceFk"));
990 mapping.addMapper(DbTextDataMapper.NewDefaultInstance("SourceNameCache"));
991
992 mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER, PesiTransformer.NOMENCLATURAL_REFERENCE));
993 mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, PesiTransformer.STR_NOMENCLATURAL_REFERENCE));
994
995 mapping.addMapper(DbExportIgnoreMapper.NewInstance("SourceDetail", "SourceDetails not available for additional sources"));
996
997 return mapping;
998 }
999
1000 /**
1001 * Returns the CDM to PESI specific export mappings for common names.
1002 * @return The {@link PesiExportMapping PesiExportMapping}.
1003 */
1004 private PesiExportMapping getCommonNamesMapping() {
1005 PesiExportMapping mapping = new PesiExportMapping(dbVernacularTableName);
1006
1007 mapping.addMapper(IdMapper.NewInstance("CommonNameId"));
1008 mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("taxonFk"));
1009
1010 mapping.addMapper(DbStringMapper.NewInstance("Name", "CommonName"));
1011 mapping.addMapper(DbAreaMapper.NewInstance(CommonTaxonName.class, "Area", "Region", IS_CACHE));
1012
1013 mapping.addMapper(DbLanguageMapper.NewInstance(CommonTaxonName.class, "Language", "LanguageFk", ! IS_CACHE));
1014 mapping.addMapper(DbLanguageMapper.NewInstance(CommonTaxonName.class, "Language", "LanguageCache", IS_CACHE));
1015
1016 // Use OccurrenceSource table instead
1017 mapping.addMapper(DbExportIgnoreMapper.NewInstance("SourceFk", "Use CommonNameSource table for sources instead"));
1018 mapping.addMapper(DbExportIgnoreMapper.NewInstance("SourceNameCache", "Use CommonNameSource table for sources instead"));
1019 //OLD
1020 // mapping.addMapper(DbSingleSourceMapper.NewInstance("SourceFk", of ( DbSingleSourceMapper.EXCLUDE.WITH_ID) , ! IS_CACHE));
1021 // mapping.addMapper(DbSingleSourceMapper.NewInstance("SourceNameCache", of ( DbSingleSourceMapper.EXCLUDE.WITH_ID) , IS_CACHE));
1022
1023 mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
1024 mapping.addCollectionMapping(getCommonNameSourceMapping());
1025 return mapping;
1026 }
1027
1028 private CollectionExportMapping<PesiExportState, PesiExportConfigurator, PesiTransformer> getCommonNameSourceMapping() {
1029 String tableName = "CommonNameSource";
1030 String collectionAttribute = "sources";
1031 IdMapper parentMapper = IdMapper.NewInstance("CommonNameFk");
1032 @SuppressWarnings("unchecked")
1033 CollectionExportMapping<PesiExportState, PesiExportConfigurator, PesiTransformer> mapping
1034 = CollectionExportMapping.NewInstance(tableName, collectionAttribute, parentMapper);
1035 mapping.addMapper(DbSimpleFilterMapper.NewSingleNullAttributeInstance("idInSource",
1036 "Sources with idInSource currently handle data lineage"));
1037 mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceFk"));
1038 mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceNameCache", IS_CACHE));
1039 mapping.addMapper(DbStringMapper.NewInstance("CitationMicroReference", "SourceDetail"));
1040 mapping.addMapper(DbOriginalNameMapper.NewInstance("OldTaxonName", IS_CACHE, null));
1041
1042 return mapping;
1043 }
1044
1045 private PesiExportMapping getImageMapping() {
1046 PesiExportMapping mapping = new PesiExportMapping(dbImageTableName);
1047 mapping.addMapper(MethodMapper.NewInstance("taxonFk", this.getClass(), "getCurrentTaxonFk", Media.class, PesiExportState.class));
1048 mapping.addMapper(MethodMapper.NewInstance("img_thumb", this.getClass(), "getMediaThumb", Media.class));
1049 mapping.addMapper(MethodMapper.NewInstance("img_url", this.getClass(), "getMediaUrl", Media.class));
1050 return mapping;
1051 }
1052
1053 @Override
1054 protected boolean doCheck(PesiExportState state) {
1055 boolean result = true;
1056 return result;
1057 }
1058
1059 @Override
1060 protected boolean isIgnore(PesiExportState state) {
1061 return ! state.getConfig().isDoDescription();
1062 }
1063 }