Project

General

Profile

Download (40.2 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 static java.util.EnumSet.of;
13

    
14
import java.sql.Connection;
15
import java.sql.PreparedStatement;
16
import java.sql.SQLException;
17
import java.sql.Types;
18
import java.util.Arrays;
19
import java.util.HashSet;
20
import java.util.List;
21
import java.util.Map;
22
import java.util.Set;
23

    
24
import org.apache.log4j.Logger;
25
import org.springframework.stereotype.Component;
26
import org.springframework.transaction.TransactionStatus;
27

    
28
import eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer;
29
import eu.etaxonomy.cdm.io.common.DbExportStateBase;
30
import eu.etaxonomy.cdm.io.common.Source;
31
import eu.etaxonomy.cdm.io.common.mapping.DbIgnoreMapper;
32
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
33
import eu.etaxonomy.cdm.io.common.mapping.out.CollectionExportMapping;
34
import eu.etaxonomy.cdm.io.common.mapping.out.DbAreaMapper;
35
import eu.etaxonomy.cdm.io.common.mapping.out.DbConstantMapper;
36
import eu.etaxonomy.cdm.io.common.mapping.out.DbDescriptionElementTaxonMapper;
37
import eu.etaxonomy.cdm.io.common.mapping.out.DbDistributionStatusMapper;
38
import eu.etaxonomy.cdm.io.common.mapping.out.DbExportIgnoreMapper;
39
import eu.etaxonomy.cdm.io.common.mapping.out.DbExportNotYetImplementedMapper;
40
import eu.etaxonomy.cdm.io.common.mapping.out.DbLanguageMapper;
41
import eu.etaxonomy.cdm.io.common.mapping.out.DbObjectMapper;
42
import eu.etaxonomy.cdm.io.common.mapping.out.DbOriginalNameMapper;
43
import eu.etaxonomy.cdm.io.common.mapping.out.DbSimpleFilterMapper;
44
import eu.etaxonomy.cdm.io.common.mapping.out.DbSingleSourceMapper;
45
import eu.etaxonomy.cdm.io.common.mapping.out.DbStringMapper;
46
import eu.etaxonomy.cdm.io.common.mapping.out.DbTextDataMapper;
47
import eu.etaxonomy.cdm.io.common.mapping.out.DbTimePeriodMapper;
48
import eu.etaxonomy.cdm.io.common.mapping.out.IdMapper;
49
import eu.etaxonomy.cdm.io.common.mapping.out.MethodMapper;
50
import eu.etaxonomy.cdm.io.profiler.ProfilerController;
51
import eu.etaxonomy.cdm.model.common.CdmBase;
52
import eu.etaxonomy.cdm.model.common.Extension;
53
import eu.etaxonomy.cdm.model.common.ExtensionType;
54
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
55
import eu.etaxonomy.cdm.model.common.Language;
56
import eu.etaxonomy.cdm.model.common.LanguageString;
57
import eu.etaxonomy.cdm.model.common.Marker;
58
import eu.etaxonomy.cdm.model.common.MarkerType;
59
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
60
import eu.etaxonomy.cdm.model.description.DescriptionBase;
61
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
62
import eu.etaxonomy.cdm.model.description.Distribution;
63
import eu.etaxonomy.cdm.model.description.Feature;
64
import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
65
import eu.etaxonomy.cdm.model.description.TaxonInteraction;
66
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
67
import eu.etaxonomy.cdm.model.description.TextData;
68
import eu.etaxonomy.cdm.model.location.NamedArea;
69
import eu.etaxonomy.cdm.model.location.TdwgArea;
70
import eu.etaxonomy.cdm.model.name.NonViralName;
71
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
72
import eu.etaxonomy.cdm.model.reference.Reference;
73
import eu.etaxonomy.cdm.model.taxon.Taxon;
74
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
75
/**
76
 * The export class for {@link eu.etaxonomy.cdm.model.description.DescriptionElementBase DescriptionElements}.<p>
77
 * Inserts into DataWarehouse database table <code>Note</code>.<p>
78
 * It is divided into two phases:<ul>
79
 * <li>Phase 1:	Export of DescriptionElements as Notes.
80
 * <li>Phase 2:	Export of TaxonName extensions <code>taxComment</code>, <code>fauComment</code> and <code>fauExtraCodes</code> as Notes.</ul>
81
 * @author e.-m.lee
82
 * @date 23.02.2010
83
 *
84
 */
85
@Component
86
public class PesiDescriptionExport extends PesiExportBase {
87
	private static final Logger logger = Logger.getLogger(PesiDescriptionExport.class);
88
	
89
	private static final Class<? extends CdmBase> standardMethodParameter = DescriptionElementBase.class;
90

    
91
	private static int modCount = 1000;
92
	private static final String dbNoteTableName = "Note";
93
	private static final String dbOccurrenceTableName = "Occurrence";
94
	private static final String dbVernacularTableName = "CommonName";
95
	private static final String dbImageTableName = "Image";
96
	private static final String dbAdditionalSourceTableName = "AdditionalTaxonSource";
97
	private static final String pluralString = "attached infos";
98
	private static final String parentPluralString = "Taxa";
99

    
100
	//decide where to handle them best (configurator, transformer, single method, ...)
101
	private static Set<Integer> excludedNoteCategories = new HashSet<Integer>(Arrays.asList(new Integer[]{250,251,252,253,10,11,13}));
102

    
103
	
104
	//debugging
105
	private static int countDescriptions;
106
	private static int countTaxa;
107
	private static int countDistribution;
108
	private static int countAdditionalSources;
109
	private static int countImages;
110
	private static int countNotes;
111
	
112
	private static int countCommonName;
113
	private static int countOccurrence;
114
	private static int countOthers;
115
	
116
	public PesiDescriptionExport() {
117
		super();
118
	}
119

    
120
	/* (non-Javadoc)
121
	 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getStandardMethodParameter()
122
	 */
123
	@Override
124
	public Class<? extends CdmBase> getStandardMethodParameter() {
125
		return standardMethodParameter;
126
	}
127

    
128
	/* (non-Javadoc)
129
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
130
	 */
131
	@Override
132
	protected boolean doCheck(PesiExportState state) {
133
		boolean result = true;
134
		return result;
135
	}
136

    
137
	/* (non-Javadoc)
138
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doInvoke(eu.etaxonomy.cdm.io.common.IoStateBase)
139
	 */
140
	@Override
141
	protected void doInvoke(PesiExportState state) {
142
		try {
143
			logger.info("*** Started Making " + pluralString + " ...");
144

    
145
			// Stores whether this invoke was successful or not.
146
			boolean success = true;
147
	
148
			// Get specific mappings: (CDM) DescriptionElement -> (PESI) Note
149
			PesiExportMapping notesMapping = getNotesMapping();
150
			notesMapping.initialize(state);
151

    
152
			// Get specific mappings: (CDM) DescriptionElement -> (PESI) Occurrence
153
			PesiExportMapping occurrenceMapping = getOccurrenceMapping();
154
			occurrenceMapping.initialize(state);
155

    
156
			// Get specific mappings: (CDM) DescriptionElement -> (PESI) Additional taxon source
157
			PesiExportMapping addSourceSourceMapping = getAddTaxonSourceSourceMapping();
158
			addSourceSourceMapping.initialize(state);
159
			PesiExportMapping additionalSourceMapping = getAdditionalTaxonSourceMapping();
160
			additionalSourceMapping.initialize(state);
161

    
162
			// Get specific mappings: (CDM) DescriptionElement -> (PESI) Additional taxon source
163
			PesiExportMapping vernacularMapping = getVernacularNamesMapping();
164
			vernacularMapping.initialize(state);
165
			
166
			// Get specific mappings: (CDM) DescriptionElement -> (PESI) Additional taxon source
167
			PesiExportMapping imageMapping = getImageMapping();
168
			imageMapping.initialize(state);
169
			
170
			// Start transaction
171
			success &= doPhase01(state, notesMapping, occurrenceMapping, addSourceSourceMapping, additionalSourceMapping, vernacularMapping, imageMapping);
172

    
173
			// Start transaction
174
			success &= doPhase01b(state, notesMapping, occurrenceMapping, addSourceSourceMapping, additionalSourceMapping, vernacularMapping, imageMapping);
175

    
176
			
177
			logger.info("PHASE 2...");
178
			success &= doPhase02(state);
179

    
180

    
181
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
182
			
183
			if (!success){
184
				state.setUnsuccessfull();
185
			}
186
			return;
187
		} catch (SQLException e) {
188
			e.printStackTrace();
189
			logger.error(e.getMessage());
190
			state.setUnsuccessfull();
191
		}
192
	}
193

    
194
	//PHASE 01: Description Elements
195
	private boolean doPhase01(PesiExportState state, PesiExportMapping notesMapping, PesiExportMapping occurrenceMapping, PesiExportMapping addSourceSourceMapping, 
196
			PesiExportMapping additionalSourceMapping, PesiExportMapping vernacularMapping, PesiExportMapping imageMapping) throws SQLException {
197
		logger.info("PHASE 1...");
198
		int count = 0;
199
		int pastCount = 0;
200
		boolean success = true;
201
		int limit = state.getConfig().getLimitSave();
202

    
203
		List<Taxon> taxonList = null;
204
		
205
		TransactionStatus txStatus = startTransaction(true);
206
		logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
207
		List<String> propPath = Arrays.asList(new String[]{"descriptions.elements.*"});
208
		
209
		logger.debug("Start snapshot, before starting loop");
210
		ProfilerController.memorySnapshot();
211
		//taxon descriptions
212
		int partitionCount = 0;
213
		while ((taxonList = getNextTaxonPartition(Taxon.class, limit, partitionCount++, propPath )) != null   ) {
214

    
215
			logger.info("Fetched " + taxonList.size() + " " + pluralString + ". Exporting...");
216
			
217
			for (Taxon taxon : taxonList) {
218
				countTaxa++;
219
				doCount(count++, modCount, pluralString);
220
				state.setCurrentTaxon(taxon);
221
				success &= handleSingleTaxon(taxon, state, notesMapping, occurrenceMapping, addSourceSourceMapping, 
222
						additionalSourceMapping, vernacularMapping, imageMapping);	
223
			}
224
			taxonList = null;
225
			state.setCurrentTaxon(null);
226

    
227
			// Commit transaction
228
			commitTransaction(txStatus);
229
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
230
			pastCount = count;
231

    
232
			// Start transaction
233
			txStatus = startTransaction(true);
234
			logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") for description import ...");
235
		}
236
		
237
	
238
		logger.info("No " + pluralString + " left to fetch.");
239
		logger.info("Partition: " + partitionCount);
240
		logger.info("Taxa: " + countTaxa);
241
		logger.info("Desc: " + countDescriptions);
242
		logger.info("Distr: " + countDistribution);
243
		logger.info("Occur: " + countOccurrence);
244
		logger.info("Commons: " + countCommonName);
245
		logger.info("AddSrc: " + countAdditionalSources);
246
		logger.info("Images: " + countImages);
247
		logger.info("Notes: " + countNotes);
248
		logger.info("Others: " + countOthers);
249
		
250
		// Commit transaction
251
		commitTransaction(txStatus);
252
		logger.debug("Committed transaction.");
253
		return success;
254
	}
255
	
256
	//PHASE 01b: Name Descriptions
257
	private boolean doPhase01b(PesiExportState state, PesiExportMapping notesMapping, PesiExportMapping occurrenceMapping, PesiExportMapping addSourceSourceMapping, 
258
			PesiExportMapping additionalSourceMapping, PesiExportMapping vernacularMapping, PesiExportMapping imageMapping) throws SQLException {
259
		logger.info("PHASE 1b...");
260
		int count = 0;
261
		int pastCount = 0;
262
		boolean success = true;
263
		int limit = state.getConfig().getLimitSave();
264
		
265
		List<TaxonNameDescription> nameDescList = null;
266
		
267
		TransactionStatus txStatus = startTransaction(true);
268
		logger.info("Started new transaction. Fetching some name descriptions (max: " + limit + ") ...");
269
		List<String> propPath = Arrays.asList(new String[]{"descriptions.elements.*"});
270
		
271
		//name descriptions
272
		int partitionCount = 0;
273
		while ((nameDescList = getNextNameDescriptionPartition( limit, partitionCount++, propPath )) != null   ) {
274
			
275
			logger.info("Fetched " + nameDescList.size() + " name descriptions. Exporting...");
276
			
277
			for (TaxonNameDescription desc : nameDescList) {
278
				countTaxa++;
279
				doCount(count++, modCount, "name descriptions");
280
				boolean isImageGallery = desc.isImageGallery();
281
				
282
				TaxonNameBase<?,?> name = desc.getTaxonName();
283
				
284
				for (DescriptionElementBase element : desc.getElements()){
285
					if (isPurePesiName(name)){
286
						success &= handleDescriptionElement(state, notesMapping, occurrenceMapping, vernacularMapping, imageMapping,
287
								addSourceSourceMapping, additionalSourceMapping, isImageGallery, element);
288
					}else{
289
						for (TaxonBase<?> taxonBase : name.getTaxonBases()){
290
							if (isPesiTaxon(taxonBase)){
291
								state.setCurrentTaxon(taxonBase);
292
								success &= handleDescriptionElement(state, notesMapping, occurrenceMapping, vernacularMapping, imageMapping,
293
										addSourceSourceMapping, additionalSourceMapping, isImageGallery, element);
294
								state.setSourceForAdditionalSourceCreated(true);
295
							}
296
						}
297
						state.setSourceForAdditionalSourceCreated(false);
298
					}
299
				}
300
			}
301
			nameDescList = null;
302
			state.setCurrentTaxon(null);
303

    
304
			// Commit transaction
305
			commitTransaction(txStatus);
306
			logger.info("Exported " + (count - pastCount) + " name descriptions. Total: " + count);
307
			pastCount = count;
308

    
309
			// Start transaction
310
			txStatus = startTransaction(true);
311
			logger.info("Started new transaction. Fetching some name descriptions (max: " + limit + ") for description import ...");
312
		}
313
		
314
		logger.info("No " + pluralString + " left to fetch.");
315
		logger.info("Partition: " + partitionCount);
316
		logger.info("Taxa: " + countTaxa);
317
		logger.info("Desc: " + countDescriptions);
318
		logger.info("Distr: " + countDistribution);
319
		logger.info("Occur: " + countOccurrence);
320
		logger.info("Commons: " + countCommonName);
321
		logger.info("AddSrc: " + countAdditionalSources);
322
		logger.info("Images: " + countImages);
323
		logger.info("Notes: " + countNotes);
324
		logger.info("Others: " + countOthers);
325
		
326
		// Commit transaction
327
		commitTransaction(txStatus);
328
		logger.debug("Committed transaction.");
329
		return success;
330
	}
331

    
332
	private boolean handleSingleTaxon(Taxon taxon, PesiExportState state, PesiExportMapping notesMapping, PesiExportMapping occurrenceMapping,
333
			PesiExportMapping addSourceSourceMapping, PesiExportMapping additionalSourceMapping, 
334
			PesiExportMapping vernacularMapping, PesiExportMapping imageMapping) throws SQLException {
335
		boolean success = true;
336
		Set<DescriptionBase<?>> descriptions = new HashSet<DescriptionBase<?>>();
337
		descriptions.addAll(taxon.getDescriptions());
338
		
339
		for (DescriptionBase<?> desc : descriptions){
340
			countDescriptions++;
341

    
342
			boolean isImageGallery = desc.isImageGallery();
343
			for (DescriptionElementBase element : desc.getElements()){
344
				success &= handleDescriptionElement(state, notesMapping, occurrenceMapping, vernacularMapping, imageMapping,
345
						addSourceSourceMapping, additionalSourceMapping, isImageGallery, element);
346
			}
347
		}
348
		return success;
349
	}
350

    
351
	private boolean handleDescriptionElement(PesiExportState state, PesiExportMapping notesMapping,
352
			PesiExportMapping occurrenceMapping, PesiExportMapping vernacularMapping, PesiExportMapping imageMapping, 
353
			PesiExportMapping addSourceSourceMapping, PesiExportMapping additionalSourceMapping, boolean isImageGallery, DescriptionElementBase element) throws SQLException {
354
		try {
355
			boolean success = true;
356
			if (isImageGallery){
357
				//TODO handle Images
358
				countImages++;
359
				success &= imageMapping.invoke(element);
360
			}else if (isCommonName(element)){
361
				countCommonName++;
362
				if (element.isInstanceOf(TextData.class)){
363
					//we do not import text data common names
364
				}else{
365
					success &= vernacularMapping.invoke(element);
366
				}
367
			}else if (isOccurrence(element)){
368
				countOccurrence++;
369
				Distribution distribution = CdmBase.deproxy(element, Distribution.class);
370
				MarkerType markerType = getUuidMarkerType(PesiTransformer.uuidMarkerTypeHasNoLastAction, state);
371
				
372
				distribution.addMarker(Marker.NewInstance(markerType, true));
373
				if (isPesiDistribution(state, distribution)){
374
					countDistribution++;
375
					success &=occurrenceMapping.invoke(element);
376
				}
377
			}else if (isAdditionalTaxonSource(element)){
378
				countAdditionalSources++;
379
				if (! state.isSourceForAdditionalSourceCreated()){
380
					success &= addSourceSourceMapping.invoke(element);
381
				}
382
				success &= additionalSourceMapping.invoke(element);
383
			}else if (isExcludedNote(element)){
384
				//do nothing
385
			}else if (isPesiNote(element)){
386
				countNotes++;
387
				success &= notesMapping.invoke(element);
388
			
389
			}else{
390
				countOthers++;
391
				String featureTitle = element.getFeature() == null ? "no feature" :element.getFeature().getTitleCache();
392
				logger.warn("Description element type not yet handled by PESI export: " + element.getUuid() + ", " +  element.getClass() + ", " +  featureTitle);
393
			}
394
			return success;
395
		} catch (Exception e) {
396
			logger.warn("Exception appeared in description element handling: " + e);
397
			e.printStackTrace();
398
			return false;
399
		}
400
	}
401

    
402
	private boolean isExcludedNote(DescriptionElementBase element) {
403
		Integer categoryFk = PesiTransformer.feature2NoteCategoryFk(element.getFeature());
404
		//TODO decide where to handle them best (configurator, transformer, single method, ...)
405
		return (excludedNoteCategories.contains(categoryFk));
406
	}
407

    
408
	private boolean isPesiDistribution(PesiExportState state, Distribution distribution) {
409
		//currently we use the E+M summary status to decide if a distribution should be exported
410
		if (distribution.getStatus() == null){
411
			return false;
412
		}
413
		
414
		//...this may change in future so we keep the following code
415
		Integer key;
416
		//area filter
417
		NamedArea area = distribution.getArea();
418
		if (area == null){
419
			logger.warn("Area is null for distribution " +  distribution.getUuid());
420
			return false;
421
		}else if (area.getUuid().equals(BerlinModelTransformer.euroMedUuid)){
422
			//E+M area only holds endemic status information and therefore is not exported to PESI
423
			return false;
424
		}else if (area.equals(TdwgArea.getAreaByTdwgAbbreviation("1"))){
425
			//Europe area never holds status information (may probably be deleted in E+M)
426
			return false;
427
//		}else if (area.equals(TdwgArea.getAreaByTdwgAbbreviation("21"))){
428
//			//Macaronesia records should not be exported to PESI
429
//			return false;
430
//		//TODO exclude Russion areas Rs*, and maybe ohters
431
		
432
		} else
433
			try {
434
				if (state.getTransformer().getKeyByNamedArea(area) == null){
435
					String warning = "Area (%s,%s) not available in PESI transformer for taxon %S: ";
436
					TaxonBase<?> taxon =  state.getCurrentTaxon();
437
					warning = String.format(warning, area.getTitleCache(), area.getRepresentation(Language.ENGLISH()).getAbbreviatedLabel(),taxon ==null? "-" : taxon.getTitleCache());
438
					logger.warn(warning);
439
					return false;
440
				}
441
			} catch (UndefinedTransformerMethodException e1) {
442
				logger.warn("Area not available in PESI transformer " +  area.getTitleCache());
443
				return false;
444
			}
445
		return true;
446
		
447
//		
448
//		//status
449
//		PresenceAbsenceTermBase<?> status = distribution.getStatus();
450
//		if (status == null){
451
//			logger.warn("No status for distribution: " +  distribution.getUuid());
452
//			return false;
453
//		}
454
//		try {
455
//			key = (Integer)state.getTransformer().getKeyByPresenceAbsenceTerm(status);
456
//			if (key != null){
457
//				return true;
458
//			}else{
459
//				logger.warn("PresenceAbsenceTerm " + status.getTitleCache() + "not handled in transformer");
460
//				return false;
461
//			}
462
//		} catch (UndefinedTransformerMethodException e) {
463
//			logger.warn("PresenceAbsenceTerm " + status.getTitleCache() + "not handled in transformer");
464
//			return false;
465
//		}
466
	}
467

    
468
	private boolean isPesiNote(DescriptionElementBase element) {
469
		return (getNoteCategoryFk(element) != null);
470
	}
471

    
472
	private boolean isAdditionalTaxonSource(DescriptionElementBase element) {
473
		Feature feature = element.getFeature();
474
		if (feature == null){
475
			return false;
476
		}
477
		return (feature.equals(Feature.CITATION()) || feature.equals(Feature.ADDITIONAL_PUBLICATION()));
478
	}
479

    
480
	private boolean isOccurrence(DescriptionElementBase element) {
481
		Feature feature = element.getFeature();
482
		if (feature != null && feature.equals(Feature.DISTRIBUTION())){
483
			return true;
484
		}else if (element.isInstanceOf(Distribution.class)){
485
			logger.warn("Description element has class 'Distribution' but has no feature 'Distribution'");
486
			return true;
487
		}else{
488
			return false;
489
		}
490
	}
491

    
492
	private boolean isCommonName(DescriptionElementBase element) {
493
		Feature feature = element.getFeature();
494
		if (feature == null){
495
			return false;
496
		}
497
		return (feature.equals(Feature.COMMON_NAME()));
498
	}
499

    
500
	//PHASE 02: Name extensions
501
	private boolean doPhase02(PesiExportState state) {
502
		TransactionStatus txStatus;
503
		boolean success =  true;
504
		
505
		// Get the limit for objects to save within a single transaction.
506
		int limit = state.getConfig().getLimitSave();
507
					
508
		txStatus = startTransaction(true);
509
		ExtensionType taxCommentExtensionType = (ExtensionType)getTermService().find(PesiTransformer.taxCommentUuid);
510
		ExtensionType fauCommentExtensionType = (ExtensionType)getTermService().find(PesiTransformer.fauCommentUuid);
511
		ExtensionType fauExtraCodesExtensionType = (ExtensionType)getTermService().find(PesiTransformer.fauExtraCodesUuid);
512
		List<TaxonNameBase> taxonNameList = null;
513
		
514
		int count = 0;
515
		int pastCount = 0;
516
		Connection connection = state.getConfig().getDestination().getConnection();
517
		logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
518
		logger.warn("TODO handle extensions on taxon level, not name level (");
519
		while ((taxonNameList = getNameService().list(null, limit, count, null, null)).size() > 0) {
520

    
521
			logger.info("Fetched " + taxonNameList.size() + " names. Exporting...");
522
			for (TaxonNameBase<?,?> taxonName : taxonNameList) {
523
				Set<Extension> extensions = taxonName.getExtensions();
524
				for (Extension extension : extensions) {
525
					if (extension.getType().equals(taxCommentExtensionType)) {
526
						String taxComment = extension.getValue();
527
						invokeNotes(taxComment, 
528
								PesiTransformer.getNoteCategoryFk(PesiTransformer.taxCommentUuid), 
529
								PesiTransformer.getNoteCategoryCache(PesiTransformer.taxCommentUuid),
530
								null, null, getTaxonKey(taxonName, state),connection);
531
					} else if (extension.getType().equals(fauCommentExtensionType)) {
532
						String fauComment = extension.getValue();
533
						invokeNotes(fauComment, 
534
								PesiTransformer.getNoteCategoryFk(PesiTransformer.fauCommentUuid), 
535
								PesiTransformer.getNoteCategoryCache(PesiTransformer.fauCommentUuid),
536
								null, null, getTaxonKey(taxonName, state),connection);
537
					} else if (extension.getType().equals(fauExtraCodesExtensionType)) {
538
						String fauExtraCodes = extension.getValue();
539
						invokeNotes(fauExtraCodes, 
540
								PesiTransformer.getNoteCategoryFk(PesiTransformer.fauExtraCodesUuid), 
541
								PesiTransformer.getNoteCategoryCache(PesiTransformer.fauExtraCodesUuid),
542
								null, null, getTaxonKey(taxonName, state),connection);
543
					}
544
				}
545
				
546
				doCount(count++, modCount, pluralString);
547
			}
548

    
549
			// Commit transaction
550
			commitTransaction(txStatus);
551
			logger.debug("Committed transaction.");
552
			logger.info("Exported " + (count - pastCount) + " names. Total: " + count);
553
			pastCount = count;
554

    
555
			// Start transaction
556
			txStatus = startTransaction(true);
557
			logger.info("Started new transaction. Fetching some names first (max: " + limit + ") ...");
558
		}
559
		if (taxonNameList.size() == 0) {
560
			logger.info("No names left to fetch.");
561
		}
562
		taxonNameList = null;
563
		// Commit transaction
564
		commitTransaction(txStatus);
565
		logger.debug("Committed transaction.");
566
		return success;
567
	}
568

    
569
	/**
570
	 * @param taxComment
571
	 * @param noteCategoryFk
572
	 * @param noteCategoryCache
573
	 * @param object
574
	 * @param object2
575
	 */
576
	private void invokeNotes(String note, Integer noteCategoryFk,
577
			String noteCategoryCache, Integer languageFk, String languageCache, 
578
			Integer taxonFk, Connection connection) {
579
		String notesSql = "UPDATE Note SET Note_1 = ?, NoteCategoryFk = ?, NoteCategoryCache = ?, LanguageFk = ?, LanguageCache = ? WHERE TaxonFk = ?"; 
580
		try {
581
			PreparedStatement notesStmt = connection.prepareStatement(notesSql);
582
			
583
			if (note != null) {
584
				notesStmt.setString(1, note);
585
			} else {
586
				notesStmt.setObject(1, null);
587
			}
588
			
589
			if (noteCategoryFk != null) {
590
				notesStmt.setInt(2, noteCategoryFk);
591
			} else {
592
				notesStmt.setObject(2, null);
593
			}
594
			
595
			if (noteCategoryCache != null) {
596
				notesStmt.setString(3, noteCategoryCache);
597
			} else {
598
				notesStmt.setObject(3, null);
599
			}
600
			
601
			if (languageFk != null) {
602
				notesStmt.setInt(4, languageFk);
603
			} else {
604
				notesStmt.setObject(4, null);
605
			}
606
			
607
			if (languageCache != null) {
608
				notesStmt.setString(5, languageCache);
609
			} else {
610
				notesStmt.setObject(5, null);
611
			}
612
			
613
			if (taxonFk != null) {
614
				notesStmt.setInt(6, taxonFk);
615
			} else {
616
				notesStmt.setObject(6, null);
617
			}
618
			
619
			notesStmt.executeUpdate();
620
		} catch (SQLException e) {
621
			logger.error("Note could not be created: " + note);
622
			e.printStackTrace();
623
		}
624

    
625

    
626
	}
627

    
628
	/**
629
	 * Deletes all entries of database tables related to <code>Note</code>.
630
	 * @param state The PesiExportState
631
	 * @return Whether the delete operation was successful or not.
632
	 */
633
	protected boolean doDelete(PesiExportState state) {
634
		PesiExportConfigurator pesiConfig = (PesiExportConfigurator) state.getConfig();
635
		
636
		String sql;
637
		Source destination =  pesiConfig.getDestination();
638

    
639
		// Clear NoteSource
640
		sql = "DELETE FROM NoteSource";
641
		destination.setQuery(sql);
642
		destination.update(sql);
643

    
644
		// Clear Note
645
		sql = "DELETE FROM " + dbNoteTableName;
646
		destination.setQuery(sql);
647
		destination.update(sql);
648
		return true;
649
	}
650

    
651
	/* (non-Javadoc)
652
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
653
	 */
654
	@Override
655
	protected boolean isIgnore(PesiExportState state) {
656
		return ! state.getConfig().isDoDescription();
657
	}
658

    
659

    
660
	/**
661
	 * Returns the <code>Note_2</code> attribute.
662
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
663
	 * @return The <code>Note_2</code> attribute.
664
	 * @see MethodMapper
665
	 */
666
	@SuppressWarnings("unused") //used for mapper
667
	private static String getNote_2(DescriptionElementBase element) {
668
		//E+M map links -> medium
669
		if (element.getFeature() != null && element.getFeature().getUuid().equals(BerlinModelTransformer.uuidFeatureMaps)){
670
			String text = CdmBase.deproxy(element, TextData.class).getText(Language.ENGLISH());
671
			if (text.contains("medium")){
672
				return "medium";
673
			}
674
		}
675
		return null;
676
	}
677

    
678
	/**
679
	 * Returns the <code>NoteCategoryFk</code> attribute.
680
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
681
	 * @return The <code>NoteCategoryFk</code> attribute.
682
	 * @see MethodMapper
683
	 */
684
	private static Integer getNoteCategoryFk(DescriptionElementBase descriptionElement) {
685
		Integer result = null;
686
		result = PesiTransformer.feature2NoteCategoryFk(descriptionElement.getFeature());
687
		//TODO decide where to handle them best (configurator, transformer, single method, ...)
688
		if (excludedNoteCategories.contains(result)){
689
			result = null;
690
		}
691
		return result;
692
	}
693
	
694
	/**
695
	 * Returns the <code>NoteCategoryCache</code> attribute.
696
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
697
	 * @return The <code>NoteCategoryCache</code> attribute.
698
	 * @see MethodMapper
699
	 */
700
	@SuppressWarnings("unused")
701
	private static String getNoteCategoryCache(DescriptionElementBase descriptionElement, PesiExportState state) {
702
		return state.getTransformer().getCacheByFeature(descriptionElement.getFeature());
703
	}
704
	
705
	
706

    
707

    
708
	/**
709
	 * Returns the <code>LanguageFk</code> attribute.
710
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
711
	 * @return The <code>LanguageFk</code> attribute.
712
	 * @see MethodMapper
713
	 */
714
	@SuppressWarnings("unused")
715
	private static Integer getLanguageFk(DescriptionElementBase descriptionElement) {
716
		Language language = getLanguage(descriptionElement);
717

    
718
		return PesiTransformer.language2LanguageId(language);
719
	}
720

    
721
	/**
722
	 * Returns the <code>LanguageCache</code> attribute.
723
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
724
	 * @return The <code>LanguageCache</code> attribute.
725
	 * @throws UndefinedTransformerMethodException 
726
	 * @see MethodMapper
727
	 */
728
	@SuppressWarnings("unused")
729
	private static String getLanguageCache(DescriptionElementBase descriptionElement, PesiExportState state) throws UndefinedTransformerMethodException {
730
		Language language = getLanguage(descriptionElement);
731
		return state.getTransformer().getCacheByLanguage(language);
732
	}
733

    
734
	private static Language getLanguage(DescriptionElementBase descriptionElement) {
735
		Language language = null;
736

    
737
		Map<Language, LanguageString> multilanguageText = null;
738
		if (descriptionElement.isInstanceOf(CommonTaxonName.class)) {
739
			CommonTaxonName commonTaxonName = CdmBase.deproxy(descriptionElement, CommonTaxonName.class);
740
			language = commonTaxonName.getLanguage();
741
		} else if (descriptionElement.isInstanceOf(TextData.class)) {
742
			TextData textData = CdmBase.deproxy(descriptionElement, TextData.class);
743
			multilanguageText = textData.getMultilanguageText();
744
		} else if (descriptionElement.isInstanceOf(IndividualsAssociation.class)) {
745
			IndividualsAssociation individualsAssociation = CdmBase.deproxy(descriptionElement, IndividualsAssociation.class);
746
			multilanguageText = individualsAssociation.getDescription();
747
		} else if (descriptionElement.isInstanceOf(TaxonInteraction.class)) {
748
			TaxonInteraction taxonInteraction = CdmBase.deproxy(descriptionElement, TaxonInteraction.class);
749
			multilanguageText = taxonInteraction.getDescriptions();
750
		} else {
751
			logger.debug("Given descriptionElement does not support languages. Hence LanguageCache could not be determined: " + descriptionElement.getUuid());
752
		}
753
		
754
		if (multilanguageText != null) {
755
			Set<Language> languages = multilanguageText.keySet();
756

    
757
			// TODO: Think of something more sophisticated than this
758
			if (languages.size() > 0) {
759
				language = languages.iterator().next();
760
			}
761
			if (languages.size() > 1){
762
				logger.warn("There is more than 1 language for a given description (" + descriptionElement.getClass().getSimpleName() + "):" + descriptionElement.getUuid());
763
			}
764
		}
765
		return language;
766
	}
767

    
768
//	/**
769
//	 * Returns the <code>Region</code> attribute.
770
//	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
771
//	 * @return The <code>Region</code> attribute.
772
//	 * @see MethodMapper
773
//	 */
774
//	@SuppressWarnings("unused")
775
//	private static String getRegion(DescriptionElementBase descriptionElement) {
776
//		String result = null;
777
//		DescriptionBase<?> inDescription = descriptionElement.getInDescription();
778
//		
779
//		// Area information are associated to TaxonDescriptions and Distributions.
780
//		if (descriptionElement.isInstanceOf(Distribution.class)) {
781
//			Distribution distribution = CdmBase.deproxy(descriptionElement, Distribution.class);
782
//			result = PesiTransformer.area2AreaCache(distribution.getArea());
783
//		} else if (inDescription != null && inDescription.isInstanceOf(TaxonDescription.class)) {
784
//			TaxonDescription taxonDescription = CdmBase.deproxy(inDescription, TaxonDescription.class);
785
//			Set<NamedArea> namedAreas = taxonDescription.getGeoScopes();
786
//			if (namedAreas.size() == 1) {
787
//				result = PesiTransformer.area2AreaCache(namedAreas.iterator().next());
788
//			} else if (namedAreas.size() > 1) {
789
//				logger.warn("This TaxonDescription contains more than one NamedArea: " + taxonDescription.getTitleCache());
790
//			}
791
//		}
792
//		return result;
793
//	}
794

    
795
	
796
	/**
797
	 * Returns the TaxonFk for a given TaxonName or Taxon.
798
	 * @param state The {@link DbExportStateBase DbExportState}.
799
	 * @return
800
	 */
801
	@SuppressWarnings("unused")  //used by mapper
802
	private static Integer getTaxonFk(DescriptionElementBase deb, PesiExportState state) {
803
		TaxonBase<?> entity = state.getCurrentTaxon();
804
		return state.getDbId(entity);
805
	}
806
	
807
	/**
808
	 * Returns the TaxonFk for a given TaxonName.
809
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
810
	 * @param state The {@link DbExportStateBase DbExportState}.
811
	 * @return
812
	 */
813
	private static Integer getTaxonKey(TaxonNameBase<?,?> taxonName, DbExportStateBase<?, PesiTransformer> state) {
814
		return state.getDbId(taxonName);
815
	}
816
	
817
	/**
818
	 * Returns the <code>FullName</code> attribute.
819
	 * @param taxonName The {@link NonViralName NonViralName}.
820
	 * @return The <code>FullName</code> attribute.
821
	 * @see MethodMapper
822
	 */
823
	@SuppressWarnings("unused")
824
	private static String getTaxonFullNameCache(DescriptionElementBase deb, PesiExportState state) {
825
		
826
		TaxonBase<?> taxon =  state.getCurrentTaxon();
827
		TaxonNameBase<?,?> taxonName = taxon.getName();
828
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
829
		String result = getCacheStrategy(nvn).getTitleCache(nvn);
830
		return result;
831
	}
832

    
833

    
834
	/**
835
	 * Returns the CDM to PESI specific export mappings for PESI notes.
836
	 * @return The {@link PesiExportMapping PesiExportMapping}.
837
	 */
838
	private PesiExportMapping getNotesMapping() {
839
		PesiExportMapping mapping = new PesiExportMapping(dbNoteTableName);
840
		
841
		mapping.addMapper(IdMapper.NewInstance("NoteId"));
842
		mapping.addMapper(DbTextDataMapper.NewInstance(Language.ENGLISH(), "Note_1"));
843
		//TODO
844
		mapping.addMapper(MethodMapper.NewInstance("Note_2", this, DescriptionElementBase.class));
845
		mapping.addMapper(MethodMapper.NewInstance("NoteCategoryFk", this, DescriptionElementBase.class ));
846
		
847
		mapping.addMapper(MethodMapper.NewInstance("NoteCategoryCache", this, DescriptionElementBase.class, PesiExportState.class ));
848
		mapping.addMapper(MethodMapper.NewInstance("LanguageFk", this));
849
		mapping.addMapper(MethodMapper.NewInstance("LanguageCache", this, DescriptionElementBase.class, PesiExportState.class));
850
		
851
//		mapping.addMapper(MethodMapper.NewInstance("Region", this));
852
		mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("taxonFk"));
853
		mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
854
		mapping.addCollectionMapping(getNoteSourceMapping());
855
		return mapping;
856
	}
