2 * Copyright (C) 2009 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.cdm
.io
.pesi
.out
;
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
;
21 import org
.apache
.log4j
.Logger
;
22 import org
.springframework
.stereotype
.Component
;
23 import org
.springframework
.transaction
.TransactionStatus
;
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
;
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>
81 public class PesiDescriptionExport
extends PesiExportBase
{
83 private static final long serialVersionUID
= -1486235807814098217L;
84 private static final Logger logger
= Logger
.getLogger(PesiDescriptionExport
.class);
86 private static final Class
<?
extends CdmBase
> standardMethodParameter
= DescriptionElementBase
.class;
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";
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}));
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
;
109 private static int countCommonName
;
110 private static int countOccurrence
;
111 private static int countOthers
;
113 public PesiDescriptionExport() {
118 public Class
<?
extends CdmBase
> getStandardMethodParameter() {
119 return standardMethodParameter
;
123 protected void doInvoke(PesiExportState state
) {
125 logger
.info("*** Started Making " + pluralString
+ " ...");
127 // Stores whether this invoke was successful or not.
128 boolean success
= true;
130 success
&= doDelete(state
);
132 // Get specific mappings: (CDM) DescriptionElement -> (PESI) Note
133 PesiExportMapping notesMapping
= getNotesMapping();
134 notesMapping
.initialize(state
);
136 // Get specific mappings: (CDM) DescriptionElement -> (PESI) Occurrence
137 PesiExportMapping occurrenceMapping
= getOccurrenceMapping();
138 occurrenceMapping
.initialize(state
);
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
);
146 // Get specific mappings: (CDM) DescriptionElement -> (PESI) Common name
148 PesiExportMapping vernacularMapping
= getCommonNamesMapping();
149 vernacularMapping
.initialize(state
);
151 // Get specific mappings: (CDM) DescriptionElement -> (PESI) Image
152 PesiExportMapping imageMapping
= getImageMapping();
153 imageMapping
.initialize(state
);
155 // Taxon Descriptions
156 success
&= doPhase01(state
, notesMapping
, occurrenceMapping
, addSourceSourceMapping
, additionalSourceMapping
, vernacularMapping
, imageMapping
);
159 success
&= doPhase01b(state
, notesMapping
, occurrenceMapping
, addSourceSourceMapping
, additionalSourceMapping
, vernacularMapping
, imageMapping
);
161 logger
.info("PHASE 2...");
162 success
&= doPhase02(state
);
164 logger
.info("*** Finished Making " + pluralString
+ " ..." + getSuccessString(success
));
167 state
.getResult().addError("An unknown problem occurred");
170 } catch (SQLException e
) {
172 logger
.error(e
.getMessage());
173 state
.getResult().addException(e
, e
.getMessage());
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
{
181 // System.out.println("PHASE 1 of description import");
182 logger
.info("PHASE 1...");
185 boolean success
= true;
186 //int limit = state.getConfig().getLimitSave();
189 List
<Taxon
> taxonList
= null;
191 TransactionStatus txStatus
= startTransaction(true);
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();
199 List
<String
> propPath
= Arrays
.asList(new String
[]{"descriptions.elements.*"});
200 int partitionCount
= 0;
201 while ((taxonList
= getNextTaxonPartition(Taxon
.class, limit
, partitionCount
++, propPath
)) != null ) {
203 if (logger
.isDebugEnabled()) {
204 logger
.info("Fetched " + taxonList
.size() + " " + parentPluralString
+ ". Exporting...");
207 for (Taxon taxon
: taxonList
) {
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
);
217 state
.setCurrentTaxon(null);
219 // Commit transaction
220 commitTransaction(txStatus
);
221 logger
.info("Exported " + (count
- pastCount
) + " " + parentPluralString
+ ". Total: " + count
+ " (Phase 01)");
223 ProfilerController
.memorySnapshot();
225 txStatus
= startTransaction(true);
226 if(logger
.isDebugEnabled()) {
227 logger
.info("Started new transaction. Fetching some " + parentPluralString
+ " (max: " + limit
+ ") for description import ...");
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
);
244 // Commit transaction
245 commitTransaction(txStatus
);
246 logger
.debug("Committed transaction.");
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...");
256 boolean success
= true;
257 //int limit = state.getConfig().getLimitSave();
259 List
<TaxonNameDescription
> nameDescList
= null;
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.*"});
266 int partitionCount
= 0;
267 while ((nameDescList
= getNextNameDescriptionPartition( limit
, partitionCount
++, propPath
)) != null ) {
269 logger
.info("Fetched " + nameDescList
.size() + " name descriptions. Exporting...");
271 for (TaxonNameDescription desc
: nameDescList
) {
273 doCount(count
++, modCount
, "name descriptions");
274 boolean isImageGallery
= desc
.isImageGallery();
276 TaxonName name
= desc
.getTaxonName();
278 for (DescriptionElementBase element
: desc
.getElements()){
279 if (isPurePesiName(name
)){
280 success
&= handleDescriptionElement(state
, notesMapping
, occurrenceMapping
, vernacularMapping
, imageMapping
,
281 addSourceSourceMapping
, additionalSourceMapping
, isImageGallery
, element
);
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);
291 state
.setSourceForAdditionalSourceCreated(false);
296 state
.setCurrentTaxon(null);
298 // Commit transaction
299 commitTransaction(txStatus
);
300 logger
.info("Exported " + (count
- pastCount
) + " name descriptions. Total: " + count
);
304 txStatus
= startTransaction(true);
305 logger
.info("Started new transaction. Fetching some name descriptions (max: " + limit
+ ") for description import ...");
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
);
321 // Commit transaction
322 commitTransaction(txStatus
);
323 logger
.debug("Committed transaction.");
327 private boolean handleSingleTaxon(Taxon taxon
, PesiExportState state
, PesiExportMapping notesMapping
, PesiExportMapping occurrenceMapping
,
328 PesiExportMapping addSourceSourceMapping
, PesiExportMapping additionalSourceMapping
,
329 PesiExportMapping vernacularMapping
, PesiExportMapping imageMapping
) throws SQLException
{
331 boolean success
= true;
333 Set
<DescriptionBase
<?
>> descriptions
= new HashSet
<>();
334 descriptions
.addAll(taxon
.getDescriptions());
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
);
344 if (logger
.isDebugEnabled()) {
345 logger
.info("number of handled decriptionelements " + countDescriptions
);
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
{
356 boolean success
= true;
358 for (Media media
: element
.getMedia()){
360 success
&= imageMapping
.invoke(media
);
362 }else if (isCommonName(element
)){
364 if (element
.isInstanceOf(TextData
.class)){
365 //we do not import text data common names
367 success
&= vernacularMapping
.invoke(element
);
369 }else if (isOccurrence(element
)){
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
)){
377 success
&=occurrenceMapping
.invoke(distribution
);
379 System
.err
.println(distribution
.getInDescription().getTitleCache());
383 logger
.warn("Distribution is not PESI distribution");
385 }else if (isAdditionalTaxonSource(element
)){
386 countAdditionalSources
++;
387 if (! state
.isSourceForAdditionalSourceCreated()){
388 success
&= addSourceSourceMapping
.invoke(element
);
390 success
&= additionalSourceMapping
.invoke(element
);
391 }else if (isExcludedNote(element
)){
393 }else if (isPesiNote(element
)){
395 success
&= notesMapping
.invoke(element
);
397 }else if (isSourceOfSynonymy(element
)){
398 countSourceOfSynonymy
++;
399 if(countSourceOfSynonymy
< 2){
400 logger
.warn("Source of synonymy not yet implemented!");
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
);
408 } catch (Exception e
) {
409 logger
.warn("Exception appeared in description element handling: " + e
);
415 private boolean isSourceOfSynonymy(DescriptionElementBase element
) {
416 if (element
.getFeature() == null){
419 return element
.getFeature().getUuid().equals(ErmsTransformer
.uuidSourceOfSynonymy
);
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
));
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){
435 //...this may change in future so we keep the following code
438 NamedArea area
= distribution
.getArea();
440 logger
.warn("Area is null for distribution " + distribution
.getUuid());
442 }else if (area
.getUuid().equals(BerlinModelTransformer
.euroMedUuid
)){
443 //E+M area only holds endemic status information and therefore is not exported to PESI
445 }else if (area
.equals(TdwgAreaProvider
.getAreaByTdwgAbbreviation("1"))){
446 //Europe area never holds status information (may probably be deleted in E+M)
448 // }else if (area.equals(TdwgArea.getAreaByTdwgAbbreviation("21"))){
449 // //Macaronesia records should not be exported to PESI
451 // //TODO exclude Russion areas Rs*, and maybe ohters
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
);
462 } catch (UndefinedTransformerMethodException e1
) {
463 logger
.warn("Area not available in PESI transformer " + area
.getTitleCache());
470 private boolean isPesiNote(DescriptionElementBase element
) {
471 return (getNoteCategoryFk(element
) != null);
474 private boolean isAdditionalTaxonSource(DescriptionElementBase element
) {
475 Feature feature
= element
.getFeature();
476 if (feature
== null){
479 return (feature
.equals(Feature
.CITATION()) || feature
.equals(Feature
.ADDITIONAL_PUBLICATION()));
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'");
489 }else if (Feature
.DISTRIBUTION().equals(feature
)){
490 logger
.debug("Description element has feature Distribtuion but is not of class 'Distribution'");
497 private boolean isCommonName(DescriptionElementBase element
) {
498 Feature feature
= element
.getFeature();
499 if (feature
== null){
502 return (feature
.equals(Feature
.COMMON_NAME()));
505 //PHASE 02: Name extensions
506 private boolean doPhase02(PesiExportState state
) {
507 TransactionStatus txStatus
;
508 boolean success
= true;
510 // Get the limit for objects to save within a single transaction.
511 //int limit = state.getConfig().getLimitSave();
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
;
521 Connection connection
= state
.getConfig().getDestination().getConnection();
522 if (logger
.isDebugEnabled()) {
523 logger
.info("Started new transaction. Fetching some " + parentPluralString
+ " first (max: " + limit
+ ") ...");
525 logger
.warn("TODO handle extensions on taxon level, not name level (");
526 while ((taxonNameList
= getNameService().list(null, limit
, count
, null, null)).size() > 0) {
528 if(logger
.isDebugEnabled()) {
529 logger
.info("Fetched " + taxonNameList
.size() + " names. Exporting...");
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
);
555 doCount(count
++, modCount
, parentPluralString
);
558 // Commit transaction
559 commitTransaction(txStatus
);
560 logger
.debug("Committed transaction.");
561 logger
.info("Exported " + (count
- pastCount
) + " names. Total: " + count
+ " (Phase 02)");
565 txStatus
= startTransaction(true);
566 if (logger
.isDebugEnabled()) {
567 logger
.info("Started new transaction. Fetching some names first (max: " + limit
+ ") ...");
570 // Commit transaction
571 commitTransaction(txStatus
);
572 logger
.debug("Committed transaction.");
576 private void invokeNotes(String note
, Integer noteCategoryFk
,
577 String noteCategoryCache
, Integer languageFk
, String languageCache
,
578 Integer taxonFk
, Connection connection
) {
580 String notesSql
= "UPDATE Note SET Note_1 = ?, NoteCategoryFk = ?, NoteCategoryCache = ?, LanguageFk = ?, LanguageCache = ? WHERE TaxonFk = ?";
582 PreparedStatement notesStmt
= connection
.prepareStatement(notesSql
);
585 notesStmt
.setString(1, note
);
587 notesStmt
.setObject(1, null);
590 if (noteCategoryFk
!= null) {
591 notesStmt
.setInt(2, noteCategoryFk
);
593 notesStmt
.setObject(2, null);
596 if (noteCategoryCache
!= null) {
597 notesStmt
.setString(3, noteCategoryCache
);
599 notesStmt
.setObject(3, null);
602 if (languageFk
!= null) {
603 notesStmt
.setInt(4, languageFk
);
605 notesStmt
.setObject(4, null);
608 if (languageCache
!= null) {
609 notesStmt
.setString(5, languageCache
);
611 notesStmt
.setObject(5, null);
614 if (taxonFk
!= null) {
615 notesStmt
.setInt(6, taxonFk
);
617 notesStmt
.setObject(6, null);
620 notesStmt
.executeUpdate();
621 } catch (SQLException e
) {
622 logger
.error("Note could not be created: " + note
);
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.
632 protected boolean doDelete(PesiExportState state
) {
633 Source destination
= state
.getConfig().getDestination();
636 String sql
= "DELETE FROM NoteSource";
637 destination
.update(sql
);
639 sql
= "DELETE FROM Note "; // + dbNoteTableName;
640 destination
.update(sql
);
642 // Clear OccurrenceSource
643 sql
= "DELETE FROM OccurrenceSource ";
644 destination
.update(sql
);
646 sql
= "DELETE FROM Occurrence ";
647 destination
.update(sql
);
650 sql
= "DELETE FROM Image ";
651 destination
.update(sql
);
654 sql
= "DELETE FROM CommonNameSource ";
655 destination
.update(sql
);
656 sql
= "DELETE FROM CommonName ";
657 destination
.update(sql
);
659 // Clear AdditionalTaxonSource
660 sql
= "DELETE FROM AdditionalTaxonSource WHERE SourceFk >= 2000000 ";
661 destination
.update(sql
);
663 // Clear Sources for AdditionalTaxonSource
664 sql
= "DELETE FROM Source WHERE SourceId >= 2000000 ";
665 destination
.update(sql
);
671 * Returns the <code>Note_2</code> attribute.
672 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
673 * @return The <code>Note_2</code> attribute.
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")){
689 * Returns the <code>NoteCategoryFk</code> attribute.
690 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
691 * @return The <code>NoteCategoryFk</code> attribute.
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
)){
705 * Returns the <code>NoteCategoryCache</code> attribute.
706 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
707 * @return The <code>NoteCategoryCache</code> attribute.
710 @SuppressWarnings("unused")
711 private static String
getNoteCategoryCache(DescriptionElementBase descriptionElement
, PesiExportState state
) {
712 return state
.getTransformer().getCacheByFeature(descriptionElement
.getFeature());
716 * Returns the <code>LanguageFk</code> attribute.
717 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
718 * @return The <code>LanguageFk</code> attribute.
721 @SuppressWarnings("unused")
722 private static Integer
getLanguageFk(DescriptionElementBase descriptionElement
) {
723 Language language
= getLanguage(descriptionElement
);
725 return PesiTransformer
.language2LanguageId(language
);
729 * Returns the <code>LanguageCache</code> attribute.
730 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
731 * @return The <code>LanguageCache</code> attribute.
732 * @throws UndefinedTransformerMethodException
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
);
741 private static Language
getLanguage(DescriptionElementBase descriptionElement
) {
742 Language language
= null;
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();
758 logger
.debug("Given descriptionElement does not support languages. Hence LanguageCache could not be determined: " + descriptionElement
.getUuid());
761 if (multilanguageText
!= null) {
762 Set
<Language
> languages
= multilanguageText
.keySet();
764 // TODO: Think of something more sophisticated than this
765 if (languages
.size() > 0) {
766 language
= languages
.iterator().next();
768 if (languages
.size() > 1){
769 logger
.warn("There is more than 1 language for a given description (" + descriptionElement
.getClass().getSimpleName() + "):" + descriptionElement
.getUuid());
776 // * Returns the <code>Region</code> attribute.
777 // * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
778 // * @return The <code>Region</code> attribute.
779 // * @see MethodMapper
781 // @SuppressWarnings("unused")
782 // private static String getRegion(DescriptionElementBase descriptionElement) {
783 // String result = null;
784 // DescriptionBase<?> inDescription = descriptionElement.getInDescription();
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());
804 * @param state The {@link DbExportStateBase DbExportState}.
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
);
814 * Returns the TaxonFk for a given TaxonName.
815 * @param taxonName The {@link TaxonNameBase TaxonName}.
816 * @param state The {@link DbExportStateBase DbExportState}.
819 private static Integer
getTaxonKey(TaxonName taxonName
, DbExportStateBase
<?
, PesiTransformer
> state
) {
820 return state
.getDbId(taxonName
);
824 * Returns the <code>FullName</code> attribute.
825 * @param taxonName The {@link NonViralName NonViralName}.
826 * @return The <code>FullName</code> attribute.
829 @SuppressWarnings("unused")
830 private static String
getTaxonFullNameCache(DescriptionElementBase deb
, PesiExportState state
) {
832 TaxonBase
<?
> taxon
= state
.getCurrentTaxon();
833 TaxonName taxonName
= taxon
.getName();
834 TaxonName nvn
= CdmBase
.deproxy(taxonName
);
835 String result
= getCacheStrategy(nvn
).getTitleCache(nvn
);
839 @SuppressWarnings("unused") //used by mapper
840 private static Integer
getCurrentTaxonFk(Media media
, PesiExportState state
) {
841 return state
.getDbId(state
.getCurrentTaxon());
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();
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();
874 //******************************* MAPPINGS ********************************************
877 * Returns the CDM to PESI specific export mappings for PESI notes.
878 * @return The {@link PesiExportMapping PesiExportMapping}.
880 private PesiExportMapping
getNotesMapping() {
881 PesiExportMapping mapping
= new PesiExportMapping(dbNoteTableName
);
883 mapping
.addMapper(IdMapper
.NewInstance("NoteId"));
884 mapping
.addMapper(DbTextDataMapper
.NewDefaultInstance("Note_1"));
886 mapping
.addMapper(MethodMapper
.NewInstance("Note_2", this, standardMethodParameter
));
887 mapping
.addMapper(MethodMapper
.NewInstance("NoteCategoryFk", this, standardMethodParameter
));
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));
893 // mapping.addMapper(MethodMapper.NewInstance("Region", this));
894 mapping
.addMapper(DbDescriptionElementTaxonMapper
.NewInstance("taxonFk"));
895 mapping
.addMapper(ExpertsAndLastActionMapper
.NewInstance());
896 mapping
.addCollectionMapping(getNoteSourceMapping());
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"));
915 * Returns the CDM to PESI specific export mappings for occurrences.
916 * @return The {@link PesiExportMapping PesiExportMapping}.
918 private PesiExportMapping
getOccurrenceMapping() {
919 PesiExportMapping mapping
= new PesiExportMapping(dbOccurrenceTableName
);
921 mapping
.addMapper(IdMapper
.NewInstance("OccurrenceId"));
922 mapping
.addMapper(DbDescriptionElementTaxonMapper
.NewInstance("taxonFk"));
923 mapping
.addMapper(DbDescriptionElementTaxonMapper
.NewInstance("TaxonFullNameCache", true, true, null));
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
));
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"));
934 mapping
.addMapper(DbAnnotationMapper
.NewExludedInstance(getLastActionAnnotationTypes(), "Notes"));
935 mapping
.addMapper(ExpertsAndLastActionMapper
.NewInstance());
936 mapping
.addCollectionMapping(getOccurrenceSourceMapping());
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));
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}.
964 private PesiExportMapping
getAddTaxonSourceSourceMapping() {
965 PesiExportMapping sourceMapping
= new PesiExportMapping(PesiSourceExport
.dbTableName
);
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
));
971 // sourceMapping.addMapper(MethodMapper.NewInstance("NomRefCache", PesiSourceExport.class, "getNomRefCache", Reference.class));
973 sourceMapping
.addMapper(DbTextDataMapper
.NewDefaultInstance("NomRefCache"));
975 return sourceMapping
;
979 * Returns the CDM to PESI specific export mappings for additional taxon sources.
980 * @see #{@link PesiDescriptionExport#getAddTaxonSourceSourceMapping()}
981 * @return The {@link PesiExportMapping PesiExportMapping}.
983 private PesiExportMapping
getAdditionalTaxonSourceMapping() {
985 PesiExportMapping mapping
= new PesiExportMapping(dbAdditionalSourceTableName
);
987 mapping
.addMapper(MethodMapper
.NewInstance("TaxonFk", this, DescriptionElementBase
.class, PesiExportState
.class));
989 mapping
.addMapper(IdMapper
.NewInstance("SourceFk"));
990 mapping
.addMapper(DbTextDataMapper
.NewDefaultInstance("SourceNameCache"));
992 mapping
.addMapper(DbConstantMapper
.NewInstance("SourceUseFk", Types
.INTEGER
, PesiTransformer
.NOMENCLATURAL_REFERENCE
));
993 mapping
.addMapper(DbConstantMapper
.NewInstance("SourceUseCache", Types
.VARCHAR
, PesiTransformer
.STR_NOMENCLATURAL_REFERENCE
));
995 mapping
.addMapper(DbExportIgnoreMapper
.NewInstance("SourceDetail", "SourceDetails not available for additional sources"));
1001 * Returns the CDM to PESI specific export mappings for common names.
1002 * @return The {@link PesiExportMapping PesiExportMapping}.
1004 private PesiExportMapping
getCommonNamesMapping() {
1005 PesiExportMapping mapping
= new PesiExportMapping(dbVernacularTableName
);
1007 mapping
.addMapper(IdMapper
.NewInstance("CommonNameId"));
1008 mapping
.addMapper(DbDescriptionElementTaxonMapper
.NewInstance("taxonFk"));
1010 mapping
.addMapper(DbStringMapper
.NewInstance("Name", "CommonName"));
1011 mapping
.addMapper(DbAreaMapper
.NewInstance(CommonTaxonName
.class, "Area", "Region", IS_CACHE
));
1013 mapping
.addMapper(DbLanguageMapper
.NewInstance(CommonTaxonName
.class, "Language", "LanguageFk", ! IS_CACHE
));
1014 mapping
.addMapper(DbLanguageMapper
.NewInstance(CommonTaxonName
.class, "Language", "LanguageCache", IS_CACHE
));
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"));
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));
1023 mapping
.addMapper(ExpertsAndLastActionMapper
.NewInstance());
1024 mapping
.addCollectionMapping(getCommonNameSourceMapping());
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));
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));
1054 protected boolean doCheck(PesiExportState state
) {
1055 boolean result
= true;
1060 protected boolean isIgnore(PesiExportState state
) {
1061 return ! state
.getConfig().isDoDescription();