(no commit message)
[cdmlib.git] / cdmlib-io / src / main / java / eu / etaxonomy / cdm / io / pesi / out / PesiOccurrenceExport.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.HashMap;
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.springframework.stereotype.Component;
22 import org.springframework.transaction.TransactionStatus;
23
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;
38
39 /**
40 * @author a.mueller
41 * @author e.-m.lee
42 * @date 02.03.2010
43 *
44 */
45 @Component
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;
50
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;
59
60 public PesiOccurrenceExport() {
61 super();
62 }
63
64 /* (non-Javadoc)
65 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getStandardMethodParameter()
66 */
67 @Override
68 public Class<? extends CdmBase> getStandardMethodParameter() {
69 return standardMethodParameter;
70 }
71
72 /* (non-Javadoc)
73 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
74 */
75 @Override
76 protected boolean doCheck(PesiExportState state) {
77 boolean result = true;
78 return result;
79 }
80
81 /* (non-Javadoc)
82 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
83 */
84 @Override
85 protected boolean doInvoke(PesiExportState state) {
86 try {
87 logger.error("*** Started Making " + pluralString + " ...");
88
89 // Get the limit for objects to save within a single transaction.
90 int limit = state.getConfig().getLimitSave();
91
92 // Stores whether this invoke was successful or not.
93 boolean success = true;
94
95 // PESI: Clear the database table Occurrence.
96 // doDelete(state);
97
98 // Get specific mappings: (CDM) Occurrence -> (PESI) Occurrence
99 PesiExportMapping mapping = getMapping();
100
101 // Initialize the db mapper
102 mapping.initialize(state);
103
104 // PESI: Create the Occurrences
105 int count = 0;
106 int taxonCount = 0;
107 int pastCount = 0;
108 TransactionStatus txStatus = null;
109 List<TaxonBase> list = null;
110
111 // Start transaction
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) {
115
116 taxonCount += list.size();
117 logger.error("Fetched " + list.size() + " " + parentPluralString + ".");
118 for (TaxonBase taxonBase : list) {
119 if (taxonBase.isInstanceOf(Taxon.class)) {
120
121 // Set the current Taxon
122 taxon = CdmBase.deproxy(taxonBase, Taxon.class);
123
124 // Determine the TaxonDescriptions
125 Set<TaxonDescription> taxonDescriptions = taxon.getDescriptions();
126
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();
132
133 if (descriptionElement.isInstanceOf(Distribution.class)) {
134 Distribution distribution = CdmBase.deproxy(descriptionElement, Distribution.class);
135 setNamedArea(distribution.getArea());
136 setDistribution(distribution);
137
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);
143 }
144 } else {
145 for (DescriptionElementSource elementSource : elementSources) {
146 ReferenceBase reference = elementSource.getCitation();
147
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);
153 }
154 }
155 }
156
157 }
158
159 setDistribution(null);
160 }
161
162 }
163 }
164 }
165 }
166
167 // Commit transaction
168 commitTransaction(txStatus);
169 logger.error("Committed transaction.");
170 logger.error("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
171 pastCount = count;
172
173 // Start transaction
174 txStatus = startTransaction(true);
175 logger.error("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
176 }
177 if (list.size() == 0) {
178 logger.error("No " + parentPluralString + " left to fetch.");
179 }
180 // Commit transaction
181 commitTransaction(txStatus);
182 logger.error("Committed transaction.");
183
184 logger.error("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
185
186 return success;
187 } catch (SQLException e) {
188 e.printStackTrace();
189 logger.error(e.getMessage());
190 return false;
191 }
192 }
193
194 /**
195 * Checks whether needed values for an entity are NULL.
196 * @return
197 */
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());
202 result = false;
203 }
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());
206 result = false;
207 }
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());
210 result = false;
211 }
212 return result;
213 }
214
215 /**
216 * Creates the entries for the database table 'OccurrenceSource'.
217 * @param entity
218 * @param state
219 * @return
220 */
221 protected boolean invokeOccurrenceSource(AnnotatableEntity entity, PesiExportState state) {
222 if (entity == null) {
223 return true;
224 } else {
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();
229
230 try {
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();
238 return true;
239 } catch (SQLException e) {
240 logger.error("SQLException during getOccurrenceId invoke...");
241 e.printStackTrace();
242 return false;
243 }
244 }
245 }
246
247 /**
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.
251 */
252 protected boolean doDelete(PesiExportState state) {
253 PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
254
255 String sql;
256 Source destination = pesiConfig.getDestination();
257
258 // Clear Occurrence
259 sql = "DELETE FROM " + dbTableName;
260 destination.setQuery(sql);
261 destination.update(sql);
262 return true;
263 }
264
265 /* (non-Javadoc)
266 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
267 */
268 @Override
269 protected boolean isIgnore(PesiExportState state) {
270 return ! state.getConfig().isDoOccurrence();
271 }
272
273 /**
274 * Returns the <code>TaxonFk</code> attribute.
275 * @param state The {@link DbExportStateBase DbExportState}.
276 * @return The <code>TaxonFk</code> attribute.
277 * @see MethodMapper
278 */
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());
285 }
286 return result;
287 }
288
289 /**
290 * Returns the <code>TaxonFullNameCache</code> attribute.
291 * @param entity
292 * @return The <code>TaxonFullNameCache</code> attribute.
293 * @see MethodMapper
294 */
295 @SuppressWarnings("unused")
296 private static String getTaxonFullNameCache(AnnotatableEntity entity) {
297 String result = null;
298 result = taxon.getName().getTitleCache();
299 return result;
300 }
301
302 /**
303 * Returns the <code>AreaFk</code> attribute.
304 * @param entity
305 * @return The <code>AreaFk</code> attribute.
306 * @see MethodMapper
307 */
308 private static Integer getAreaFk(AnnotatableEntity entity) {
309 Integer result = null;
310 if (getNamedArea() != null) {
311 result = PesiTransformer.area2AreaId(namedArea);
312 } else {
313 logger.warn("This should never happen, but a NamedArea could not be found for entity: " + entity.getUuid());
314 }
315 return result;
316 }
317
318 /**
319 * Returns the <code>AreaNameCache</code> attribute.
320 * @param entity
321 * @return The <code>AreaNameCache</code> attribute.
322 * @see MethodMapper
323 */
324 @SuppressWarnings("unused")
325 private static String getAreaNameCache(AnnotatableEntity entity) {
326 String result = null;
327 if (getNamedArea() != null) {
328 result = PesiTransformer.area2AreaCache(namedArea);
329 } else {
330 logger.warn("This should never happen, but a NamedArea could not be found for entity: " + entity.getUuid());
331 }
332 return result;
333 }
334
335 /**
336 * @return the distribution
337 */
338 public static Distribution getDistribution() {
339 return distribution;
340 }
341
342 /**
343 * @param distribution the distribution to set
344 */
345 public static void setDistribution(Distribution distribution) {
346 PesiOccurrenceExport.distribution = distribution;
347 }
348
349 /**
350 * @return the namedArea
351 */
352 public static NamedArea getNamedArea() {
353 return namedArea;
354 }
355
356 /**
357 * @param namedArea the namedArea to set
358 */
359 public static void setNamedArea(NamedArea namedArea) {
360 PesiOccurrenceExport.namedArea = namedArea;
361 }
362
363 /**
364 * Returns the <code>OccurrenceStatusFk</code> attribute.
365 * @param entity
366 * @return The <code>OccurrenceStatusFk</code> attribute.
367 * @throws UnknownCdmTypeException
368 * @see MethodMapper
369 */
370 private static Integer getOccurrenceStatusFk(AnnotatableEntity entity) {
371 Integer result = null;
372 if (getDistribution() != null) {
373 result = PesiTransformer.presenceAbsenceTerm2OccurrenceStatusId(getDistribution().getStatus());
374 }
375 return result;
376 }
377
378 /**
379 * Returns the <code>OccurrenceStatusCache</code> attribute.
380 * @param entity
381 * @return The <code>OccurrenceStatusCache</code> attribute.
382 * @throws UnknownCdmTypeException
383 * @see MethodMapper
384 */
385 @SuppressWarnings("unused")
386 private static String getOccurrenceStatusCache(AnnotatableEntity entity) {
387 String result = null;
388 if (getDistribution() != null) {
389 result = PesiTransformer.presenceAbsenceTerm2OccurrenceStatusCache(getDistribution().getStatus());
390 }
391 return result;
392 }
393
394 /**
395 * Returns the <code>SourceFk</code> attribute.
396 * @param entity
397 * @param state The {@link PesiExportState PesiExportState}.
398 * @return The <code>SourceFk</code> attribute.
399 * @see MethodMapper
400 */
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.");
411 }
412 }
413 return result;
414 }
415
416 /**
417 * Returns the <code>SourceCache</code> attribute.
418 * @param entity
419 * @return The <code>SourceCache</code> attribute.
420 * @see MethodMapper
421 */
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();
433 }
434 } else if (sources.size() > 1) {
435 logger.warn("Found Distribution with " + sources.size() + " sources.");
436 }
437 }
438 return result;
439 }
440
441 /**
442 * Returns the <code>Notes</code> attribute.
443 * @param entity
444 * @return The <code>Notes</code> attribute.
445 * @see MethodMapper
446 */
447 @SuppressWarnings("unused")
448 private static String getNotes(AnnotatableEntity entity) {
449 // TODO
450 return null;
451 }
452
453 /**
454 * Returns the CDM to PESI specific export mappings.
455 * @return The {@link PesiExportMapping PesiExportMapping}.
456 */
457 private PesiExportMapping getMapping() {
458 PesiExportMapping mapping = new PesiExportMapping(dbTableName);
459
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());
470
471 return mapping;
472 }
473
474 }