857
	
858
	private CollectionExportMapping<PesiExportState, PesiExportConfigurator,PesiTransformer> getNoteSourceMapping() {
859
		String tableName = "NoteSource";
860
		String collectionAttribute = "sources";
861
		IdMapper parentMapper = IdMapper.NewInstance("NoteFk");
862
		CollectionExportMapping<PesiExportState, PesiExportConfigurator, PesiTransformer> mapping = CollectionExportMapping.NewInstance(tableName, collectionAttribute, parentMapper);
863
		mapping.addMapper(DbSimpleFilterMapper.NewSingleNullAttributeInstance("idInSource", "Sources with idInSource currently handle data lineage"));
864
		mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceFk"));
865
		mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceNameCache", IS_CACHE));
866
		mapping.addMapper(DbStringMapper.NewInstance("CitationMicroReference", "SourceDetail"));
867
		return mapping;
868
	}
869
	
870

    
871
	/**
872
	 * Returns the CDM to PESI specific export mappings for occurrences.
873
	 * @return The {@link PesiExportMapping PesiExportMapping}.
874
	 */
875
	private PesiExportMapping getOccurrenceMapping() {
876
		PesiExportMapping mapping = new PesiExportMapping(dbOccurrenceTableName);
877
		
878
		mapping.addMapper(IdMapper.NewInstance("OccurrenceId"));
879
		mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("taxonFk"));
