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
.Arrays
;
16 import java
.util
.List
;
20 import org
.apache
.log4j
.Logger
;
21 import org
.joda
.time
.DateTime
;
22 import org
.springframework
.stereotype
.Component
;
23 import org
.springframework
.transaction
.TransactionStatus
;
25 import eu
.etaxonomy
.cdm
.io
.common
.DbExportStateBase
;
26 import eu
.etaxonomy
.cdm
.io
.common
.Source
;
27 import eu
.etaxonomy
.cdm
.io
.common
.mapping
.out
.IdMapper
;
28 import eu
.etaxonomy
.cdm
.io
.common
.mapping
.out
.MethodMapper
;
29 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
30 import eu
.etaxonomy
.cdm
.model
.common
.Extension
;
31 import eu
.etaxonomy
.cdm
.model
.common
.ExtensionType
;
32 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
33 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
34 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
35 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
36 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
37 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
38 import eu
.etaxonomy
.cdm
.model
.description
.IndividualsAssociation
;
39 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
40 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
41 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
42 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
43 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
44 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
47 * The export class for {@link eu.etaxonomy.cdm.model.description.DescriptionElementBase DescriptionElements}.<p>
48 * Inserts into DataWarehouse database table <code>Note</code>.<p>
49 * It is divided into two phases:<ul>
50 * <li>Phase 1: Export of DescriptionElements as Notes.
51 * <li>Phase 2: Export of TaxonName extensions <code>taxComment</code>, <code>fauComment</code> and <code>fauExtraCodes</code> as Notes.</ul>
57 public class PesiNoteExport
extends PesiExportBase
{
58 private static final Logger logger
= Logger
.getLogger(PesiNoteExport
.class);
59 private static final Class
<?
extends CdmBase
> standardMethodParameter
= DescriptionElementBase
.class;
61 private static int modCount
= 1000;
62 private static final String dbTableName
= "Note";
63 private static final String pluralString
= "Notes";
64 private static final String parentPluralString
= "Taxa";
66 public PesiNoteExport() {
71 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getStandardMethodParameter()
74 public Class
<?
extends CdmBase
> getStandardMethodParameter() {
75 return standardMethodParameter
;
79 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
82 protected boolean doCheck(PesiExportState state
) {
83 boolean result
= true;
88 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
91 protected void doInvoke(PesiExportState state
) {
93 logger
.info("*** Started Making " + pluralString
+ " ...");
95 // Get the limit for objects to save within a single transaction.
96 int limit
= state
.getConfig().getLimitSave();
98 // Stores whether this invoke was successful or not.
99 boolean success
= true;
101 // PESI: Clear the database table Note.
105 success
&= doPhase01(state
);
108 logger
.info("PHASE 2...");
109 doPhase02(state
, limit
);
112 logger
.info("*** Finished Making " + pluralString
+ " ..." + getSuccessString(success
));
115 state
.setUnsuccessfull();
118 } catch (SQLException e
) {
120 logger
.error(e
.getMessage());
121 state
.setUnsuccessfull();
125 //PHASE 01: Description Elements
126 private boolean doPhase01(PesiExportState state
) throws SQLException
{
127 logger
.info("PHASE 1...");
130 boolean success
= true;
132 // Calculate the pageNumber
137 // Get specific mappings: (CDM) DescriptionElement -> (PESI) Note
138 PesiExportMapping mapping
= getMapping();
140 // Initialize the db mapper
141 mapping
.initialize(state
);
144 List
<DescriptionElementBase
> list
= null;
146 TransactionStatus txStatus
= startTransaction(true);
147 logger
.info("Started new transaction. Fetching some " + pluralString
+ " (max: " + pageSize
+ ") ...");
148 List
<String
> propPath
= Arrays
.asList(new String
[]{"inDescription.taxon"});
149 while ((list
= getDescriptionService().listDescriptionElements(null, null, null, pageSize
, pageNumber
, propPath
)).size() > 0) {
151 logger
.info("Fetched " + list
.size() + " " + pluralString
+ ". Exporting...");
152 for (DescriptionElementBase descriptionElement
: list
) {
153 if (getNoteCategoryFk(descriptionElement
) != null) {
154 doCount(count
++, modCount
, pluralString
);
155 success
&= mapping
.invoke(descriptionElement
);
159 // Commit transaction
160 commitTransaction(txStatus
);
161 logger
.debug("Committed transaction.");
162 logger
.info("Exported " + (count
- pastCount
) + " " + pluralString
+ ". Total: " + count
);
166 txStatus
= startTransaction(true);
167 logger
.info("Started new transaction. Fetching some " + pluralString
+ " (max: " + pageSize
+ ") ...");
169 // Increment pageNumber
172 if (list
.size() == 0) {
173 logger
.info("No " + pluralString
+ " left to fetch.");
175 // Commit transaction
176 commitTransaction(txStatus
);
177 logger
.info("Committed transaction.");
181 //PHASE 02: Name extensions
182 private void doPhase02(PesiExportState state
, int limit
) {
183 TransactionStatus txStatus
;
184 txStatus
= startTransaction(true);
185 ExtensionType taxCommentExtensionType
= (ExtensionType
)getTermService().find(PesiTransformer
.taxCommentUuid
);
186 ExtensionType fauCommentExtensionType
= (ExtensionType
)getTermService().find(PesiTransformer
.fauCommentUuid
);
187 ExtensionType fauExtraCodesExtensionType
= (ExtensionType
)getTermService().find(PesiTransformer
.fauExtraCodesUuid
);
188 List
<TaxonNameBase
> taxonNameList
= null;
192 Connection connection
= state
.getConfig().getDestination().getConnection();
193 logger
.info("Started new transaction. Fetching some " + parentPluralString
+ " first (max: " + limit
+ ") ...");
194 logger
.warn("TODO handle extensions on taxon level, not name level (");
195 while ((taxonNameList
= getNameService().list(null, limit
, count
, null, null)).size() > 0) {
197 logger
.info("Fetched " + taxonNameList
.size() + " names. Exporting...");
198 for (TaxonNameBase
<?
,?
> taxonName
: taxonNameList
) {
199 Set
<Extension
> extensions
= taxonName
.getExtensions();
200 for (Extension extension
: extensions
) {
201 if (extension
.getType().equals(taxCommentExtensionType
)) {
202 String taxComment
= extension
.getValue();
203 invokeNotes(taxComment
,
204 PesiTransformer
.getNoteCategoryFk(PesiTransformer
.taxCommentUuid
),
205 PesiTransformer
.getNoteCategoryCache(PesiTransformer
.taxCommentUuid
),
206 null, null, getTaxonFk(taxonName
, state
),connection
);
207 } else if (extension
.getType().equals(fauCommentExtensionType
)) {
208 String fauComment
= extension
.getValue();
209 invokeNotes(fauComment
,
210 PesiTransformer
.getNoteCategoryFk(PesiTransformer
.fauCommentUuid
),
211 PesiTransformer
.getNoteCategoryCache(PesiTransformer
.fauCommentUuid
),
212 null, null, getTaxonFk(taxonName
, state
),connection
);
213 } else if (extension
.getType().equals(fauExtraCodesExtensionType
)) {
214 String fauExtraCodes
= extension
.getValue();
215 invokeNotes(fauExtraCodes
,
216 PesiTransformer
.getNoteCategoryFk(PesiTransformer
.fauExtraCodesUuid
),
217 PesiTransformer
.getNoteCategoryCache(PesiTransformer
.fauExtraCodesUuid
),
218 null, null, getTaxonFk(taxonName
, state
),connection
);
222 doCount(count
++, modCount
, pluralString
);
225 // Commit transaction
226 commitTransaction(txStatus
);
227 logger
.debug("Committed transaction.");
228 logger
.info("Exported " + (count
- pastCount
) + " names. Total: " + count
);
232 txStatus
= startTransaction(true);
233 logger
.info("Started new transaction. Fetching some names first (max: " + limit
+ ") ...");
235 if (taxonNameList
.size() == 0) {
236 logger
.info("No names left to fetch.");
238 // Commit transaction
239 commitTransaction(txStatus
);
240 logger
.debug("Committed transaction.");
245 * @param noteCategoryFk
246 * @param noteCategoryCache
250 private void invokeNotes(String note
, Integer noteCategoryFk
,
251 String noteCategoryCache
, Integer languageFk
, String languageCache
,
252 Integer taxonFk
, Connection connection
) {
253 String notesSql
= "UPDATE Note SET Note_1 = ?, NoteCategoryFk = ?, NoteCategoryCache = ?, LanguageFk = ?, LanguageCache = ? WHERE TaxonFk = ?";
255 PreparedStatement notesStmt
= connection
.prepareStatement(notesSql
);
258 notesStmt
.setString(1, note
);
260 notesStmt
.setObject(1, null);
263 if (noteCategoryFk
!= null) {
264 notesStmt
.setInt(2, noteCategoryFk
);
266 notesStmt
.setObject(2, null);
269 if (noteCategoryCache
!= null) {
270 notesStmt
.setString(3, noteCategoryCache
);
272 notesStmt
.setObject(3, null);
275 if (languageFk
!= null) {
276 notesStmt
.setInt(4, languageFk
);
278 notesStmt
.setObject(4, null);
281 if (languageCache
!= null) {
282 notesStmt
.setString(5, languageCache
);
284 notesStmt
.setObject(5, null);
287 if (taxonFk
!= null) {
288 notesStmt
.setInt(6, taxonFk
);
290 notesStmt
.setObject(6, null);
293 notesStmt
.executeUpdate();
294 } catch (SQLException e
) {
295 logger
.error("Note could not be created: " + note
);
303 * Deletes all entries of database tables related to <code>Note</code>.
304 * @param state The PesiExportState
305 * @return Whether the delete operation was successful or not.
307 protected boolean doDelete(PesiExportState state
) {
308 PesiExportConfigurator pesiConfig
= (PesiExportConfigurator
) state
.getConfig();
311 Source destination
= pesiConfig
.getDestination();
314 sql
= "DELETE FROM NoteSource";
315 destination
.setQuery(sql
);
316 destination
.update(sql
);
319 sql
= "DELETE FROM " + dbTableName
;
320 destination
.setQuery(sql
);
321 destination
.update(sql
);
326 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
329 protected boolean isIgnore(PesiExportState state
) {
330 return ! state
.getConfig().isDoNotes();
334 * Returns the <code>Note_1</code> attribute.
335 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
336 * @return The <code>Note_1</code> attribute.
339 @SuppressWarnings("unused")
340 private static String
getNote_1(DescriptionElementBase descriptionElement
) {
341 String result
= null;
343 if (descriptionElement
.isInstanceOf(TextData
.class)) {
344 TextData textData
= CdmBase
.deproxy(descriptionElement
, TextData
.class);
345 result
= textData
.getText(Language
.DEFAULT());
352 * Returns the <code>Note_2</code> attribute.
353 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
354 * @return The <code>Note_2</code> attribute.
357 @SuppressWarnings("unused")
358 private static String
getNote_2(DescriptionElementBase descriptionElement
) {
359 logger
.warn("Not yet implemented");
364 * Returns the <code>NoteCategoryFk</code> attribute.
365 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
366 * @return The <code>NoteCategoryFk</code> attribute.
369 private static Integer
getNoteCategoryFk(DescriptionElementBase descriptionElement
) {
370 Integer result
= null;
371 result
= PesiTransformer
.feature2NodeCategoryFk(descriptionElement
.getFeature());
376 * Returns the <code>NoteCategoryCache</code> attribute.
377 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
378 * @return The <code>NoteCategoryCache</code> attribute.
381 @SuppressWarnings("unused")
382 private static String
getNoteCategoryCache(DescriptionElementBase descriptionElement
) {
383 String result
= null;
385 if (descriptionElement
.isInstanceOf(TextData
.class)) {
386 result
= PesiTransformer
.textData2NodeCategoryCache(descriptionElement
.getFeature());
393 * Returns the <code>LanguageFk</code> attribute.
394 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
395 * @return The <code>LanguageFk</code> attribute.
398 @SuppressWarnings("unused")
399 private static Integer
getLanguageFk(DescriptionElementBase descriptionElement
) {
400 Language language
= getLanguage(descriptionElement
);
402 return PesiTransformer
.language2LanguageId(language
);
406 * Returns the <code>LanguageCache</code> attribute.
407 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
408 * @return The <code>LanguageCache</code> attribute.
411 @SuppressWarnings("unused")
412 private static String
getLanguageCache(DescriptionElementBase descriptionElement
) {
413 Language language
= getLanguage(descriptionElement
);
415 return PesiTransformer
.language2LanguageCache(language
);
418 private static Language
getLanguage(DescriptionElementBase descriptionElement
) {
419 Language language
= null;
421 Map
<Language
, LanguageString
> multilanguageText
= null;
422 if (descriptionElement
.isInstanceOf(CommonTaxonName
.class)) {
423 CommonTaxonName commonTaxonName
= CdmBase
.deproxy(descriptionElement
, CommonTaxonName
.class);
424 language
= commonTaxonName
.getLanguage();
425 } else if (descriptionElement
.isInstanceOf(TextData
.class)) {
426 TextData textData
= CdmBase
.deproxy(descriptionElement
, TextData
.class);
427 multilanguageText
= textData
.getMultilanguageText();
428 } else if (descriptionElement
.isInstanceOf(IndividualsAssociation
.class)) {
429 IndividualsAssociation individualsAssociation
= CdmBase
.deproxy(descriptionElement
, IndividualsAssociation
.class);
430 multilanguageText
= individualsAssociation
.getDescription();
431 } else if (descriptionElement
.isInstanceOf(TaxonInteraction
.class)) {
432 TaxonInteraction taxonInteraction
= CdmBase
.deproxy(descriptionElement
, TaxonInteraction
.class);
433 multilanguageText
= taxonInteraction
.getDescriptions();
435 logger
.debug("Given descriptionElement does not support languages. Hence LanguageCache could not be determined: " + descriptionElement
.getUuid());
438 if (multilanguageText
!= null) {
439 Set
<Language
> languages
= multilanguageText
.keySet();
441 // TODO: Think of something more sophisticated than this
442 if (languages
.size() > 0) {
443 language
= languages
.iterator().next();
445 if (languages
.size() > 1){
446 logger
.warn("There is more than 1 language for a given description (" + descriptionElement
.getClass().getSimpleName() + "):" + descriptionElement
.getUuid());
453 * Returns the <code>Region</code> attribute.
454 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
455 * @return The <code>Region</code> attribute.
458 @SuppressWarnings("unused")
459 private static String
getRegion(DescriptionElementBase descriptionElement
) {
460 String result
= null;
461 DescriptionBase
<?
> inDescription
= descriptionElement
.getInDescription();
463 // Area information are associated to TaxonDescriptions and Distributions.
464 if (descriptionElement
.isInstanceOf(Distribution
.class)) {
465 Distribution distribution
= CdmBase
.deproxy(descriptionElement
, Distribution
.class);
466 result
= PesiTransformer
.area2AreaCache(distribution
.getArea());
467 } else if (inDescription
!= null && inDescription
.isInstanceOf(TaxonDescription
.class)) {
468 TaxonDescription taxonDescription
= CdmBase
.deproxy(inDescription
, TaxonDescription
.class);
469 Set
<NamedArea
> namedAreas
= taxonDescription
.getGeoScopes();
470 if (namedAreas
.size() == 1) {
471 result
= PesiTransformer
.area2AreaCache(namedAreas
.iterator().next());
472 } else if (namedAreas
.size() > 1) {
473 logger
.warn("This TaxonDescription contains more than one NamedArea: " + taxonDescription
.getTitleCache());
480 * Returns the <code>TaxonFk</code> attribute.
481 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
482 * @param state The {@link PesiExportState PesiExportState}.
483 * @return The <code>TaxonFk</code> attribute.
486 @SuppressWarnings("unused")
487 private static Integer
getTaxonFk(DescriptionElementBase descriptionElement
, DbExportStateBase
<?
> state
) {
488 Integer result
= null;
489 DescriptionBase
<?
> inDescription
= descriptionElement
.getInDescription();
490 if (inDescription
!= null && inDescription
.isInstanceOf(TaxonDescription
.class)) {
491 TaxonDescription taxonDescription
= CdmBase
.deproxy(inDescription
, TaxonDescription
.class);
492 Taxon taxon
= taxonDescription
.getTaxon();
493 result
= state
.getDbId(taxon
.getName());
499 * Returns the TaxonFk for a given TaxonName.
500 * @param taxonName The {@link TaxonNameBase TaxonName}.
501 * @param state The {@link DbExportStateBase DbExportState}.
504 private static Integer
getTaxonFk(TaxonNameBase
<?
,?
> taxonName
, DbExportStateBase
<?
> state
) {
505 return state
.getDbId(taxonName
);
509 * Returns the <code>LastAction</code> attribute.
510 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
511 * @return The <code>LastAction</code> attribute.
514 @SuppressWarnings("unused")
515 private static String
getLastAction(DescriptionElementBase descriptionElement
) {
521 * Returns the <code>LastActionDate</code> attribute.
522 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
523 * @return The <code>LastActionDate</code> attribute.
526 @SuppressWarnings("unused")
527 private static DateTime
getLastActionDate(DescriptionElementBase descriptionElement
) {
528 DateTime result
= null;
533 * Returns the CDM to PESI specific export mappings.
534 * @return The {@link PesiExportMapping PesiExportMapping}.
536 private PesiExportMapping
getMapping() {
537 PesiExportMapping mapping
= new PesiExportMapping(dbTableName
);
539 mapping
.addMapper(IdMapper
.NewInstance("NoteId"));
540 mapping
.addMapper(MethodMapper
.NewInstance("Note_1", this));
541 mapping
.addMapper(MethodMapper
.NewInstance("Note_2", this));
542 mapping
.addMapper(MethodMapper
.NewInstance("NoteCategoryFk", this));
543 mapping
.addMapper(MethodMapper
.NewInstance("NoteCategoryCache", this));
544 mapping
.addMapper(MethodMapper
.NewInstance("LanguageFk", this));
545 mapping
.addMapper(MethodMapper
.NewInstance("LanguageCache", this));
546 mapping
.addMapper(MethodMapper
.NewInstance("Region", this));
547 mapping
.addMapper(MethodMapper
.NewInstance("TaxonFk", this.getClass(), "getTaxonFk", standardMethodParameter
, DbExportStateBase
.class));
548 mapping
.addMapper(MethodMapper
.NewInstance("LastAction", this));
549 mapping
.addMapper(MethodMapper
.NewInstance("LastActionDate", this));