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.ResultSet;
15  import java.sql.SQLException;
16  import java.util.HashSet;
17  import java.util.List;
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.Source;
26  import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
27  import eu.etaxonomy.cdm.model.common.CdmBase;
28  import eu.etaxonomy.cdm.model.common.DescriptionElementSource;
29  import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
30  import eu.etaxonomy.cdm.model.description.TaxonDescription;
31  import eu.etaxonomy.cdm.model.reference.ReferenceBase;
32  import eu.etaxonomy.cdm.model.taxon.Taxon;
33  import eu.etaxonomy.cdm.model.taxon.TaxonBase;
34  
35  /**
36   * The export class for additional information linked to {@link eu.etaxonomy.cdm.model.description.Distribution Distributions} and {@link eu.etaxonomy.cdm.model.common.DescriptionElementSource DescriptionElements}.<p>
37   * Inserts into DataWarehouse database table <code>OccurrenceSource</code>.
38   * @author e.-m.lee
39   * @date 15.03.2010
40   *
41   */
42  @Component
43  @SuppressWarnings("unchecked")
44  public class PesiOccurrenceSourceExport extends PesiExportBase {
45  	private static final Logger logger = Logger.getLogger(PesiOccurrenceSourceExport.class);
46  	private static final Class<? extends CdmBase> standardMethodParameter = AnnotatableEntity.class;
47  
48  	private static int modCount = 1000;
49  	private static final String dbTableName = "OccurrenceSource";
50  	private static final String pluralString = "OccurrenceSources";
51  	private static final String parentPluralString = "Taxa";
52  	private static Taxon taxon = null;
53  
54  	public PesiOccurrenceSourceExport() {
55  		super();
56  	}
57  
58  	/* (non-Javadoc)
59  	 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getStandardMethodParameter()
60  	 */
61  	@Override
62  	public Class<? extends CdmBase> getStandardMethodParameter() {
63  		return standardMethodParameter;
64  	}
65  
66  	/* (non-Javadoc)
67  	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
68  	 */
69  	@Override
70  	protected boolean doCheck(PesiExportState state) {
71  		boolean result = true;
72  		return result;
73  	}
74  
75  	/* (non-Javadoc)
76  	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
77  	 */
78  	@Override
79  	protected boolean doInvoke(PesiExportState state) {
80  		try {
81  			logger.error("*** Started Making " + pluralString + " ...");
82  	
83  			String occurrenceSql = "Insert into OccurrenceSource (OccurrenceFk, SourceFk, SourceNameCache, OldTaxonName) " +
84  			"values (?, ?, ?, ?)";
85  			Connection con = state.getConfig().getDestination().getConnection();
86  			PreparedStatement stmt = con.prepareStatement(occurrenceSql);
87  
88  			// Get the limit for objects to save within a single transaction.
89  			int limit = state.getConfig().getLimitSave();
90  
91  			// Stores whether this invoke was successful or not.
92  			boolean success = true;
93  
94  			// PESI: Clear the database table OccurrenceSource.
95  			doDelete(state);
96  	
97  			// Get specific mappings: (CDM) ? -> (PESI) OccurrenceSource
98  			PesiExportMapping mapping = getMapping();
99  
100 			// Initialize the db mapper
101 			mapping.initialize(state);
102 
103 			// PESI: Create the OccurrenceSource
104 			int count = 0;
105 			int taxonCount = 0;
106 			int pastCount = 0;
107 			TransactionStatus txStatus = null;
108 			List<TaxonBase> list = null;
109 			
110 			// Start transaction
111 			txStatus = startTransaction(true);
112 			logger.error("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
113 			while ((list = getTaxonService().list(null, limit, taxonCount, null, null)).size() > 0) {
114 
115 				logger.error("Fetched " + list.size() + " " + parentPluralString + ".");
116 				taxonCount += list.size();
117 				for (TaxonBase taxonBase : list) {
118 					if (taxonBase.isInstanceOf(Taxon.class)) {
119 
120 						// Set the current Taxon
121 						taxon = CdmBase.deproxy(taxonBase, Taxon.class);
122 
123 						// Determine the TaxonDescriptions
124 						Set<TaxonDescription> taxonDescriptions = taxon.getDescriptions();
125 
126 						// Determine the DescriptionElements (Citations) for the current Taxon
127 						for (TaxonDescription taxonDescription : taxonDescriptions) {
128 							Set<DescriptionElementBase> descriptionElements = taxonDescription.getElements();
129 							for (DescriptionElementBase descriptionElement : descriptionElements) {
130 								Set<DescriptionElementSource> elementSources = descriptionElement.getSources();
131 								
132 								// Focus on descriptionElements with sources.
133 								if (elementSources.size() > 0) {
134 									for (DescriptionElementSource elementSource : elementSources) {
135 										ReferenceBase reference = elementSource.getCitation();
136 
137 										// Citations can be empty (null): Is it wrong data or just a normal case?
138 										if (reference != null) {
139 
140 											// Lookup sourceFk
141 											Integer sourceFk = getSourceFk(reference, state);
142 											
143 											if (sourceFk != null && ! state.alreadyProcessedSource(sourceFk)) {
144 												doCount(count++, modCount, pluralString);
145 												
146 												// Add to processed sourceFk's since sourceFk's can be scanned more than once.
147 												state.addToProcessedSources(sourceFk);
148 												
149 												// Query the database for all entries in table 'Occurrence' with the sourceFk just determined.
150 												Set<Integer> occurrenceIds = getOccurrenceIds(sourceFk, state);
151 	
152 												// Insert as many entries in database table 'OccurrenceSource' as occurrenceId's were determined.
153 												insertColumns(stmt, reference, sourceFk, occurrenceIds);
154 											}
155 
156 										}
157 									}
158 								}
159 								
160 							}
161 						}
162 					}
163 				}
164 				
165 				state.clearAlreadyProcessedSources();
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 " + pluralString + " 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 	 * Inserts columns in the database table OccurrenceSource.
196 	 * @param stmt The prepared statement.
197 	 * @param reference {@link ReferenceBase Reference}.
198 	 * @param sourceFk The SourceFk.
199 	 * @param occurrenceIds A {@link java.util.Set Set} of OccurrenceId's.
200 	 */
201 	private void insertColumns(PreparedStatement stmt, ReferenceBase reference,
202 			Integer sourceFk, Set<Integer> occurrenceIds) {
203 		for (Integer occurrenceId : occurrenceIds) {
204 			try {
205 				stmt.setInt(1, occurrenceId);
206 				stmt.setInt(2, sourceFk);
207 				stmt.setString(3, getSourceNameCache(reference));
208 				stmt.setString(4, null); // TODO: This is the name of the former taxon (accepted taxon as well as synonym) the source was associated to. How can we get a hand on it?
209 				stmt.execute();
210 			} catch (SQLException e) {
211 				logger.error("SQLException during getOccurrenceId invoke.");
212 				e.printStackTrace();
213 			}
214 		}
215 	}
216 
217 	/**
218 	 * Returns a Set of OccurrenceId's associated to a given SourceFk.
219 	 * @param state The {@link PesiExportState PesiExportState}.
220 	 * @return Existing OccurrenceId's for a given SourceFk.
221 	 */
222 	private static Set<Integer> getOccurrenceIds(Integer sourceFk, PesiExportState state) {
223 		String occurrenceSql = "Select OccurrenceId From Occurrence where SourceFk = ?";
224 		Connection con = state.getConfig().getDestination().getConnection();
225 		PreparedStatement stmt = null;
226 		Set<Integer> occurrenceIds = new HashSet();
227 		
228 		try {
229 			stmt = con.prepareStatement(occurrenceSql);
230 			stmt.setInt(1, sourceFk);
231 			ResultSet resultSet = stmt.executeQuery();
232 			while (resultSet.next()) {
233 				occurrenceIds.add(resultSet.getInt(1));
234 			}
235 		} catch (SQLException e) {
236 			logger.error("SQLException during getOccurrenceId invoke. (2)");
237 			e.printStackTrace();
238 		}
239 
240 		return occurrenceIds;
241 	}
242 
243 	/**
244 	 * Deletes all entries of database tables related to <code>OccurrenceSource</code>.
245 	 * @param state The {@link PesiExportState PesiExportState}.
246 	 * @return Whether the delete operation was successful or not.
247 	 */
248 	protected boolean doDelete(PesiExportState state) {
249 		PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
250 		
251 		String sql;
252 		Source destination =  pesiConfig.getDestination();
253 
254 		// Clear OccurrenceSource
255 		sql = "DELETE FROM " + dbTableName;
256 		destination.setQuery(sql);
257 		destination.update(sql);
258 		return true;
259 	}
260 
261 	/* (non-Javadoc)
262 	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
263 	 */
264 	@Override
265 	protected boolean isIgnore(PesiExportState state) {
266 		// TODO
267 //		return ! state.getConfig().isDoOccurrenceSource();
268 		return false;
269 	}
270 
271 	/**
272 	 * Returns the <code>OccurrenceFk</code> attribute.
273 	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
274 	 * @param state The {@link PesiExportState PesiExportState}.
275 	 * @return The <code>OccurrenceFk</code> attribute.
276 	 * @see MethodMapper
277 	 */
278 	@SuppressWarnings("unused")
279 	private static Integer getOccurrenceFk(AnnotatableEntity entity, PesiExportState state) {
280 		Integer result = null;
281 		return result;
282 	}
283 	
284 	/**
285 	 * Returns the <code>SourceFk</code> attribute.
286 	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
287 	 * @param state The {@link PesiExportState PesiExportState}.
288 	 * @return The <code>SourceFk</code> attribute.
289 	 * @see MethodMapper
290 	 */
291 	private static Integer getSourceFk(AnnotatableEntity entity, PesiExportState state) {
292 		Integer result = null;
293 		if (state != null && entity != null && entity.isInstanceOf(ReferenceBase.class)) {
294 			ReferenceBase reference = CdmBase.deproxy(entity, ReferenceBase.class);
295 			result = state.getDbId(reference);
296 		}
297 		return result;
298 	}
299 	
300 	/**
301 	 * Returns the <code>SourceNameCache</code> attribute.
302 	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
303 	 * @return The <code>SourceNameCache</code> attribute.
304 	 * @see MethodMapper
305 	 */
306 	private static String getSourceNameCache(AnnotatableEntity entity) {
307 		String result = null;
308 		if (entity != null && entity.isInstanceOf(ReferenceBase.class)) {
309 			ReferenceBase reference = CdmBase.deproxy(entity, ReferenceBase.class);
310 			result = reference.getTitle();
311 		}
312 		return result;
313 	}
314 	
315 	/**
316 	 * Returns the <code>OldTaxonName</code> attribute.
317 	 * @param entity An {@link AnnotatableEntity AnnotatableEntity}.
318 	 * @return The <code>OldTaxonName</code> attribute.
319 	 * @see MethodMapper
320 	 */
321 	@SuppressWarnings("unused")
322 	private static String getOldTaxonName(AnnotatableEntity entity) {
323 		// TODO: This is the name of the former taxon (accepted taxon as well as synonym) the source was associated to.
324 		return null;
325 	}
326 
327 	/**
328 	 * Returns the CDM to PESI specific export mappings.
329 	 * @return The {@link PesiExportMapping PesiExportMapping}.
330 	 */
331 	private PesiExportMapping getMapping() {
332 		PesiExportMapping mapping = new PesiExportMapping(dbTableName);
333 
334 		// These mapping are not used.
335 		mapping.addMapper(MethodMapper.NewInstance("OccurrenceFk", this.getClass(), "getOccurrenceFk", standardMethodParameter, PesiExportState.class));
336 		mapping.addMapper(MethodMapper.NewInstance("SourceFk", this.getClass(), "getSourceFk", standardMethodParameter, PesiExportState.class));
337 		mapping.addMapper(MethodMapper.NewInstance("SourceNameCache", this));
338 		mapping.addMapper(MethodMapper.NewInstance("OldTaxonName", this));
339 
340 		return mapping;
341 	}
342 
343 }