880
		mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("TaxonFullNameCache", true, true, null)); 
881
		
882
		mapping.addMapper(DbAreaMapper.NewInstance(Distribution.class, "Area", "AreaFk", ! IS_CACHE));
883
		mapping.addMapper(DbAreaMapper.NewInstance(Distribution.class, "Area", "AreaNameCache", IS_CACHE));
884
		mapping.addMapper(DbDistributionStatusMapper.NewInstance("OccurrenceStatusFk", ! IS_CACHE));
885
		mapping.addMapper(DbDistributionStatusMapper.NewInstance("OccurrenceStatusCache", IS_CACHE));
886
		
887
//		Use Occurrence source instead
888
		mapping.addMapper(DbExportIgnoreMapper.NewInstance("SourceFk", "Use OccurrenceSource table for sources instead"));
889
		mapping.addMapper(DbExportIgnoreMapper.NewInstance("SourceNameCache", "Use OccurrenceSource table for sources instead"));
890
		
891
		
892
		mapping.addMapper(DbExportNotYetImplementedMapper.NewInstance("Notes", "Needs reimplementation in description export"));
893
		mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
894
		mapping.addCollectionMapping(getOccurrenceSourceMapping());
895
		
896
		return mapping;
897
	}
898

    
899
	private CollectionExportMapping<PesiExportState, PesiExportConfigurator, PesiTransformer> getOccurrenceSourceMapping() {
900
		String tableName = "OccurrenceSource";
901
		String collectionAttribute = "sources";
902
		IdMapper parentMapper = IdMapper.NewInstance("OccurrenceFk");
903
		CollectionExportMapping<PesiExportState, PesiExportConfigurator, PesiTransformer> mapping = CollectionExportMapping.NewInstance(tableName, collectionAttribute, parentMapper);
904
		mapping.addMapper(DbSimpleFilterMapper.NewSingleNullAttributeInstance("idInSource", "Sources with idInSource currently handle data lineage"));
905
		mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceFk"));
906
		mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceNameCache", IS_CACHE));
907
		mapping.addMapper(DbOriginalNameMapper.NewInstance("OldTaxonName", IS_CACHE, null));
908

    
909
		return mapping;
910
	}
