3 * Copyright (C) 2009 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
10 package eu
.etaxonomy
.cdm
.io
.pesi
.out
;
12 import java
.sql
.Connection
;
13 import java
.sql
.PreparedStatement
;
14 import java
.sql
.SQLException
;
15 import java
.util
.HashMap
;
16 import java
.util
.List
;
20 import org
.apache
.log4j
.Logger
;
21 import org
.springframework
.stereotype
.Component
;
22 import org
.springframework
.transaction
.TransactionStatus
;
24 import eu
.etaxonomy
.cdm
.io
.berlinModel
.out
.mapper
.MethodMapper
;
25 import eu
.etaxonomy
.cdm
.io
.common
.DbExportStateBase
;
26 import eu
.etaxonomy
.cdm
.io
.common
.Source
;
27 import eu
.etaxonomy
.cdm
.model
.common
.AnnotatableEntity
;
28 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
29 import eu
.etaxonomy
.cdm
.model
.common
.DescriptionElementSource
;
30 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
31 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
32 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
33 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
34 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceBase
;
35 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
36 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
37 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.UnknownCdmTypeException
;
46 @SuppressWarnings("unchecked")
47 public class PesiOccurrenceExport
extends PesiExportBase
{
48 private static final Logger logger
= Logger
.getLogger(PesiOccurrenceExport
.class);
49 private static final Class
<?
extends CdmBase
> standardMethodParameter
= AnnotatableEntity
.class;
51 private static int modCount
= 1000;
52 private static final String dbTableName
= "Occurrence";
53 private static final String pluralString
= "Occurrences";
54 private static final String parentPluralString
= "Taxa";
55 private static Taxon taxon
= null;
56 private static Map
<Integer
, Integer
> sourceId2OccurenceIdMap
= new HashMap
<Integer
, Integer
>();
57 private static NamedArea namedArea
= null;
58 private static Distribution distribution
= null;
60 public PesiOccurrenceExport() {
65 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getStandardMethodParameter()
68 public Class
<?
extends CdmBase
> getStandardMethodParameter() {
69 return standardMethodParameter
;
73 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
76 protected boolean doCheck(PesiExportState state
) {
77 boolean result
= true;
82 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
85 protected boolean doInvoke(PesiExportState state
) {
87 logger
.error("*** Started Making " + pluralString
+ " ...");
89 // Get the limit for objects to save within a single transaction.
90 int limit
= state
.getConfig().getLimitSave();
92 // Stores whether this invoke was successful or not.
93 boolean success
= true;
95 // PESI: Clear the database table Occurrence.
98 // Get specific mappings: (CDM) Occurrence -> (PESI) Occurrence
99 PesiExportMapping mapping
= getMapping();
101 // Initialize the db mapper
102 mapping
.initialize(state
);
104 // PESI: Create the Occurrences
108 TransactionStatus txStatus
= null;
109 List
<TaxonBase
> list
= null;
112 txStatus
= startTransaction(true);
113 logger
.error("Started new transaction. Fetching some " + parentPluralString
+ " first (max: " + limit
+ ") ...");
114 while ((list
= getTaxonService().list(null, limit
, taxonCount
, null, null)).size() > 0) {
116 taxonCount
+= list
.size();
117 logger
.error("Fetched " + list
.size() + " " + parentPluralString
+ ".");
118 for (TaxonBase taxonBase
: list
) {
119 if (taxonBase
.isInstanceOf(Taxon
.class)) {
121 // Set the current Taxon
122 taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
124 // Determine the TaxonDescriptions
125 Set
<TaxonDescription
> taxonDescriptions
= taxon
.getDescriptions();
127 // Determine the DescriptionElements (Citations) for the current Taxon
128 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
129 Set
<DescriptionElementBase
> descriptionElements
= taxonDescription
.getElements();
130 for (DescriptionElementBase descriptionElement
: descriptionElements
) {
131 Set
<DescriptionElementSource
> elementSources
= descriptionElement
.getSources();
133 if (descriptionElement
.isInstanceOf(Distribution
.class)) {
134 Distribution distribution
= CdmBase
.deproxy(descriptionElement
, Distribution
.class);
135 setNamedArea(distribution
.getArea());
136 setDistribution(distribution
);
138 // Differentiate between descriptionElements with and without sources.
139 if (elementSources
.size() == 0 && state
.getDbId(descriptionElement
) != null) {
140 if (neededValuesNotNull(descriptionElement
, state
)) {
141 doCount(count
++, modCount
, pluralString
);
142 success
&= mapping
.invoke(descriptionElement
);
145 for (DescriptionElementSource elementSource
: elementSources
) {
146 ReferenceBase reference
= elementSource
.getCitation();
148 // Citations can be empty (null): Is it wrong data or just a normal case?
149 if (reference
!= null && state
.getDbId(reference
) != null) {
150 if (neededValuesNotNull(reference
, state
)) {
151 doCount(count
++, modCount
, pluralString
);
152 success
&= mapping
.invoke(reference
);
159 setDistribution(null);
167 // Commit transaction
168 commitTransaction(txStatus
);
169 logger
.error("Committed transaction.");
170 logger
.error("Exported " + (count
- pastCount
) + " " + pluralString
+ ". Total: " + count
);
174 txStatus
= startTransaction(true);
175 logger
.error("Started new transaction. Fetching some " + parentPluralString
+ " first (max: " + limit
+ ") ...");
177 if (list
.size() == 0) {
178 logger
.error("No " + parentPluralString
+ " left to fetch.");
180 // Commit transaction
181 commitTransaction(txStatus
);
182 logger
.error("Committed transaction.");
184 logger
.error("*** Finished Making " + pluralString
+ " ..." + getSuccessString(success
));
187 } catch (SQLException e
) {
189 logger
.error(e
.getMessage());
195 * Checks whether needed values for an entity are NULL.
198 private boolean neededValuesNotNull(AnnotatableEntity entity
, PesiExportState state
) {
199 boolean result
= true;
200 if (getTaxonFk(entity
, state
) == null) {
201 logger
.error("TaxonFk is NULL, but is not allowed to be. Therefore no record was written to export database for this entity: " + entity
.getUuid());
204 if (getAreaFk(entity
) == null) {
205 logger
.error("AreaFk is NULL, but is not allowed to be. Therefore no record was written to export database for this entity: " + entity
.getUuid());
208 if (getOccurrenceStatusFk(entity
) == null) {
209 logger
.error("OccurrenceStatusFk is NULL, but is not allowed to be. Therefore no record was written to export database for this entity: " + entity
.getUuid());
216 * Creates the entries for the database table 'OccurrenceSource'.
221 protected boolean invokeOccurrenceSource(AnnotatableEntity entity
, PesiExportState state
) {
222 if (entity
== null) {
225 // Create OccurrenceSource database table entry
226 String lastStoredRecordSql
= "Insert Into OccurrenceSource (OccurrenceFk, SourceFk, SourceNameCache, OldTaxonName) " +
227 "values(?, ?, ?, ?)";
228 Connection con
= state
.getConfig().getDestination().getConnection();
231 PreparedStatement stmt
= con
.prepareStatement(lastStoredRecordSql
);
232 Integer sourceFk
= getSourceFk(entity
, state
);
233 stmt
.setInt(1, sourceId2OccurenceIdMap
.get(sourceFk
));
234 stmt
.setInt(2, sourceFk
);
235 stmt
.setString(3, getSourceCache(entity
));
236 stmt
.setString(4, null); // Which taxon are we talking about?
237 stmt
.executeUpdate();
239 } catch (SQLException e
) {
240 logger
.error("SQLException during getOccurrenceId invoke...");
248 * Deletes all entries of database tables related to <code>Occurrence</code>.
249 * @param state The {@link PesiExportState PesiExportState}.
250 * @return Whether the delete operation was successful or not.
252 protected boolean doDelete(PesiExportState state
) {
253 PesiExportConfigurator pesiConfig
= (PesiExportConfigurator
) state
.getConfig();
256 Source destination
= pesiConfig
.getDestination();
259 sql
= "DELETE FROM " + dbTableName
;
260 destination
.setQuery(sql
);
261 destination
.update(sql
);
266 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
269 protected boolean isIgnore(PesiExportState state
) {
270 return ! state
.getConfig().isDoOccurrence();
274 * Returns the <code>TaxonFk</code> attribute.
275 * @param state The {@link DbExportStateBase DbExportState}.
276 * @return The <code>TaxonFk</code> attribute.
279 private static Integer
getTaxonFk(AnnotatableEntity entity
, PesiExportState state
) {
280 // AnnotatableEntity parameter isn't needed, but the DbSingleAttributeExportMapperBase throws a type mismatch exception otherwise
281 // since it awaits two parameters if one of them is of instance DbExportStateBase.
282 Integer result
= null;
283 if (state
!= null && taxon
!= null) {
284 result
= state
.getDbId(taxon
.getName());
290 * Returns the <code>TaxonFullNameCache</code> attribute.
292 * @return The <code>TaxonFullNameCache</code> attribute.
295 @SuppressWarnings("unused")
296 private static String
getTaxonFullNameCache(AnnotatableEntity entity
) {
297 String result
= null;
298 result
= taxon
.getName().getTitleCache();
303 * Returns the <code>AreaFk</code> attribute.
305 * @return The <code>AreaFk</code> attribute.
308 private static Integer
getAreaFk(AnnotatableEntity entity
) {
309 Integer result
= null;
310 if (getNamedArea() != null) {
311 result
= PesiTransformer
.area2AreaId(namedArea
);
313 logger
.warn("This should never happen, but a NamedArea could not be found for entity: " + entity
.getUuid());
319 * Returns the <code>AreaNameCache</code> attribute.
321 * @return The <code>AreaNameCache</code> attribute.
324 @SuppressWarnings("unused")
325 private static String
getAreaNameCache(AnnotatableEntity entity
) {
326 String result
= null;
327 if (getNamedArea() != null) {
328 result
= PesiTransformer
.area2AreaCache(namedArea
);
330 logger
.warn("This should never happen, but a NamedArea could not be found for entity: " + entity
.getUuid());
336 * @return the distribution
338 public static Distribution
getDistribution() {
343 * @param distribution the distribution to set
345 public static void setDistribution(Distribution distribution
) {
346 PesiOccurrenceExport
.distribution
= distribution
;
350 * @return the namedArea
352 public static NamedArea
getNamedArea() {
357 * @param namedArea the namedArea to set
359 public static void setNamedArea(NamedArea namedArea
) {
360 PesiOccurrenceExport
.namedArea
= namedArea
;
364 * Returns the <code>OccurrenceStatusFk</code> attribute.
366 * @return The <code>OccurrenceStatusFk</code> attribute.
367 * @throws UnknownCdmTypeException
370 private static Integer
getOccurrenceStatusFk(AnnotatableEntity entity
) {
371 Integer result
= null;
372 if (getDistribution() != null) {
373 result
= PesiTransformer
.presenceAbsenceTerm2OccurrenceStatusId(getDistribution().getStatus());
379 * Returns the <code>OccurrenceStatusCache</code> attribute.
381 * @return The <code>OccurrenceStatusCache</code> attribute.
382 * @throws UnknownCdmTypeException
385 @SuppressWarnings("unused")
386 private static String
getOccurrenceStatusCache(AnnotatableEntity entity
) {
387 String result
= null;
388 if (getDistribution() != null) {
389 result
= PesiTransformer
.presenceAbsenceTerm2OccurrenceStatusCache(getDistribution().getStatus());
395 * Returns the <code>SourceFk</code> attribute.
397 * @param state The {@link PesiExportState PesiExportState}.
398 * @return The <code>SourceFk</code> attribute.
401 private static Integer
getSourceFk(AnnotatableEntity entity
, PesiExportState state
) {
402 Integer result
= null;
403 if (state
!= null && entity
!= null && entity
.isInstanceOf(Distribution
.class)) {
404 Distribution distribution
= CdmBase
.deproxy(entity
, Distribution
.class);
405 Set
<DescriptionElementSource
> sources
= distribution
.getSources();
406 if (sources
.size() == 1) {
407 DescriptionElementSource source
= sources
.iterator().next();
408 result
= state
.getDbId(source
.getCitation());
409 } else if (sources
.size() > 1) {
410 logger
.warn("Found Distribution with " + sources
.size() + " sources.");
417 * Returns the <code>SourceCache</code> attribute.
419 * @return The <code>SourceCache</code> attribute.
422 private static String
getSourceCache(AnnotatableEntity entity
) {
423 String result
= null;
424 ReferenceBase reference
;
425 if (entity
!= null && entity
.isInstanceOf(Distribution
.class)) {
426 Distribution distribution
= CdmBase
.deproxy(entity
, Distribution
.class);
427 Set
<DescriptionElementSource
> sources
= distribution
.getSources();
428 if (sources
.size() == 1) {
429 DescriptionElementSource source
= sources
.iterator().next();
430 reference
= source
.getCitation();
431 if (reference
!= null) {
432 result
= reference
.getTitle();
434 } else if (sources
.size() > 1) {
435 logger
.warn("Found Distribution with " + sources
.size() + " sources.");
442 * Returns the <code>Notes</code> attribute.
444 * @return The <code>Notes</code> attribute.
447 @SuppressWarnings("unused")
448 private static String
getNotes(AnnotatableEntity entity
) {
454 * Returns the CDM to PESI specific export mappings.
455 * @return The {@link PesiExportMapping PesiExportMapping}.
457 private PesiExportMapping
getMapping() {
458 PesiExportMapping mapping
= new PesiExportMapping(dbTableName
);
460 mapping
.addMapper(MethodMapper
.NewInstance("TaxonFk", this.getClass(), "getTaxonFk", standardMethodParameter
, PesiExportState
.class));
461 mapping
.addMapper(MethodMapper
.NewInstance("AreaFk", this));
462 mapping
.addMapper(MethodMapper
.NewInstance("TaxonFullNameCache", this));
463 mapping
.addMapper(MethodMapper
.NewInstance("AreaNameCache", this));
464 mapping
.addMapper(MethodMapper
.NewInstance("OccurrenceStatusFk", this));
465 mapping
.addMapper(MethodMapper
.NewInstance("OccurrenceStatusCache", this));
466 mapping
.addMapper(MethodMapper
.NewInstance("SourceFk", this.getClass(), "getSourceFk", standardMethodParameter
, PesiExportState
.class));
467 mapping
.addMapper(MethodMapper
.NewInstance("SourceCache", this));
468 mapping
.addMapper(MethodMapper
.NewInstance("Notes", this));
469 // mapping.addMapper(CreatedAndNotesMapper.NewInstance());