View Javadoc

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.Date;
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.berlinModel.out.mapper.IdMapper;
26  import eu.etaxonomy.cdm.io.berlinModel.out.mapper.MethodMapper;
27  import eu.etaxonomy.cdm.io.common.DbExportStateBase;
28  import eu.etaxonomy.cdm.io.common.Source;
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 boolean doInvoke(PesiExportState state) {
92  		try {
93  			logger.error("*** Started Making " + pluralString + " ...");
94  
95  			// Get the limit for objects to save within a single transaction.
96  			int limit = state.getConfig().getLimitSave();
97  			int pageSize = 1000;
98  
99  			// Calculate the pageNumber
100 			int pageNumber = 1;
101 
102 			// Stores whether this invoke was successful or not.
103 			boolean success = true;
104 	
105 			// PESI: Clear the database table Note.
106 			doDelete(state);
107 	
108 			// Get specific mappings: (CDM) DescriptionElement -> (PESI) Note
109 			PesiExportMapping mapping = getMapping();
110 	
111 			// Initialize the db mapper
112 			mapping.initialize(state);
113 	
114 			// PESI: Create the Notes
115 			int count = 0;
116 			int pastCount = 0;
117 			TransactionStatus txStatus = null;
118 			List<DescriptionElementBase> list = null;
119 			
120 			// Start transaction
121 			logger.error("PHASE 1...");
122 			txStatus = startTransaction(true);
123 			logger.error("Started new transaction. Fetching some " + pluralString + " (max: " + pageSize + ") ...");
124 			while ((list = getDescriptionService().listDescriptionElements(null, null, null, pageSize, pageNumber, null)).size() > 0) {
125 
126 				logger.error("Fetched " + list.size() + " " + pluralString + ". Exporting...");
127 				for (DescriptionElementBase description : list) {
128 					
129 					if (getNoteCategoryFk(description) != null) {
130 						doCount(count++, modCount, pluralString);
131 						success &= mapping.invoke(description);
132 					}
133 				}
134 
135 				// Commit transaction
136 				commitTransaction(txStatus);
137 				logger.error("Committed transaction.");
138 				logger.error("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
139 				pastCount = count;
140 	
141 				// Start transaction
142 				txStatus = startTransaction(true);
143 				logger.error("Started new transaction. Fetching some " + pluralString + " (max: " + pageSize + ") ...");
144 				
145 				// Increment pageNumber
146 				pageNumber++;
147 			}
148 			if (list.size() == 0) {
149 				logger.error("No " + pluralString + " left to fetch.");
150 			}
151 			// Commit transaction
152 			commitTransaction(txStatus);
153 			logger.error("Committed transaction.");
154 
155 			
156 			logger.error("PHASE 2...");
157 			txStatus = startTransaction(true);
158 			List<TaxonNameBase> taxonNameList = null;
159 			ExtensionType taxCommentExtensionType = (ExtensionType)getTermService().find(PesiTransformer.taxCommentUuid);
160 			ExtensionType fauCommentExtensionType = (ExtensionType)getTermService().find(PesiTransformer.fauCommentUuid);
161 			ExtensionType fauExtraCodesExtensionType = (ExtensionType)getTermService().find(PesiTransformer.fauExtraCodesUuid);
162 			String taxComment = null;
163 			String fauComment = null;
164 			String fauExtraCodes = null;
165 			
166 			Connection connection = state.getConfig().getDestination().getConnection();
167 			logger.error("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
168 			while ((taxonNameList = getNameService().list(null, limit, count, null, null)).size() > 0) {
169 
170 				logger.error("Fetched " + list.size() + " " + parentPluralString + ". Exporting...");
171 				for (TaxonNameBase taxonName : taxonNameList) {
172 					Set<Extension> extensions = taxonName.getExtensions();
173 					for (Extension extension : extensions) {
174 						if (extension.getType().equals(taxCommentExtensionType)) {
175 							taxComment = extension.getValue();
176 							invokeNotes(taxComment, 
177 									PesiTransformer.getNoteCategoryFk(PesiTransformer.taxCommentUuid), 
178 									PesiTransformer.getNoteCategoryCache(PesiTransformer.taxCommentUuid),
179 									null, null, getTaxonFk(taxonName, state),connection);
180 						} else if (extension.getType().equals(fauCommentExtensionType)) {
181 							fauComment = extension.getValue();
182 							invokeNotes(fauComment, 
183 									PesiTransformer.getNoteCategoryFk(PesiTransformer.fauCommentUuid), 
184 									PesiTransformer.getNoteCategoryCache(PesiTransformer.fauCommentUuid),
185 									null, null, getTaxonFk(taxonName, state),connection);
186 						} else if (extension.getType().equals(fauExtraCodesExtensionType)) {
187 							fauExtraCodes = extension.getValue();
188 							invokeNotes(fauExtraCodes, 
189 									PesiTransformer.getNoteCategoryFk(PesiTransformer.fauExtraCodesUuid), 
190 									PesiTransformer.getNoteCategoryCache(PesiTransformer.fauExtraCodesUuid),
191 									null, null, getTaxonFk(taxonName, state),connection);
192 						}
193 					}
194 					
195 					doCount(count++, modCount, pluralString);
196 				}
197 
198 				// Commit transaction
199 				commitTransaction(txStatus);
200 				logger.error("Committed transaction.");
201 				logger.error("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
202 				pastCount = count;
203 
204 				// Start transaction
205 				txStatus = startTransaction(true);
206 				logger.error("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
207 			}
208 			if (list.size() == 0) {
209 				logger.error("No " + parentPluralString + " left to fetch.");
210 			}
211 			// Commit transaction
212 			commitTransaction(txStatus);
213 			logger.error("Committed transaction.");
214 
215 
216 			logger.error("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
217 			
218 			return success;
219 		} catch (SQLException e) {
220 			e.printStackTrace();
221 			logger.error(e.getMessage());
222 			return false;
223 		}
224 	}
225 
226 	/**
227 	 * @param taxComment
228 	 * @param noteCategoryFk
229 	 * @param noteCategoryCache
230 	 * @param object
231 	 * @param object2
232 	 */
233 	private void invokeNotes(String note, Integer noteCategoryFk,
234 			String noteCategoryCache, Integer languageFk, String languageCache, 
235 			Integer taxonFk, Connection connection) {
236 		String notesSql = "UPDATE Note SET Note_1 = ?, NoteCategoryFk = ?, NoteCategoryCache = ?, LanguageFk = ?, LanguageCache = ? WHERE TaxonFk = ?"; 
237 		try {
238 			PreparedStatement notesStmt = connection.prepareStatement(notesSql);
239 			
240 			if (note != null) {
241 				notesStmt.setString(1, note);
242 			} else {
243 				notesStmt.setObject(1, null);
244 			}
245 			
246 			if (noteCategoryFk != null) {
247 				notesStmt.setInt(2, noteCategoryFk);
248 			} else {
249 				notesStmt.setObject(2, null);
250 			}
251 			
252 			if (noteCategoryCache != null) {
253 				notesStmt.setString(3, noteCategoryCache);
254 			} else {
255 				notesStmt.setObject(3, null);
256 			}
257 			
258 			if (languageFk != null) {
259 				notesStmt.setInt(4, languageFk);
260 			} else {
261 				notesStmt.setObject(4, null);
262 			}
263 			
264 			if (languageCache != null) {
265 				notesStmt.setString(5, languageCache);
266 			} else {
267 				notesStmt.setObject(5, null);
268 			}
269 			
270 			if (taxonFk != null) {
271 				notesStmt.setInt(6, taxonFk);
272 			} else {
273 				notesStmt.setObject(6, null);
274 			}
275 			
276 			notesStmt.executeUpdate();
277 		} catch (SQLException e) {
278 			logger.error("Note could not be created: " + note);
279 			e.printStackTrace();
280 		}
281 
282 
283 	}
284 
285 	/**
286 	 * Deletes all entries of database tables related to <code>Note</code>.
287 	 * @param state The PesiExportState
288 	 * @return Whether the delete operation was successful or not.
289 	 */
290 	protected boolean doDelete(PesiExportState state) {
291 		PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
292 		
293 		String sql;
294 		Source destination =  pesiConfig.getDestination();
295 
296 		// Clear NoteSource
297 		sql = "DELETE FROM NoteSource";
298 		destination.setQuery(sql);
299 		destination.update(sql);
300 
301 		// Clear Note
302 		sql = "DELETE FROM " + dbTableName;
303 		destination.setQuery(sql);
304 		destination.update(sql);
305 		return true;
306 	}
307 
308 	/* (non-Javadoc)
309 	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
310 	 */
311 	@Override
312 	protected boolean isIgnore(PesiExportState state) {
313 		// TODO Auto-generated method stub
314 		return false;
315 	}
316 
317 	/**
318 	 * Returns the <code>Note_1</code> attribute.
319 	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
320 	 * @return The <code>Note_1</code> attribute.
321 	 * @see MethodMapper
322 	 */
323 	@SuppressWarnings("unused")
324 	private static String getNote_1(DescriptionElementBase descriptionElement) {
325 		String result = null;
326 
327 		if (descriptionElement.isInstanceOf(TextData.class)) {
328 			TextData textData = CdmBase.deproxy(descriptionElement, TextData.class);
329 			result = textData.getText(Language.DEFAULT());
330 		}
331 
332 		return result;
333 	}
334 
335 	/**
336 	 * Returns the <code>Note_2</code> attribute.
337 	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
338 	 * @return The <code>Note_2</code> attribute.
339 	 * @see MethodMapper
340 	 */
341 	@SuppressWarnings("unused")
342 	private static String getNote_2(DescriptionElementBase descriptionElement) {
343 		// TODO: extension
344 		return null;
345 	}
346 
347 	/**
348 	 * Returns the <code>NoteCategoryFk</code> attribute.
349 	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
350 	 * @return The <code>NoteCategoryFk</code> attribute.
351 	 * @see MethodMapper
352 	 */
353 	private static Integer getNoteCategoryFk(DescriptionElementBase descriptionElement) {
354 		Integer result = null;
355 		
356 		result = PesiTransformer.textData2NodeCategoryFk(descriptionElement.getFeature());
357 
358 		return result;
359 	}
360 	
361 	/**
362 	 * Returns the <code>NoteCategoryCache</code> attribute.
363 	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
364 	 * @return The <code>NoteCategoryCache</code> attribute.
365 	 * @see MethodMapper
366 	 */
367 	@SuppressWarnings("unused")
368 	private static String getNoteCategoryCache(DescriptionElementBase descriptionElement) {
369 		String result = null;
370 
371 		if (descriptionElement.isInstanceOf(TextData.class)) {
372 			result = PesiTransformer.textData2NodeCategoryCache(descriptionElement.getFeature());
373 		}
374 
375 		return result;
376 	}
377 
378 	/**
379 	 * Returns the <code>LanguageFk</code> attribute.
380 	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
381 	 * @return The <code>LanguageFk</code> attribute.
382 	 * @see MethodMapper
383 	 */
384 	@SuppressWarnings("unused")
385 	private static Integer getLanguageFk(DescriptionElementBase descriptionElement) {
386 		Language language = null;
387 
388 		Map<Language, LanguageString> multilanguageText = null;
389 		if (descriptionElement.isInstanceOf(CommonTaxonName.class)) {
390 			CommonTaxonName commonTaxonName = CdmBase.deproxy(descriptionElement, CommonTaxonName.class);
391 			language = commonTaxonName.getLanguage();
392 		} else if (descriptionElement.isInstanceOf(TextData.class)) {
393 			TextData textData = CdmBase.deproxy(descriptionElement, TextData.class);
394 			multilanguageText = textData.getMultilanguageText();
395 		} else if (descriptionElement.isInstanceOf(IndividualsAssociation.class)) {
396 			IndividualsAssociation individualsAssociation = CdmBase.deproxy(descriptionElement, IndividualsAssociation.class);
397 			multilanguageText = individualsAssociation.getDescription();
398 		} else if (descriptionElement.isInstanceOf(TaxonInteraction.class)) {
399 			TaxonInteraction taxonInteraction = CdmBase.deproxy(descriptionElement, TaxonInteraction.class);
400 			multilanguageText = taxonInteraction.getDescriptions();
401 		} else {
402 			logger.warn("Given descriptionElement is not of appropriate instance. Hence LanguageCache could not be determined: " + descriptionElement.getUuid());
403 		}
404 		
405 		if (multilanguageText != null) {
406 			Set<Language> languages = multilanguageText.keySet();
407 
408 			// TODO: Think of something more sophisticated than this
409 			if (languages.size() > 0) {
410 				language = languages.iterator().next();
411 			}
412 		}
413 
414 		return PesiTransformer.language2LanguageId(language);
415 	}
416 
417 	/**
418 	 * Returns the <code>LanguageCache</code> attribute.
419 	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
420 	 * @return The <code>LanguageCache</code> attribute.
421 	 * @see MethodMapper
422 	 */
423 	@SuppressWarnings("unused")
424 	private static String getLanguageCache(DescriptionElementBase descriptionElement) {
425 		Language language = null;
426 
427 		Map<Language, LanguageString> multilanguageText = null;
428 		if (descriptionElement.isInstanceOf(CommonTaxonName.class)) {
429 			CommonTaxonName commonTaxonName = CdmBase.deproxy(descriptionElement, CommonTaxonName.class);
430 			language = commonTaxonName.getLanguage();
431 		} else if (descriptionElement.isInstanceOf(TextData.class)) {
432 			TextData textData = CdmBase.deproxy(descriptionElement, TextData.class);
433 			multilanguageText = textData.getMultilanguageText();
434 		} else if (descriptionElement.isInstanceOf(IndividualsAssociation.class)) {
435 			IndividualsAssociation individualsAssociation = CdmBase.deproxy(descriptionElement, IndividualsAssociation.class);
436 			multilanguageText = individualsAssociation.getDescription();
437 		} else if (descriptionElement.isInstanceOf(TaxonInteraction.class)) {
438 			TaxonInteraction taxonInteraction = CdmBase.deproxy(descriptionElement, TaxonInteraction.class);
439 			multilanguageText = taxonInteraction.getDescriptions();
440 		} else {
441 			logger.warn("Given descriptionElement is not of appropriate instance. Hence LanguageCache could not be determined: " + descriptionElement.getUuid());
442 		}
443 		
444 		if (multilanguageText != null) {
445 			Set<Language> languages = multilanguageText.keySet();
446 
447 			// TODO: Think of something more sophisticated than this
448 			if (languages.size() > 0) {
449 				language = languages.iterator().next();
450 			}
451 		}
452 
453 		return PesiTransformer.language2LanguageCache(language);
454 	}
455 
456 	/**
457 	 * Returns the <code>Region</code> attribute.
458 	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
459 	 * @return The <code>Region</code> attribute.
460 	 * @see MethodMapper
461 	 */
462 	@SuppressWarnings("unused")
463 	private static String getRegion(DescriptionElementBase descriptionElement) {
464 		String result = null;
465 		DescriptionBase inDescription = descriptionElement.getInDescription();
466 		
467 		// Area information are associated to TaxonDescriptions and Distributions.
468 		if (descriptionElement.isInstanceOf(Distribution.class)) {
469 			Distribution distribution = CdmBase.deproxy(descriptionElement, Distribution.class);
470 			result = PesiTransformer.area2AreaCache(distribution.getArea());
471 		} else if (inDescription != null && inDescription.isInstanceOf(TaxonDescription.class)) {
472 			TaxonDescription taxonDescription = CdmBase.deproxy(inDescription, TaxonDescription.class);
473 			Set<NamedArea> namedAreas = taxonDescription.getGeoScopes();
474 			if (namedAreas.size() == 1) {
475 				result = PesiTransformer.area2AreaCache(namedAreas.iterator().next());
476 			} else if (namedAreas.size() > 1) {
477 				logger.warn("This TaxonDescription contains more than one NamedArea: " + taxonDescription.getTitleCache());
478 			}
479 		}
480 		return result;
481 	}
482 
483 	/**
484 	 * Returns the <code>TaxonFk</code> attribute.
485 	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
486 	 * @param state The {@link PesiExportState PesiExportState}.
487 	 * @return The <code>TaxonFk</code> attribute.
488 	 * @see MethodMapper
489 	 */
490 	@SuppressWarnings("unused")
491 	private static Integer getTaxonFk(DescriptionElementBase descriptionElement, DbExportStateBase<?> state) {
492 		Integer result = null;
493 		DescriptionBase inDescription = descriptionElement.getInDescription();
494 		if (inDescription != null && inDescription.isInstanceOf(TaxonDescription.class)) {
495 			TaxonDescription taxonDescription = CdmBase.deproxy(inDescription, TaxonDescription.class);
496 			Taxon taxon = taxonDescription.getTaxon();
497 			result = state.getDbId(taxon.getName());
498 		}
499 		return result;
500 	}
501 	
502 	/**
503 	 * Returns the TaxonFk for a given TaxonName.
504 	 * @param taxonName The {@link TaxonNameBase TaxonName}.
505 	 * @param state The {@link DbExportStateBase DbExportState}.
506 	 * @return
507 	 */
508 	private static Integer getTaxonFk(TaxonNameBase taxonName, DbExportStateBase<?> state) {
509 		return state.getDbId(taxonName);
510 	}
511 	
512 	/**
513 	 * Returns the <code>LastAction</code> attribute.
514 	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
515 	 * @return The <code>LastAction</code> attribute.
516 	 * @see MethodMapper
517 	 */
518 	@SuppressWarnings("unused")
519 	private static String getLastAction(DescriptionElementBase descriptionElement) {
520 		// TODO
521 		return null;
522 	}
523 
524 	/**
525 	 * Returns the <code>LastActionDate</code> attribute.
526 	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
527 	 * @return The <code>LastActionDate</code> attribute.
528 	 * @see MethodMapper
529 	 */
530 	@SuppressWarnings("unused")
531 	private static DateTime getLastActionDate(DescriptionElementBase descriptionElement) {
532 		DateTime result = null;
533 		return result;
534 	}
535 
536 	/**
537 	 * Returns the CDM to PESI specific export mappings.
538 	 * @return The {@link PesiExportMapping PesiExportMapping}.
539 	 */
540 	private PesiExportMapping getMapping() {
541 		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
542 		
543 		mapping.addMapper(IdMapper.NewInstance("NoteId"));
544 		mapping.addMapper(MethodMapper.NewInstance("Note_1", this));
545 		mapping.addMapper(MethodMapper.NewInstance("Note_2", this));
546 		mapping.addMapper(MethodMapper.NewInstance("NoteCategoryFk", this));
547 		mapping.addMapper(MethodMapper.NewInstance("NoteCategoryCache", this));
548 		mapping.addMapper(MethodMapper.NewInstance("LanguageFk", this));
549 		mapping.addMapper(MethodMapper.NewInstance("LanguageCache", this));
550 		mapping.addMapper(MethodMapper.NewInstance("Region", this));
551 		mapping.addMapper(MethodMapper.NewInstance("TaxonFk", this.getClass(), "getTaxonFk", standardMethodParameter, DbExportStateBase.class));
552 		mapping.addMapper(MethodMapper.NewInstance("LastAction", this));
553 		mapping.addMapper(MethodMapper.NewInstance("LastActionDate", this));
554 
555 		return mapping;
556 	}
557 
558 }