911
	
912

    
913
	/**
914
	 * Returns the CDM to PESI specific export mappings for additional taxon sources to create a new
915
	 * source for the additional source
916
	 * @see #{@link PesiDescriptionExport#getAdditionalTaxonSourceMapping()}
917
	 * @return The {@link PesiExportMapping PesiExportMapping}.
918
	 */
919
	private PesiExportMapping getAddTaxonSourceSourceMapping() {
920
		PesiExportMapping sourceMapping = new PesiExportMapping(PesiSourceExport.dbTableName);
921
		
922
		sourceMapping.addMapper(IdMapper.NewInstance("SourceId"));
923
		sourceMapping.addMapper(DbConstantMapper.NewInstance("SourceCategoryFk", Types.INTEGER, PesiTransformer.REF_UNRESOLVED));
924
		sourceMapping.addMapper(DbConstantMapper.NewInstance("SourceCategoryCache", Types.VARCHAR, PesiTransformer.REF_STR_UNRESOLVED));
925
		
926
//		sourceMapping.addMapper(MethodMapper.NewInstance("NomRefCache", PesiSourceExport.class, "getNomRefCache", Reference.class));
927
		
928
		sourceMapping.addMapper(DbTextDataMapper.NewInstance(Language.ENGLISH(), "NomRefCache"));
929
		
930
		return sourceMapping;
931
	}
