Project

General

Profile

Download (16.6 KB) Statistics
| Branch: | Revision:
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.Reference;
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
 * The export class for {@link eu.etaxonomy.cdm.model.description.Distribution Distributions}.<p>
41
 * Inserts into DataWarehouse database table <code>Occurrence</code>.
42
 * @author e.-m.lee
43
 * @date 02.03.2010
44
 *
45
 */
46
@Component
47
@SuppressWarnings("unchecked")
48
public class PesiOccurrenceExport extends PesiExportBase {
49
	private static final Logger logger = Logger.getLogger(PesiOccurrenceExport.class);
50
	private static final Class<? extends CdmBase> standardMethodParameter = AnnotatableEntity.class;
51

    
52
	private static int modCount = 1000;
53
	private static final String dbTableName = "Occurrence";
54
	private static final String pluralString = "Occurrences";
55
	private static final String parentPluralString = "Taxa";
56
	private static Taxon taxon = null;
57
	private static Map<Integer, Integer> sourceId2OccurenceIdMap = new HashMap<Integer, Integer>();
58
	private static NamedArea namedArea = null;
59
	private static Distribution distribution = null;
60

    
61
	public PesiOccurrenceExport() {
62
		super();
63
	}
64

    
65
	/* (non-Javadoc)
66
	 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getStandardMethodParameter()
67
	 */
68
	@Override
69
	public Class<? extends CdmBase> getStandardMethodParameter() {
70
		return standardMethodParameter;
71
	}
72

    
73
	/* (non-Javadoc)
74
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
75
	 */
76
	@Override
77
	protected boolean doCheck(PesiExportState state) {
78
		boolean result = true;
79
		return result;
80
	}
81

    
82
	/* (non-Javadoc)
83
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
84
	 */
85
	@Override
86
	protected void doInvoke(PesiExportState state) {
87
		try {
88
			logger.error("*** Started Making " + pluralString + " ...");
89
	
90
			// Get the limit for objects to save within a single transaction.
91
			int limit = state.getConfig().getLimitSave();
92

    
93
			// Stores whether this invoke was successful or not.
94
			boolean success = true;
95

    
96
			// PESI: Clear the database table Occurrence.
97
//			doDelete(state);
98
	
99
			// Get specific mappings: (CDM) Occurrence -> (PESI) Occurrence
100
			PesiExportMapping mapping = getMapping();
101

    
102
			// Initialize the db mapper
103
			mapping.initialize(state);
104

    
105
			// PESI: Create the Occurrences
106
			int count = 0;
107
			int taxonCount = 0;
108
			int pastCount = 0;
109
			TransactionStatus txStatus = null;
110
			List<TaxonBase> list = null;
111

    
112
			// Start transaction
113
			txStatus = startTransaction(true);
114
			logger.error("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
115
			while ((list = getTaxonService().list(null, limit, taxonCount, null, null)).size() > 0) {
116

    
117
				taxonCount += list.size();
118
				logger.error("Fetched " + list.size() + " " + parentPluralString + ".");
119
				for (TaxonBase taxonBase : list) {
120
					if (taxonBase.isInstanceOf(Taxon.class)) {
121
						
122
						// Set the current Taxon
123
						taxon = CdmBase.deproxy(taxonBase, Taxon.class);
124

    
125
						// Determine the TaxonDescriptions
126
						Set<TaxonDescription> taxonDescriptions = taxon.getDescriptions();
127

    
128
						// Determine the DescriptionElements (Citations) for the current Taxon
129
						for (TaxonDescription taxonDescription : taxonDescriptions) {
130
							Set<DescriptionElementBase> descriptionElements = taxonDescription.getElements();
131
							for (DescriptionElementBase descriptionElement : descriptionElements) {
132
								Set<DescriptionElementSource> elementSources = descriptionElement.getSources();
133
								
134
								if (descriptionElement.isInstanceOf(Distribution.class)) {
135
									Distribution distribution = CdmBase.deproxy(descriptionElement, Distribution.class);
136
									setNamedArea(distribution.getArea());
137
									setDistribution(distribution);
138

    
139
									// Differentiate between descriptionElements with and without sources.
140
									if (elementSources.size() == 0 && state.getDbId(descriptionElement) != null) {
141
										if (neededValuesNotNull(descriptionElement, state)) {
142
											doCount(count++, modCount, pluralString);
143
											success &= mapping.invoke(descriptionElement);
144
										}
145
									} else {
146
										for (DescriptionElementSource elementSource : elementSources) {
147
											Reference reference = elementSource.getCitation();
148
	
149
											// Citations can be empty (null): Is it wrong data or just a normal case?
150
											if (reference != null && state.getDbId(reference) != null) {
151
												if (neededValuesNotNull(reference, state)) {
152
													doCount(count++, modCount, pluralString);
153
													success &= mapping.invoke(reference);
154
												}
155
											}
156
										}
157
										
158
									}
159
									
160
									setDistribution(null);
161
								}
162
								
163
							}
164
						}
165
					}
166
				}
167
				
168
				// Commit transaction
169
				commitTransaction(txStatus);
170
				logger.error("Committed transaction.");
171
				logger.error("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
172
				pastCount = count;
173

    
174
				// Start transaction
175
				txStatus = startTransaction(true);
176
				logger.error("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
177
			}
178
			if (list.size() == 0) {
179
				logger.error("No " + parentPluralString + " left to fetch.");
180
			}
181
			// Commit transaction
182
			commitTransaction(txStatus);
183
			logger.error("Committed transaction.");
184

    
185
			logger.error("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
186
			
187
			if (!success){
188
				state.setUnsuccessfull();
189
			}
190
			return;
191
		} catch (SQLException e) {
192
			e.printStackTrace();
193
			logger.error(e.getMessage());
194
			state.setUnsuccessfull();
195
			return;
196
		}
197
	}
198

    
199
	/**
200
	 * Checks whether needed values for an entity are NULL.
201
	 * @return
202
	 */
203
	private boolean neededValuesNotNull(AnnotatableEntity entity, PesiExportState state) {
204
		boolean result = true;
205
		if (getTaxonFk(entity, state) == null) {
206
			logger.error("TaxonFk is NULL, but is not allowed to be. Therefore no record was written to export database for this entity: " + entity.getUuid());
207
			result = false;
208
		}
209
		if (getAreaFk(entity) == null) {
210
			logger.error("AreaFk is NULL, but is not allowed to be. Therefore no record was written to export database for this entity: " + entity.getUuid());
211
			result = false;
212
		}
213
		if (getOccurrenceStatusFk(entity) == null) {
214
			logger.error("OccurrenceStatusFk is NULL, but is not allowed to be. Therefore no record was written to export database for this entity: " + entity.getUuid());
215
			result = false;
216
		}
217
		return result;
218
	}
219

    
220
	/**
221
	 * Creates the entries for the database table 'OccurrenceSource'.
222
	 * @param entity
223
	 * @param state
224
	 * @return
225
	 */
226
	protected boolean invokeOccurrenceSource(AnnotatableEntity entity, PesiExportState state) {
227
		if (entity == null) {
228
			return true;
229
		} else {
230
			// Create OccurrenceSource database table entry
231
			String lastStoredRecordSql = "Insert Into OccurrenceSource (OccurrenceFk, SourceFk, SourceNameCache, OldTaxonName) " +
232
					"values(?, ?, ?, ?)";
233
			Connection con = state.getConfig().getDestination().getConnection();
234

    
235
			try {
236
				PreparedStatement stmt = con.prepareStatement(lastStoredRecordSql);
237
				Integer sourceFk = getSourceFk(entity, state);
238
				stmt.setInt(1, sourceId2OccurenceIdMap.get(sourceFk));
239
				stmt.setInt(2, sourceFk);
240
				stmt.setString(3, getSourceCache(entity));
241
				stmt.setString(4, null); // Which taxon are we talking about?
242
				stmt.executeUpdate();
243
				return true;
244
			} catch (SQLException e) {
245
				logger.error("SQLException during getOccurrenceId invoke...");
246
				e.printStackTrace();
247
				return false;
248
			}
249
		}
250
	}
251

    
252
	/**
253
	 * Deletes all entries of database tables related to <code>Occurrence</code>.
254
	 * @param state The {@link PesiExportState PesiExportState}.
255
	 * @return Whether the delete operation was successful or not.
256
	 */
257
	protected boolean doDelete(PesiExportState state) {
258
		PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
259
		
260
		String sql;
261
		Source destination =  pesiConfig.getDestination();
262

    
263
		// Clear Occurrence
264
		sql = "DELETE FROM " + dbTableName;
265
		destination.setQuery(sql);
266
		destination.update(sql);
267
		return true;
268
	}
269

    
270
	/* (non-Javadoc)
271
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
272
	 */
273
	@Override
274
	protected boolean isIgnore(PesiExportState state) {
275
		return ! state.getConfig().isDoOccurrence();
276
	}
277

    
278
	/**
279
	 * Returns the <code>TaxonFk</code> attribute.
280
	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
281
	 * @param state The {@link PesiExportState PesiExportState}.
282
	 * @return The <code>TaxonFk</code> attribute.
283
	 * @see MethodMapper
284
	 */
285
	private static Integer getTaxonFk(AnnotatableEntity entity, PesiExportState state) {
286
		// AnnotatableEntity parameter isn't needed, but the DbSingleAttributeExportMapperBase throws a type mismatch exception otherwise
287
		// since it awaits two parameters if one of them is of instance DbExportStateBase.
288
		Integer result = null;
289
		if (state != null && taxon != null) {
290
			result = state.getDbId(taxon.getName());
291
		}
292
		return result;
293
	}
294

    
295
	/**
296
	 * Returns the <code>TaxonFullNameCache</code> attribute.
297
	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
298
	 * @return The <code>TaxonFullNameCache</code> attribute.
299
	 * @see MethodMapper
300
	 */
301
	@SuppressWarnings("unused")
302
	private static String getTaxonFullNameCache(AnnotatableEntity entity) {
303
		String result = null;
304
		result = taxon.getName().getTitleCache();
305
		return result;
306
	}
307

    
308
	/**
309
	 * Returns the <code>AreaFk</code> attribute.
310
	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
311
	 * @return The <code>AreaFk</code> attribute.
312
	 * @see MethodMapper
313
	 */
314
	private static Integer getAreaFk(AnnotatableEntity entity) {
315
		Integer result = null;
316
		if (getNamedArea() != null) {
317
			result = PesiTransformer.area2AreaId(namedArea);
318
		} else {
319
			logger.warn("This should never happen, but a NamedArea could not be found for entity: " + entity.getUuid());
320
		}
321
		return result;
322
	}
323

    
324
	/**
325
	 * Returns the <code>AreaNameCache</code> attribute.
326
	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
327
	 * @return The <code>AreaNameCache</code> attribute.
328
	 * @see MethodMapper
329
	 */
330
	@SuppressWarnings("unused")
331
	private static String getAreaNameCache(AnnotatableEntity entity) {
332
		String result = null;
333
		if (getNamedArea() != null) {
334
			result = PesiTransformer.area2AreaCache(namedArea);
335
		} else {
336
			logger.warn("This should never happen, but a NamedArea could not be found for entity: " + entity.getUuid());
337
		}
338
		return result;
339
	}
340

    
341
	/**
342
	 * @return the distribution
343
	 */
344
	public static Distribution getDistribution() {
345
		return distribution;
346
	}
347

    
348
	/**
349
	 * @param distribution the distribution to set
350
	 */
351
	public static void setDistribution(Distribution distribution) {
352
		PesiOccurrenceExport.distribution = distribution;
353
	}
354

    
355
	/**
356
	 * @return the namedArea
357
	 */
358
	public static NamedArea getNamedArea() {
359
		return namedArea;
360
	}
361

    
362
	/**
363
	 * @param namedArea the namedArea to set
364
	 */
365
	public static void setNamedArea(NamedArea namedArea) {
366
		PesiOccurrenceExport.namedArea = namedArea;
367
	}
368

    
369
	/**
370
	 * Returns the <code>OccurrenceStatusFk</code> attribute.
371
	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
372
	 * @return The <code>OccurrenceStatusFk</code> attribute.
373
	 * @throws UnknownCdmTypeException 
374
	 * @see MethodMapper
375
	 */
376
	private static Integer getOccurrenceStatusFk(AnnotatableEntity entity) {
377
		Integer result = null;
378
		if (getDistribution() != null) {
379
			result = PesiTransformer.presenceAbsenceTerm2OccurrenceStatusId(getDistribution().getStatus());
380
		}
381
		return result;
382
	}
383

    
384
	/**
385
	 * Returns the <code>OccurrenceStatusCache</code> attribute.
386
	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
387
	 * @return The <code>OccurrenceStatusCache</code> attribute.
388
	 * @throws UnknownCdmTypeException 
389
	 * @see MethodMapper
390
	 */
391
	@SuppressWarnings("unused")
392
	private static String getOccurrenceStatusCache(AnnotatableEntity entity) {
393
		String result = null;
394
		if (getDistribution() != null) {
395
			result = PesiTransformer.presenceAbsenceTerm2OccurrenceStatusCache(getDistribution().getStatus());
396
		}
397
		return result;
398
	}
399

    
400
	/**
401
	 * Returns the <code>SourceFk</code> attribute.
402
	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
403
	 * @param state The {@link PesiExportState PesiExportState}.
404
	 * @return The <code>SourceFk</code> attribute.
405
	 * @see MethodMapper
406
	 */
407
	private static Integer getSourceFk(AnnotatableEntity entity, PesiExportState state) {
408
		Integer result = null;
409
		if (state != null && entity != null && entity.isInstanceOf(Distribution.class)) {
410
			Distribution distribution = CdmBase.deproxy(entity, Distribution.class);
411
			Set<DescriptionElementSource> sources = distribution.getSources();
412
			if (sources.size() == 1) {
413
				DescriptionElementSource source = sources.iterator().next();
414
				result = state.getDbId(source.getCitation());
415
			} else if (sources.size() > 1) {
416
				logger.warn("Found Distribution with " + sources.size() + " sources.");
417
			}
418
		}
419
		return result;
420
	}
421

    
422
	/**
423
	 * Returns the <code>SourceCache</code> attribute.
424
	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
425
	 * @return The <code>SourceCache</code> attribute.
426
	 * @see MethodMapper
427
	 */
428
	private static String getSourceCache(AnnotatableEntity entity) {
429
		String result = null;
430
		Reference reference;
431
		if (entity != null && entity.isInstanceOf(Distribution.class)) {
432
			Distribution distribution = CdmBase.deproxy(entity, Distribution.class);
433
			Set<DescriptionElementSource> sources = distribution.getSources();
434
			if (sources.size() == 1) {
435
				DescriptionElementSource source = sources.iterator().next();
436
				reference = source.getCitation();
437
				if (reference != null) {
438
					result = reference.getTitle();
439
				}
440
			} else if (sources.size() > 1) {
441
				logger.warn("Found Distribution with " + sources.size() + " sources.");
442
			}
443
		}
444
		return result;
445
	}
446

    
447
	/**
448
	 * Returns the <code>Notes</code> attribute.
449
	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
450
	 * @return The <code>Notes</code> attribute.
451
	 * @see MethodMapper
452
	 */
453
	@SuppressWarnings("unused")
454
	private static String getNotes(AnnotatableEntity entity) {
455
		// TODO
456
		return null;
457
	}
458

    
459
	/**
460
	 * Returns the CDM to PESI specific export mappings.
461
	 * @return The {@link PesiExportMapping PesiExportMapping}.
462
	 */
463
	private PesiExportMapping getMapping() {
464
		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
465
		
466
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk", this.getClass(), "getTaxonFk", standardMethodParameter, PesiExportState.class));
467
		mapping.addMapper(MethodMapper.NewInstance("AreaFk", this));
468
		mapping.addMapper(MethodMapper.NewInstance("TaxonFullNameCache", this));
469
		mapping.addMapper(MethodMapper.NewInstance("AreaNameCache", this));
470
		mapping.addMapper(MethodMapper.NewInstance("OccurrenceStatusFk", this));
471
		mapping.addMapper(MethodMapper.NewInstance("OccurrenceStatusCache", this));
472
		mapping.addMapper(MethodMapper.NewInstance("SourceFk", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
473
		mapping.addMapper(MethodMapper.NewInstance("SourceCache", this));
474
		mapping.addMapper(MethodMapper.NewInstance("Notes", this));
475

    
476
		return mapping;
477
	}
478

    
479
}
(10-10/15)