latest changes for PESI description export and transformer
[cdmlib-apps.git] / cdm-pesi / src / main / java / eu / etaxonomy / cdm / io / pesi / out / PesiNoteExport.java
1 // $Id$
2 /**
3 * Copyright (C) 2009 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
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.
9 */
10 package eu.etaxonomy.cdm.io.pesi.out;
11
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;
17 import java.util.Map;
18 import java.util.Set;
19
20 import org.apache.log4j.Logger;
21 import org.joda.time.DateTime;
22 import org.springframework.stereotype.Component;
23 import org.springframework.transaction.TransactionStatus;
24
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;
45
46 /**
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>
52 * @author e.-m.lee
53 * @date 23.02.2010
54 *
55 */
56 @Component
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;
60
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";
65
66 public PesiNoteExport() {
67 super();
68 }
69
70 /* (non-Javadoc)
71 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getStandardMethodParameter()
72 */
73 @Override
74 public Class<? extends CdmBase> getStandardMethodParameter() {
75 return standardMethodParameter;
76 }
77
78 /* (non-Javadoc)
79 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
80 */
81 @Override
82 protected boolean doCheck(PesiExportState state) {
83 boolean result = true;
84 return result;
85 }
86
87 /* (non-Javadoc)
88 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
89 */
90 @Override
91 protected void doInvoke(PesiExportState state) {
92 try {
93 logger.info("*** Started Making " + pluralString + " ...");
94
95 // Get the limit for objects to save within a single transaction.
96 int limit = state.getConfig().getLimitSave();
97
98 // Stores whether this invoke was successful or not.
99 boolean success = true;
100
101 // PESI: Clear the database table Note.
102 doDelete(state);
103
104 // Start transaction
105 success &= doPhase01(state);
106
107
108 logger.info("PHASE 2...");
109 doPhase02(state, limit);
110
111
112 logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
113
114 if (!success){
115 state.setUnsuccessfull();
116 }
117 return;
118 } catch (SQLException e) {
119 e.printStackTrace();
120 logger.error(e.getMessage());
121 state.setUnsuccessfull();
122 }
123 }
124
125 //PHASE 01: Description Elements
126 private boolean doPhase01(PesiExportState state) throws SQLException {
127 logger.info("PHASE 1...");
128 int count = 0;
129 int pastCount = 0;
130 boolean success = true;
131
132 // Calculate the pageNumber
133 int pageNumber = 1;
134 int pageSize = 1000;
135
136
137 // Get specific mappings: (CDM) DescriptionElement -> (PESI) Note
138 PesiExportMapping mapping = getMapping();
139
140 // Initialize the db mapper
141 mapping.initialize(state);
142
143
144 List<DescriptionElementBase> list = null;
145
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) {
150
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);
156 }
157 }
158
159 // Commit transaction
160 commitTransaction(txStatus);
161 logger.debug("Committed transaction.");
162 logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
163 pastCount = count;
164
165 // Start transaction
166 txStatus = startTransaction(true);
167 logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + pageSize + ") ...");
168
169 // Increment pageNumber
170 pageNumber++;
171 }
172 if (list.size() == 0) {
173 logger.info("No " + pluralString + " left to fetch.");
174 }
175 // Commit transaction
176 commitTransaction(txStatus);
177 logger.info("Committed transaction.");
178 return success;
179 }
180
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;
189
190 int count = 0;
191 int pastCount = 0;
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) {
196
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);
219 }
220 }
221
222 doCount(count++, modCount, pluralString);
223 }
224
225 // Commit transaction
226 commitTransaction(txStatus);
227 logger.debug("Committed transaction.");
228 logger.info("Exported " + (count - pastCount) + " names. Total: " + count);
229 pastCount = count;
230
231 // Start transaction
232 txStatus = startTransaction(true);
233 logger.info("Started new transaction. Fetching some names first (max: " + limit + ") ...");
234 }
235 if (taxonNameList.size() == 0) {
236 logger.info("No names left to fetch.");
237 }
238 // Commit transaction
239 commitTransaction(txStatus);
240 logger.debug("Committed transaction.");
241 }
242
243 /**
244 * @param taxComment
245 * @param noteCategoryFk
246 * @param noteCategoryCache
247 * @param object
248 * @param object2
249 */
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 = ?";
254 try {
255 PreparedStatement notesStmt = connection.prepareStatement(notesSql);
256
257 if (note != null) {
258 notesStmt.setString(1, note);
259 } else {
260 notesStmt.setObject(1, null);
261 }
262
263 if (noteCategoryFk != null) {
264 notesStmt.setInt(2, noteCategoryFk);
265 } else {
266 notesStmt.setObject(2, null);
267 }
268
269 if (noteCategoryCache != null) {
270 notesStmt.setString(3, noteCategoryCache);
271 } else {
272 notesStmt.setObject(3, null);
273 }
274
275 if (languageFk != null) {
276 notesStmt.setInt(4, languageFk);
277 } else {
278 notesStmt.setObject(4, null);
279 }
280
281 if (languageCache != null) {
282 notesStmt.setString(5, languageCache);
283 } else {
284 notesStmt.setObject(5, null);
285 }
286
287 if (taxonFk != null) {
288 notesStmt.setInt(6, taxonFk);
289 } else {
290 notesStmt.setObject(6, null);
291 }
292
293 notesStmt.executeUpdate();
294 } catch (SQLException e) {
295 logger.error("Note could not be created: " + note);
296 e.printStackTrace();
297 }
298
299
300 }
301
302 /**
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.
306 */
307 protected boolean doDelete(PesiExportState state) {
308 PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
309
310 String sql;
311 Source destination = pesiConfig.getDestination();
312
313 // Clear NoteSource
314 sql = "DELETE FROM NoteSource";
315 destination.setQuery(sql);
316 destination.update(sql);
317
318 // Clear Note
319 sql = "DELETE FROM " + dbTableName;
320 destination.setQuery(sql);
321 destination.update(sql);
322 return true;
323 }
324
325 /* (non-Javadoc)
326 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
327 */
328 @Override
329 protected boolean isIgnore(PesiExportState state) {
330 return ! state.getConfig().isDoNotes();
331 }
332
333 /**
334 * Returns the <code>Note_1</code> attribute.
335 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
336 * @return The <code>Note_1</code> attribute.
337 * @see MethodMapper
338 */
339 @SuppressWarnings("unused")
340 private static String getNote_1(DescriptionElementBase descriptionElement) {
341 String result = null;
342
343 if (descriptionElement.isInstanceOf(TextData.class)) {
344 TextData textData = CdmBase.deproxy(descriptionElement, TextData.class);
345 result = textData.getText(Language.DEFAULT());
346 }
347
348 return result;
349 }
350
351 /**
352 * Returns the <code>Note_2</code> attribute.
353 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
354 * @return The <code>Note_2</code> attribute.
355 * @see MethodMapper
356 */
357 @SuppressWarnings("unused")
358 private static String getNote_2(DescriptionElementBase descriptionElement) {
359 logger.warn("Not yet implemented");
360 return null;
361 }
362
363 /**
364 * Returns the <code>NoteCategoryFk</code> attribute.
365 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
366 * @return The <code>NoteCategoryFk</code> attribute.
367 * @see MethodMapper
368 */
369 private static Integer getNoteCategoryFk(DescriptionElementBase descriptionElement) {
370 Integer result = null;
371 result = PesiTransformer.feature2NodeCategoryFk(descriptionElement.getFeature());
372 return result;
373 }
374
375 /**
376 * Returns the <code>NoteCategoryCache</code> attribute.
377 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
378 * @return The <code>NoteCategoryCache</code> attribute.
379 * @see MethodMapper
380 */
381 @SuppressWarnings("unused")
382 private static String getNoteCategoryCache(DescriptionElementBase descriptionElement) {
383 String result = null;
384
385 if (descriptionElement.isInstanceOf(TextData.class)) {
386 result = PesiTransformer.textData2NodeCategoryCache(descriptionElement.getFeature());
387 }
388
389 return result;
390 }
391
392 /**
393 * Returns the <code>LanguageFk</code> attribute.
394 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
395 * @return The <code>LanguageFk</code> attribute.
396 * @see MethodMapper
397 */
398 @SuppressWarnings("unused")
399 private static Integer getLanguageFk(DescriptionElementBase descriptionElement) {
400 Language language = getLanguage(descriptionElement);
401
402 return PesiTransformer.language2LanguageId(language);
403 }
404
405 /**
406 * Returns the <code>LanguageCache</code> attribute.
407 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
408 * @return The <code>LanguageCache</code> attribute.
409 * @see MethodMapper
410 */
411 @SuppressWarnings("unused")
412 private static String getLanguageCache(DescriptionElementBase descriptionElement) {
413 Language language = getLanguage(descriptionElement);
414
415 return PesiTransformer.language2LanguageCache(language);
416 }
417
418 private static Language getLanguage(DescriptionElementBase descriptionElement) {
419 Language language = null;
420
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();
434 } else {
435 logger.debug("Given descriptionElement does not support languages. Hence LanguageCache could not be determined: " + descriptionElement.getUuid());
436 }
437
438 if (multilanguageText != null) {
439 Set<Language> languages = multilanguageText.keySet();
440
441 // TODO: Think of something more sophisticated than this
442 if (languages.size() > 0) {
443 language = languages.iterator().next();
444 }
445 if (languages.size() > 1){
446 logger.warn("There is more than 1 language for a given description (" + descriptionElement.getClass().getSimpleName() + "):" + descriptionElement.getUuid());
447 }
448 }
449 return language;
450 }
451
452 /**
453 * Returns the <code>Region</code> attribute.
454 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
455 * @return The <code>Region</code> attribute.
456 * @see MethodMapper
457 */
458 @SuppressWarnings("unused")
459 private static String getRegion(DescriptionElementBase descriptionElement) {
460 String result = null;
461 DescriptionBase<?> inDescription = descriptionElement.getInDescription();
462
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());
474 }
475 }
476 return result;
477 }
478
479 /**
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.
484 * @see MethodMapper
485 */
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());
494 }
495 return result;
496 }
497
498 /**
499 * Returns the TaxonFk for a given TaxonName.
500 * @param taxonName The {@link TaxonNameBase TaxonName}.
501 * @param state The {@link DbExportStateBase DbExportState}.
502 * @return
503 */
504 private static Integer getTaxonFk(TaxonNameBase<?,?> taxonName, DbExportStateBase<?> state) {
505 return state.getDbId(taxonName);
506 }
507
508 /**
509 * Returns the <code>LastAction</code> attribute.
510 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
511 * @return The <code>LastAction</code> attribute.
512 * @see MethodMapper
513 */
514 @SuppressWarnings("unused")
515 private static String getLastAction(DescriptionElementBase descriptionElement) {
516 // TODO
517 return null;
518 }
519
520 /**
521 * Returns the <code>LastActionDate</code> attribute.
522 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
523 * @return The <code>LastActionDate</code> attribute.
524 * @see MethodMapper
525 */
526 @SuppressWarnings("unused")
527 private static DateTime getLastActionDate(DescriptionElementBase descriptionElement) {
528 DateTime result = null;
529 return result;
530 }
531
532 /**
533 * Returns the CDM to PESI specific export mappings.
534 * @return The {@link PesiExportMapping PesiExportMapping}.
535 */
536 private PesiExportMapping getMapping() {
537 PesiExportMapping mapping = new PesiExportMapping(dbTableName);
538
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));
550
551 return mapping;
552 }
553
554 }