932

    
933
	
934
	/**
935
	 * Returns the CDM to PESI specific export mappings for additional taxon sources.
936
	 * @see #{@link PesiDescriptionExport#getAddTaxonSourceSourceMapping()}
937
	 * @return The {@link PesiExportMapping PesiExportMapping}.
938
	 */
939
	private PesiExportMapping getAdditionalTaxonSourceMapping() {
940
	
941
		PesiExportMapping mapping = new PesiExportMapping(dbAdditionalSourceTableName);
942
		
943
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk", this, DescriptionElementBase.class, PesiExportState.class));
944
		
945
		mapping.addMapper(IdMapper.NewInstance("SourceFk"));
946
		mapping.addMapper(DbTextDataMapper.NewInstance(Language.ENGLISH(), "SourceNameCache"));
947
		
948
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER, PesiTransformer.NOMENCLATURAL_REFERENCE));
949
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, PesiTransformer.STR_NOMENCLATURAL_REFERENCE));
950
		
951
		mapping.addMapper(DbExportIgnoreMapper.NewInstance("SourceDetail", "SourceDetails not available for additional sources"));
952
		
953
		return mapping;
954
	}
955
	
956
	/**
957
	 * Returns the CDM to PESI specific export mappings for common names.
958
	 * @return The {@link PesiExportMapping PesiExportMapping}.
959
	 */
960
	private PesiExportMapping getVernacularNamesMapping() {
961
		PesiExportMapping mapping = new PesiExportMapping(dbVernacularTableName);
962
		
963
		mapping.addMapper(IdMapper.NewInstance("CommonNameId"));
964
		mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("taxonFk"));
965
		
966
		mapping.addMapper(DbStringMapper.NewInstance("Name", "CommonName"));
967
		mapping.addMapper(DbAreaMapper.NewInstance(CommonTaxonName.class, "Area", "Region", IS_CACHE));
968
		
969
		mapping.addMapper(DbLanguageMapper.NewInstance(CommonTaxonName.class, "Language", "LanguageFk", ! IS_CACHE));
970
		mapping.addMapper(DbLanguageMapper.NewInstance(CommonTaxonName.class, "Language", "LanguageCache", IS_CACHE));
971
		
972
		mapping.addMapper(DbSingleSourceMapper.NewInstance("SourceFk", of ( DbSingleSourceMapper.EXCLUDE.WITH_ID) , ! IS_CACHE));
973
		mapping.addMapper(DbSingleSourceMapper.NewInstance("SourceNameCache", of ( DbSingleSourceMapper.EXCLUDE.WITH_ID) , IS_CACHE));
974
		mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
975
		return mapping;
976

    
977
	}
978
	
979
	private PesiExportMapping getImageMapping() {
980
		PesiExportMapping mapping = new PesiExportMapping(dbImageTableName);
981
		mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("taxonFk"));
982
		
983
		//TODO xxx
984
		
985
		return mapping;
986
	}
987

    
988
}
(3-3/12)