Project

General

Profile

Download (40.1 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 com.microsoft.sqlserver.jdbc.SQLServerException;
29

    
30
import eu.etaxonomy.cdm.profiler.ProfilerController;
31
import eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer;
32
import eu.etaxonomy.cdm.io.common.DbExportStateBase;
33
import eu.etaxonomy.cdm.io.common.Source;
34
import eu.etaxonomy.cdm.io.common.TdwgAreaProvider;
35
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
36
import eu.etaxonomy.cdm.io.common.mapping.out.CollectionExportMapping;
37
import eu.etaxonomy.cdm.io.common.mapping.out.DbAnnotationMapper;
38
import eu.etaxonomy.cdm.io.common.mapping.out.DbAreaMapper;
39
import eu.etaxonomy.cdm.io.common.mapping.out.DbConstantMapper;
40
import eu.etaxonomy.cdm.io.common.mapping.out.DbDescriptionElementTaxonMapper;
41
import eu.etaxonomy.cdm.io.common.mapping.out.DbDistributionStatusMapper;
42
import eu.etaxonomy.cdm.io.common.mapping.out.DbExportIgnoreMapper;
43
import eu.etaxonomy.cdm.io.common.mapping.out.DbLanguageMapper;
44
import eu.etaxonomy.cdm.io.common.mapping.out.DbObjectMapper;
45
import eu.etaxonomy.cdm.io.common.mapping.out.DbOriginalNameMapper;
46
import eu.etaxonomy.cdm.io.common.mapping.out.DbSimpleFilterMapper;
47
import eu.etaxonomy.cdm.io.common.mapping.out.DbSingleSourceMapper;
48
import eu.etaxonomy.cdm.io.common.mapping.out.DbStringMapper;
49
import eu.etaxonomy.cdm.io.common.mapping.out.DbTextDataMapper;
50
import eu.etaxonomy.cdm.io.common.mapping.out.IdMapper;
51
import eu.etaxonomy.cdm.io.common.mapping.out.MethodMapper;
52
import eu.etaxonomy.cdm.model.common.CdmBase;
53
import eu.etaxonomy.cdm.model.common.Extension;
54
import eu.etaxonomy.cdm.model.common.ExtensionType;
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.name.NonViralName;
70
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
71
import eu.etaxonomy.cdm.model.taxon.Taxon;
72
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
73
/**
74
 * The export class for {@link eu.etaxonomy.cdm.model.description.DescriptionElementBase DescriptionElements}.<p>
75
 * Inserts into DataWarehouse database table <code>Note</code>.<p>
76
 * It is divided into two phases:<ul>
77
 * <li>Phase 1:	Export of DescriptionElements as Notes.
78
 * <li>Phase 2:	Export of TaxonName extensions <code>taxComment</code>, <code>fauComment</code> and <code>fauExtraCodes</code> as Notes.</ul>
79
 * @author e.-m.lee
80
 * @date 23.02.2010
81
 *
82
 */
83
@Component
84
public class PesiDescriptionExport extends PesiExportBase {
85
	private static final Logger logger = Logger.getLogger(PesiDescriptionExport.class);
86

    
87
	private static final Class<? extends CdmBase> standardMethodParameter = DescriptionElementBase.class;
88

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

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

    
101

    
102
	//debugging
103
	private static int countDescriptions;
104
	private static int countTaxa;
105
	private static int countDistribution;
106
	private static int countAdditionalSources;
107
	private static int countImages;
108
	private static int countNotes;
109

    
110
	private static int countCommonName;
111
	private static int countOccurrence;
112
	private static int countOthers;
113

    
114
	public PesiDescriptionExport() {
115
		super();
116
	}
117

    
118
	@Override
119
	public Class<? extends CdmBase> getStandardMethodParameter() {
120
		return standardMethodParameter;
121
	}
122

    
123
	@Override
124
	protected boolean doCheck(PesiExportState state) {
125
		boolean result = true;
126
		return result;
127
	}
128

    
129
	@Override
130
	protected void doInvoke(PesiExportState state) {
131
		try {
132
			logger.info("*** Started Making " + pluralString + " ...");
133

    
134
			// Stores whether this invoke was successful or not.
135
			boolean success = true;
136

    
137
			// Get specific mappings: (CDM) DescriptionElement -> (PESI) Note
138
			PesiExportMapping notesMapping = getNotesMapping();
139
			notesMapping.initialize(state);
140

    
141
			// Get specific mappings: (CDM) DescriptionElement -> (PESI) Occurrence
142
			PesiExportMapping occurrenceMapping = getOccurrenceMapping();
143
			occurrenceMapping.initialize(state);
144

    
145
			// Get specific mappings: (CDM) DescriptionElement -> (PESI) Additional taxon source
146
			PesiExportMapping addSourceSourceMapping = getAddTaxonSourceSourceMapping();
147
			addSourceSourceMapping.initialize(state);
148
			PesiExportMapping additionalSourceMapping = getAdditionalTaxonSourceMapping();
149
			additionalSourceMapping.initialize(state);
150

    
151
			// Get specific mappings: (CDM) DescriptionElement -> (PESI) Additional taxon source
152

    
153
			PesiExportMapping vernacularMapping = getVernacularNamesMapping();
154
			vernacularMapping.initialize(state);
155

    
156
			// Get specific mappings: (CDM) DescriptionElement -> (PESI) Additional taxon source
157
			PesiExportMapping imageMapping = getImageMapping();
158
			imageMapping.initialize(state);
159

    
160
			// Start transaction
161
			success &= doPhase01(state, notesMapping, occurrenceMapping, addSourceSourceMapping, additionalSourceMapping, vernacularMapping, imageMapping);
162

    
163
			// Start transaction
164
			success &= doPhase01b(state, notesMapping, occurrenceMapping, addSourceSourceMapping, additionalSourceMapping, vernacularMapping, imageMapping);
165

    
166

    
167
			logger.info("PHASE 2...");
168
			success &= doPhase02(state);
169

    
170

    
171
			logger.info("*** Finished Making " + pluralString + " ..." + getSuccessString(success));
172

    
173
			if (!success){
174
				state.setUnsuccessfull();
175
			}
176
			return;
177
		} catch (SQLException e) {
178
			e.printStackTrace();
179
			logger.error(e.getMessage());
180
			state.setUnsuccessfull();
181
		}
182
	}
183

    
184
	//PHASE 01: Description Elements
185
	private boolean doPhase01(PesiExportState state, PesiExportMapping notesMapping, PesiExportMapping occurrenceMapping, PesiExportMapping addSourceSourceMapping,
186
			PesiExportMapping additionalSourceMapping, PesiExportMapping vernacularMapping, PesiExportMapping imageMapping) throws SQLException {
187

    
188
	    System.out.println("PHASE 1 of occurence import");
189
	    logger.info("PHASE 1...");
190
		int count = 0;
191
		int pastCount = 0;
192
		boolean success = true;
193
		//int limit = state.getConfig().getLimitSave();
194
		int limit = 100;
195

    
196
		List<Taxon> taxonList = null;
197

    
198
		TransactionStatus txStatus = startTransaction(true);
199
		logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") ...");
200
		List<String> propPath = Arrays.asList(new String[]{"descriptions.elements.*"});
201

    
202
		logger.debug("Start snapshot, before starting loop");
203
		ProfilerController.memorySnapshot();
204
		//taxon descriptions
205
		int partitionCount = 0;
206
		while ((taxonList = getNextTaxonPartition(Taxon.class, limit, partitionCount++, propPath )) != null   ) {
207

    
208
			logger.info("Fetched " + taxonList.size() + " " + pluralString + ". Exporting...");
209
			System.out.println("Fetched " + taxonList.size() + " " + pluralString + ". Exporting...");
210

    
211
			for (Taxon taxon : taxonList) {
212
				countTaxa++;
213
				doCount(count++, modCount, pluralString);
214
				state.setCurrentTaxon(taxon);
215
				if (!taxon.getDescriptions().isEmpty()){
216
					success &= handleSingleTaxon(taxon, state, notesMapping, occurrenceMapping, addSourceSourceMapping,
217
						additionalSourceMapping, vernacularMapping, imageMapping);
218
				}
219
				
220
			}
221
			taxonList = null;
222
			state.setCurrentTaxon(null);
223
			
224
			// Commit transaction
225
			commitTransaction(txStatus);
226
			logger.info("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
227
			System.out.println("Exported " + (count - pastCount) + " " + pluralString + ". Total: " + count);
228
			pastCount = count;
229
			ProfilerController.memorySnapshot();
230
			// Start transaction
231
			txStatus = startTransaction(true);
232
			logger.info("Started new transaction. Fetching some " + pluralString + " (max: " + limit + ") for description import ...");
233
		}
234

    
235

    
236
		logger.info("No " + pluralString + " left to fetch.");
237
		logger.info("Partition: " + partitionCount);
238
		logger.info("Taxa: " + countTaxa);
239
		logger.info("Desc: " + countDescriptions);
240
		logger.info("Distr: " + countDistribution);
241
		logger.info("Occur: " + countOccurrence);
242
		logger.info("Commons: " + countCommonName);
243
		logger.info("AddSrc: " + countAdditionalSources);
244
		logger.info("Images: " + countImages);
245
		logger.info("Notes: " + countNotes);
246
		logger.info("Others: " + countOthers);
247

    
248
		// Commit transaction
249
		commitTransaction(txStatus);
250
		logger.debug("Committed transaction.");
251
		return success;
252
	}
253

    
254
	//PHASE 01b: Name Descriptions
255
	private boolean doPhase01b(PesiExportState state, PesiExportMapping notesMapping, PesiExportMapping occurrenceMapping, PesiExportMapping addSourceSourceMapping,
256
			PesiExportMapping additionalSourceMapping, PesiExportMapping vernacularMapping, PesiExportMapping imageMapping) throws SQLException {
257
		logger.info("PHASE 1b...");
258
		int count = 0;
259
		int pastCount = 0;
260
		boolean success = true;
261
		//int limit = state.getConfig().getLimitSave();
262
		int limit = 100;
263
		List<TaxonNameDescription> nameDescList = null;
264

    
265
		TransactionStatus txStatus = startTransaction(true);
266
		logger.info("Started new transaction. Fetching some name descriptions (max: " + limit + ") ...");
267
		List<String> propPath = Arrays.asList(new String[]{"descriptions.elements.*"});
268

    
269
		//name descriptions
270
		int partitionCount = 0;
271
		while ((nameDescList = getNextNameDescriptionPartition( limit, partitionCount++, propPath )) != null   ) {
272

    
273
			logger.info("Fetched " + nameDescList.size() + " name descriptions. Exporting...");
274

    
275
			for (TaxonNameDescription desc : nameDescList) {
276
				countTaxa++;
277
				doCount(count++, modCount, "name descriptions");
278
				boolean isImageGallery = desc.isImageGallery();
279

    
280
				TaxonNameBase<?,?> name = desc.getTaxonName();
281

    
282
				for (DescriptionElementBase element : desc.getElements()){
283
					if (isPurePesiName(name)){
284
						success &= handleDescriptionElement(state, notesMapping, occurrenceMapping, vernacularMapping, imageMapping,
285
								addSourceSourceMapping, additionalSourceMapping, isImageGallery, element);
286
					}else{
287
						for (TaxonBase<?> taxonBase : name.getTaxonBases()){
288
							if (isPesiTaxon(taxonBase)){
289
								state.setCurrentTaxon(taxonBase);
290
								success &= handleDescriptionElement(state, notesMapping, occurrenceMapping, vernacularMapping, imageMapping,
291
										addSourceSourceMapping, additionalSourceMapping, isImageGallery, element);
292
								state.setSourceForAdditionalSourceCreated(true);
293
							}
294
						}
295
						state.setSourceForAdditionalSourceCreated(false);
296
					}
297
				}
298
			}
299
			nameDescList = null;
300
			state.setCurrentTaxon(null);
301

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

    
307
			// Start transaction
308
			txStatus = startTransaction(true);
309
			logger.info("Started new transaction. Fetching some name descriptions (max: " + limit + ") for description import ...");
310
		}
311

    
312
		logger.info("No " + pluralString + " left to fetch.");
313
		logger.info("Partition: " + partitionCount);
314
		logger.info("Taxa: " + countTaxa);
315
		logger.info("Desc: " + countDescriptions);
316
		logger.info("Distr: " + countDistribution);
317
		logger.info("Occur: " + countOccurrence);
318
		logger.info("Commons: " + countCommonName);
319
		logger.info("AddSrc: " + countAdditionalSources);
320
		logger.info("Images: " + countImages);
321
		logger.info("Notes: " + countNotes);
322
		logger.info("Others: " + countOthers);
323

    
324
		// Commit transaction
325
		commitTransaction(txStatus);
326
		logger.debug("Committed transaction.");
327
		return success;
328
	}
329

    
330
	private boolean handleSingleTaxon(Taxon taxon, PesiExportState state, PesiExportMapping notesMapping, PesiExportMapping occurrenceMapping,
331
			PesiExportMapping addSourceSourceMapping, PesiExportMapping additionalSourceMapping,
332
			PesiExportMapping vernacularMapping, PesiExportMapping imageMapping) throws SQLException {
333
		boolean success = true;
334

    
335
		
336
		Set<DescriptionBase<?>> descriptions = new HashSet<DescriptionBase<?>>();
337
		descriptions.addAll(taxon.getDescriptions());
338

    
339
		for (DescriptionBase<?> desc : descriptions){
340
			
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
				countDescriptions++;
347
			}
348
		}
349
		logger.info("number of handled decriptionelements " + countDescriptions);
350
		descriptions = null;
351
		return success;
352
	}
353

    
354
	private boolean handleDescriptionElement(PesiExportState state, PesiExportMapping notesMapping,
355
			PesiExportMapping occurrenceMapping, PesiExportMapping vernacularMapping, PesiExportMapping imageMapping,
356
			PesiExportMapping addSourceSourceMapping, PesiExportMapping additionalSourceMapping, boolean isImageGallery, DescriptionElementBase element) throws SQLException {
357
		try {
358
			boolean success = true;
359
			if (isImageGallery){
360
				//TODO handle Images
361
				countImages++;
362
				success &= imageMapping.invoke(element);
363
			}else if (isCommonName(element)){
364
				countCommonName++;
365
				if (element.isInstanceOf(TextData.class)){
366
					//we do not import text data common names
367
				}else{
368
					success &= vernacularMapping.invoke(element);
369
				}
370
			}else if (isOccurrence(element)){
371
				countOccurrence++;
372
				Distribution distribution = CdmBase.deproxy(element, Distribution.class);
373
				MarkerType markerType = getUuidMarkerType(PesiTransformer.uuidMarkerTypeHasNoLastAction, state);
374

    
375
				distribution.addMarker(Marker.NewInstance(markerType, true));
376
				if (isPesiDistribution(state, distribution)){
377
					countDistribution++;
378
					try{
379
					    success &=occurrenceMapping.invoke(distribution);
380
					}catch(SQLServerException e){
381
					    System.err.println(distribution.getInDescription().getTitleCache());
382
					    e.printStackTrace();
383
					}
384
				}
385
			}else if (isAdditionalTaxonSource(element)){
386
				countAdditionalSources++;
387
				if (! state.isSourceForAdditionalSourceCreated()){
388
					success &= addSourceSourceMapping.invoke(element);
389
				}
390
				success &= additionalSourceMapping.invoke(element);
391
			}else if (isExcludedNote(element)){
392
				//do nothing
393
			}else if (isPesiNote(element)){
394
				countNotes++;
395
				success &= notesMapping.invoke(element);
396

    
397
			}else{
398
				countOthers++;
399
				String featureTitle = element.getFeature() == null ? "no feature" :element.getFeature().getTitleCache();
400
				logger.warn("Description element type not yet handled by PESI export: " + element.getUuid() + ", " +  element.getClass() + ", " +  featureTitle);
401
			}
402
			return success;
403
		} catch (Exception e) {
404
			logger.warn("Exception appeared in description element handling: " + e);
405
			e.printStackTrace();
406
			return false;
407
		}
408
	}
409

    
410
	private boolean isExcludedNote(DescriptionElementBase element) {
411
		Integer categoryFk = PesiTransformer.feature2NoteCategoryFk(element.getFeature());
412
		//TODO decide where to handle them best (configurator, transformer, single method, ...)
413
		return (excludedNoteCategories.contains(categoryFk));
414
	}
415

    
416
	private boolean isPesiDistribution(PesiExportState state, Distribution distribution) {
417
		//currently we use the E+M summary status to decide if a distribution should be exported
418
		if (distribution.getStatus() == null){
419
			return false;
420
		}
421

    
422
		//...this may change in future so we keep the following code
423
		Integer key;
424
		//area filter
425
		NamedArea area = distribution.getArea();
426
		if (area == null){
427
			logger.warn("Area is null for distribution " +  distribution.getUuid());
428
			return false;
429
		}else if (area.getUuid().equals(BerlinModelTransformer.euroMedUuid)){
430
			//E+M area only holds endemic status information and therefore is not exported to PESI
431
			return false;
432
		}else if (area.equals(TdwgAreaProvider.getAreaByTdwgAbbreviation("1"))){
433
			//Europe area never holds status information (may probably be deleted in E+M)
434
			return false;
435
//		}else if (area.equals(TdwgArea.getAreaByTdwgAbbreviation("21"))){
436
//			//Macaronesia records should not be exported to PESI
437
//			return false;
438
//		//TODO exclude Russion areas Rs*, and maybe ohters
439

    
440
		} else {
441
            try {
442
				if (state.getTransformer().getKeyByNamedArea(area) == null){
443
					String warning = "Area (%s,%s) not available in PESI transformer for taxon %S: ";
444
					TaxonBase<?> taxon =  state.getCurrentTaxon();
445
					warning = String.format(warning, area.getTitleCache(), area.getRepresentation(Language.ENGLISH()).getAbbreviatedLabel(),taxon ==null? "-" : taxon.getTitleCache());
446
					logger.warn(warning);
447
					return false;
448
				}
449
			} catch (UndefinedTransformerMethodException e1) {
450
				logger.warn("Area not available in PESI transformer " +  area.getTitleCache());
451
				return false;
452
			}
453
        }
454
		return true;
455

    
456
//
457
//		//status
458
//		PresenceAbsenceTermBase<?> status = distribution.getStatus();
459
//		if (status == null){
460
//			logger.warn("No status for distribution: " +  distribution.getUuid());
461
//			return false;
462
//		}
463
//		try {
464
//			key = (Integer)state.getTransformer().getKeyByPresenceAbsenceTerm(status);
465
//			if (key != null){
466
//				return true;
467
//			}else{
468
//				logger.warn("PresenceAbsenceTerm " + status.getTitleCache() + "not handled in transformer");
469
//				return false;
470
//			}
471
//		} catch (UndefinedTransformerMethodException e) {
472
//			logger.warn("PresenceAbsenceTerm " + status.getTitleCache() + "not handled in transformer");
473
//			return false;
474
//		}
475
	}
476

    
477
	private boolean isPesiNote(DescriptionElementBase element) {
478
		return (getNoteCategoryFk(element) != null);
479
	}
480

    
481
	private boolean isAdditionalTaxonSource(DescriptionElementBase element) {
482
		Feature feature = element.getFeature();
483
		if (feature == null){
484
			return false;
485
		}
486
		return (feature.equals(Feature.CITATION()) || feature.equals(Feature.ADDITIONAL_PUBLICATION()));
487
	}
488

    
489
	private boolean isOccurrence(DescriptionElementBase element) {
490
		Feature feature = element.getFeature();
491
		if (feature != null && feature.equals(Feature.DISTRIBUTION())){
492
			return true;
493
		}else if (element.isInstanceOf(Distribution.class)){
494
			logger.warn("Description element has class 'Distribution' but has no feature 'Distribution'");
495
			return true;
496
		}else{
497
			return false;
498
		}
499
	}
500

    
501
	private boolean isCommonName(DescriptionElementBase element) {
502
		Feature feature = element.getFeature();
503
		if (feature == null){
504
			return false;
505
		}
506
		return (feature.equals(Feature.COMMON_NAME()));
507
	}
508

    
509
	//PHASE 02: Name extensions
510
	private boolean doPhase02(PesiExportState state) {
511
		TransactionStatus txStatus;
512
		boolean success =  true;
513

    
514
		// Get the limit for objects to save within a single transaction.
515
		//int limit = state.getConfig().getLimitSave();
516
		int limit = 100;
517
		txStatus = startTransaction(true);
518
		ExtensionType taxCommentExtensionType = (ExtensionType)getTermService().find(PesiTransformer.taxCommentUuid);
519
		ExtensionType fauCommentExtensionType = (ExtensionType)getTermService().find(PesiTransformer.fauCommentUuid);
520
		ExtensionType fauExtraCodesExtensionType = (ExtensionType)getTermService().find(PesiTransformer.fauExtraCodesUuid);
521
		List<TaxonNameBase> taxonNameList = null;
522

    
523
		int count = 0;
524
		int pastCount = 0;
525
		Connection connection = state.getConfig().getDestination().getConnection();
526
		logger.info("Started new transaction. Fetching some " + parentPluralString + " first (max: " + limit + ") ...");
527
		logger.warn("TODO handle extensions on taxon level, not name level (");
528
		while ((taxonNameList = getNameService().list(null, limit, count, null, null)).size() > 0) {
529

    
530
			logger.info("Fetched " + taxonNameList.size() + " names. Exporting...");
531
			for (TaxonNameBase<?,?> taxonName : taxonNameList) {
532
				Set<Extension> extensions = taxonName.getExtensions();
533
				for (Extension extension : extensions) {
534
					if (extension.getType().equals(taxCommentExtensionType)) {
535
						String taxComment = extension.getValue();
536
						invokeNotes(taxComment,
537
								PesiTransformer.getNoteCategoryFk(PesiTransformer.taxCommentUuid),
538
								PesiTransformer.getNoteCategoryCache(PesiTransformer.taxCommentUuid),
539
								null, null, getTaxonKey(taxonName, state),connection);
540
					} else if (extension.getType().equals(fauCommentExtensionType)) {
541
						String fauComment = extension.getValue();
542
						invokeNotes(fauComment,
543
								PesiTransformer.getNoteCategoryFk(PesiTransformer.fauCommentUuid),
544
								PesiTransformer.getNoteCategoryCache(PesiTransformer.fauCommentUuid),
545
								null, null, getTaxonKey(taxonName, state),connection);
546
					} else if (extension.getType().equals(fauExtraCodesExtensionType)) {
547
						String fauExtraCodes = extension.getValue();
548
						invokeNotes(fauExtraCodes,
549
								PesiTransformer.getNoteCategoryFk(PesiTransformer.fauExtraCodesUuid),
550
								PesiTransformer.getNoteCategoryCache(PesiTransformer.fauExtraCodesUuid),
551
								null, null, getTaxonKey(taxonName, state),connection);
552
					}
553
				}
554

    
555
				doCount(count++, modCount, pluralString);
556
			}
557

    
558
			// Commit transaction
559
			commitTransaction(txStatus);
560
			logger.debug("Committed transaction.");
561
			logger.info("Exported " + (count - pastCount) + " names. Total: " + count);
562
			pastCount = count;
563

    
564
			// Start transaction
565
			txStatus = startTransaction(true);
566
			logger.info("Started new transaction. Fetching some names first (max: " + limit + ") ...");
567
		}
568
		if (taxonNameList.size() == 0) {
569
			logger.info("No names left to fetch.");
570
		}
571
		taxonNameList = null;
572
		// Commit transaction
573
		commitTransaction(txStatus);
574
		logger.debug("Committed transaction.");
575
		return success;
576
	}
577

    
578
	/**
579
	 * @param taxComment
580
	 * @param noteCategoryFk
581
	 * @param noteCategoryCache
582
	 * @param object
583
	 * @param object2
584
	 */
585
	private void invokeNotes(String note, Integer noteCategoryFk,
586
			String noteCategoryCache, Integer languageFk, String languageCache,
587
			Integer taxonFk, Connection connection) {
588
		String notesSql = "UPDATE Note SET Note_1 = ?, NoteCategoryFk = ?, NoteCategoryCache = ?, LanguageFk = ?, LanguageCache = ? WHERE TaxonFk = ?";
589
		try {
590
			PreparedStatement notesStmt = connection.prepareStatement(notesSql);
591

    
592
			if (note != null) {
593
				notesStmt.setString(1, note);
594
			} else {
595
				notesStmt.setObject(1, null);
596
			}
597

    
598
			if (noteCategoryFk != null) {
599
				notesStmt.setInt(2, noteCategoryFk);
600
			} else {
601
				notesStmt.setObject(2, null);
602
			}
603

    
604
			if (noteCategoryCache != null) {
605
				notesStmt.setString(3, noteCategoryCache);
606
			} else {
607
				notesStmt.setObject(3, null);
608
			}
609

    
610
			if (languageFk != null) {
611
				notesStmt.setInt(4, languageFk);
612
			} else {
613
				notesStmt.setObject(4, null);
614
			}
615

    
616
			if (languageCache != null) {
617
				notesStmt.setString(5, languageCache);
618
			} else {
619
				notesStmt.setObject(5, null);
620
			}
621

    
622
			if (taxonFk != null) {
623
				notesStmt.setInt(6, taxonFk);
624
			} else {
625
				notesStmt.setObject(6, null);
626
			}
627

    
628
			notesStmt.executeUpdate();
629
		} catch (SQLException e) {
630
			logger.error("Note could not be created: " + note);
631
			e.printStackTrace();
632
		}
633

    
634

    
635
	}
636

    
637
	/**
638
	 * Deletes all entries of database tables related to <code>Note</code>.
639
	 * @param state The PesiExportState
640
	 * @return Whether the delete operation was successful or not.
641
	 */
642
	protected boolean doDelete(PesiExportState state) {
643
		PesiExportConfigurator pesiConfig = state.getConfig();
644

    
645
		String sql;
646
		Source destination =  pesiConfig.getDestination();
647

    
648
		// Clear NoteSource
649
		sql = "DELETE FROM NoteSource";
650
		destination.setQuery(sql);
651
		destination.update(sql);
652

    
653
		// Clear Note
654
		sql = "DELETE FROM " + dbNoteTableName;
655
		destination.setQuery(sql);
656
		destination.update(sql);
657
		return true;
658
	}
659

    
660
	/* (non-Javadoc)
661
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
662
	 */
663
	@Override
664
	protected boolean isIgnore(PesiExportState state) {
665
		return ! state.getConfig().isDoDescription();
666
	}
667

    
668

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

    
687
	/**
688
	 * Returns the <code>NoteCategoryFk</code> attribute.
689
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
690
	 * @return The <code>NoteCategoryFk</code> attribute.
691
	 * @see MethodMapper
692
	 */
693
	private static Integer getNoteCategoryFk(DescriptionElementBase descriptionElement) {
694
		Integer result = null;
695
		result = PesiTransformer.feature2NoteCategoryFk(descriptionElement.getFeature());
696
		//TODO decide where to handle them best (configurator, transformer, single method, ...)
697
		if (excludedNoteCategories.contains(result)){
698
			result = null;
699
		}
700
		return result;
701
	}
702

    
703
	/**
704
	 * Returns the <code>NoteCategoryCache</code> attribute.
705
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
706
	 * @return The <code>NoteCategoryCache</code> attribute.
707
	 * @see MethodMapper
708
	 */
709
	@SuppressWarnings("unused")
710
	private static String getNoteCategoryCache(DescriptionElementBase descriptionElement, PesiExportState state) {
711
		return state.getTransformer().getCacheByFeature(descriptionElement.getFeature());
712
	}
713

    
714

    
715

    
716

    
717
	/**
718
	 * Returns the <code>LanguageFk</code> attribute.
719
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
720
	 * @return The <code>LanguageFk</code> attribute.
721
	 * @see MethodMapper
722
	 */
723
	@SuppressWarnings("unused")
724
	private static Integer getLanguageFk(DescriptionElementBase descriptionElement) {
725
		Language language = getLanguage(descriptionElement);
726

    
727
		return PesiTransformer.language2LanguageId(language);
728
	}
729

    
730
	/**
731
	 * Returns the <code>LanguageCache</code> attribute.
732
	 * @param descriptionElement The {@link DescriptionElementBase DescriptionElement}.
733
	 * @return The <code>LanguageCache</code> attribute.
734
	 * @throws UndefinedTransformerMethodException
735
	 * @see MethodMapper
736
	 */
737
	@SuppressWarnings("unused")
738
	private static String getLanguageCache(DescriptionElementBase descriptionElement, PesiExportState state) throws UndefinedTransformerMethodException {
739
		Language language = getLanguage(descriptionElement);
740
		return state.getTransformer().getCacheByLanguage(language);
741
	}
742

    
743
	private static Language getLanguage(DescriptionElementBase descriptionElement) {
744
		Language language = null;
745

    
746
		Map<Language, LanguageString> multilanguageText = null;
747
		if (descriptionElement.isInstanceOf(CommonTaxonName.class)) {
748
			CommonTaxonName commonTaxonName = CdmBase.deproxy(descriptionElement, CommonTaxonName.class);
749
			language = commonTaxonName.getLanguage();
750
		} else if (descriptionElement.isInstanceOf(TextData.class)) {
751
			TextData textData = CdmBase.deproxy(descriptionElement, TextData.class);
752
			multilanguageText = textData.getMultilanguageText();
753
		} else if (descriptionElement.isInstanceOf(IndividualsAssociation.class)) {
754
			IndividualsAssociation individualsAssociation = CdmBase.deproxy(descriptionElement, IndividualsAssociation.class);
755
			multilanguageText = individualsAssociation.getDescription();
756
		} else if (descriptionElement.isInstanceOf(TaxonInteraction.class)) {
757
			TaxonInteraction taxonInteraction = CdmBase.deproxy(descriptionElement, TaxonInteraction.class);
758
			multilanguageText = taxonInteraction.getDescriptions();
759
		} else {
760
			logger.debug("Given descriptionElement does not support languages. Hence LanguageCache could not be determined: " + descriptionElement.getUuid());
761
		}
762

    
763
		if (multilanguageText != null) {
764
			Set<Language> languages = multilanguageText.keySet();
765

    
766
			// TODO: Think of something more sophisticated than this
767
			if (languages.size() > 0) {
768
				language = languages.iterator().next();
769
			}
770
			if (languages.size() > 1){
771
				logger.warn("There is more than 1 language for a given description (" + descriptionElement.getClass().getSimpleName() + "):" + descriptionElement.getUuid());
772
			}
773
		}
774
		return language;
775
	}
776

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

    
804

    
805
	/**
806
	 * Returns the TaxonFk for a given TaxonName or Taxon.
807
	 * @param state The {@link DbExportStateBase DbExportState}.
808
	 * @return
809
	 */
810
	@SuppressWarnings("unused")  //used by mapper
811
	private static Integer getTaxonFk(DescriptionElementBase deb, PesiExportState state) {
812
		TaxonBase<?> entity = state.getCurrentTaxon();
813
		return state.getDbId(entity);
814
	}
815

    
816
	/**
817
	 * Returns the TaxonFk for a given TaxonName.
818
	 * @param taxonName The {@link TaxonNameBase TaxonName}.
819
	 * @param state The {@link DbExportStateBase DbExportState}.
820
	 * @return
821
	 */
822
	private static Integer getTaxonKey(TaxonNameBase<?,?> taxonName, DbExportStateBase<?, PesiTransformer> state) {
823
		return state.getDbId(taxonName);
824
	}
825

    
826
	/**
827
	 * Returns the <code>FullName</code> attribute.
828
	 * @param taxonName The {@link NonViralName NonViralName}.
829
	 * @return The <code>FullName</code> attribute.
830
	 * @see MethodMapper
831
	 */
832
	@SuppressWarnings("unused")
833
	private static String getTaxonFullNameCache(DescriptionElementBase deb, PesiExportState state) {
834

    
835
		TaxonBase<?> taxon =  state.getCurrentTaxon();
836
		TaxonNameBase<?,?> taxonName = taxon.getName();
837
		NonViralName<?> nvn = CdmBase.deproxy(taxonName, NonViralName.class);
838
		String result = getCacheStrategy(nvn).getTitleCache(nvn);
839
		return result;
840
	}
841

    
842

    
843
	/**
844
	 * Returns the CDM to PESI specific export mappings for PESI notes.
845
	 * @return The {@link PesiExportMapping PesiExportMapping}.
846
	 */
847
	private PesiExportMapping getNotesMapping() {
848
		PesiExportMapping mapping = new PesiExportMapping(dbNoteTableName);
849

    
850
		mapping.addMapper(IdMapper.NewInstance("NoteId"));
851
		mapping.addMapper(DbTextDataMapper.NewInstance(Language.ENGLISH(), "Note_1"));
852
		//TODO
853
		mapping.addMapper(MethodMapper.NewInstance("Note_2", this, DescriptionElementBase.class));
854
		mapping.addMapper(MethodMapper.NewInstance("NoteCategoryFk", this, DescriptionElementBase.class ));
855

    
856
		mapping.addMapper(MethodMapper.NewInstance("NoteCategoryCache", this, DescriptionElementBase.class, PesiExportState.class ));
857
		mapping.addMapper(MethodMapper.NewInstance("LanguageFk", this));
858
		mapping.addMapper(MethodMapper.NewInstance("LanguageCache", this, DescriptionElementBase.class, PesiExportState.class));
859

    
860
//		mapping.addMapper(MethodMapper.NewInstance("Region", this));
861
		mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("taxonFk"));
862
	//	mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
863
		mapping.addCollectionMapping(getNoteSourceMapping());
864
		return mapping;
865
	}
866

    
867
	private CollectionExportMapping<PesiExportState, PesiExportConfigurator,PesiTransformer> getNoteSourceMapping() {
868
		String tableName = "NoteSource";
869
		String collectionAttribute = "sources";
870
		IdMapper parentMapper = IdMapper.NewInstance("NoteFk");
871
		CollectionExportMapping<PesiExportState, PesiExportConfigurator, PesiTransformer> mapping = CollectionExportMapping.NewInstance(tableName, collectionAttribute, parentMapper);
872
		mapping.addMapper(DbSimpleFilterMapper.NewSingleNullAttributeInstance("idInSource", "Sources with idInSource currently handle data lineage"));
873
		mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceFk"));
874
		mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceNameCache", IS_CACHE));
875
		mapping.addMapper(DbStringMapper.NewInstance("CitationMicroReference", "SourceDetail"));
876
		return mapping;
877
	}
878

    
879

    
880
	/**
881
	 * Returns the CDM to PESI specific export mappings for occurrences.
882
	 * @return The {@link PesiExportMapping PesiExportMapping}.
883
	 */
884
	private PesiExportMapping getOccurrenceMapping() {
885
		PesiExportMapping mapping = new PesiExportMapping(dbOccurrenceTableName);
886

    
887
		mapping.addMapper(IdMapper.NewInstance("OccurrenceId"));
888
		mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("taxonFk"));
889
		mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("TaxonFullNameCache", true, true, null));
890

    
891
		mapping.addMapper(DbAreaMapper.NewInstance(Distribution.class, "Area", "AreaFk", ! IS_CACHE));
892
		mapping.addMapper(DbAreaMapper.NewInstance(Distribution.class, "Area", "AreaNameCache", IS_CACHE));
893
		mapping.addMapper(DbDistributionStatusMapper.NewInstance("OccurrenceStatusFk", ! IS_CACHE));
894
		mapping.addMapper(DbDistributionStatusMapper.NewInstance("OccurrenceStatusCache", IS_CACHE));
895

    
896
//		Use Occurrence source instead
897
		mapping.addMapper(DbExportIgnoreMapper.NewInstance("SourceFk", "Use OccurrenceSource table for sources instead"));
898
		mapping.addMapper(DbExportIgnoreMapper.NewInstance("SourceNameCache", "Use OccurrenceSource table for sources instead"));
899

    
900
		mapping.addMapper(DbAnnotationMapper.NewInstance(null, "Notes"));
901
		mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
902
		mapping.addCollectionMapping(getOccurrenceSourceMapping());
903

    
904
		return mapping;
905
	}
906

    
907
	private CollectionExportMapping<PesiExportState, PesiExportConfigurator, PesiTransformer> getOccurrenceSourceMapping() {
908
		String tableName = "OccurrenceSource";
909
		String collectionAttribute = "sources";
910
		IdMapper parentMapper = IdMapper.NewInstance("OccurrenceFk");
911
		CollectionExportMapping<PesiExportState, PesiExportConfigurator, PesiTransformer> mapping = CollectionExportMapping.NewInstance(tableName, collectionAttribute, parentMapper);
912
		mapping.addMapper(DbSimpleFilterMapper.NewSingleNullAttributeInstance("idInSource", "Sources with idInSource currently handle data lineage"));
913
		mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceFk"));
914
		mapping.addMapper(DbObjectMapper.NewInstance("Citation", "SourceNameCache", IS_CACHE));
915
		mapping.addMapper(DbOriginalNameMapper.NewInstance("OldTaxonName", IS_CACHE, null));
916

    
917
		return mapping;
918
	}
919

    
920

    
921
	/**
922
	 * Returns the CDM to PESI specific export mappings for additional taxon sources to create a new
923
	 * source for the additional source
924
	 * @see #{@link PesiDescriptionExport#getAdditionalTaxonSourceMapping()}
925
	 * @return The {@link PesiExportMapping PesiExportMapping}.
926
	 */
927
	private PesiExportMapping getAddTaxonSourceSourceMapping() {
928
		PesiExportMapping sourceMapping = new PesiExportMapping(PesiSourceExport.dbTableName);
929

    
930
		sourceMapping.addMapper(IdMapper.NewInstance("SourceId"));
931
		sourceMapping.addMapper(DbConstantMapper.NewInstance("SourceCategoryFk", Types.INTEGER, PesiTransformer.REF_UNRESOLVED));
932
		sourceMapping.addMapper(DbConstantMapper.NewInstance("SourceCategoryCache", Types.VARCHAR, PesiTransformer.REF_STR_UNRESOLVED));
933

    
934
//		sourceMapping.addMapper(MethodMapper.NewInstance("NomRefCache", PesiSourceExport.class, "getNomRefCache", Reference.class));
935

    
936
		sourceMapping.addMapper(DbTextDataMapper.NewInstance(Language.ENGLISH(), "NomRefCache"));
937

    
938
		return sourceMapping;
939
	}
940

    
941

    
942
	/**
943
	 * Returns the CDM to PESI specific export mappings for additional taxon sources.
944
	 * @see #{@link PesiDescriptionExport#getAddTaxonSourceSourceMapping()}
945
	 * @return The {@link PesiExportMapping PesiExportMapping}.
946
	 */
947
	private PesiExportMapping getAdditionalTaxonSourceMapping() {
948

    
949
		PesiExportMapping mapping = new PesiExportMapping(dbAdditionalSourceTableName);
950

    
951
		mapping.addMapper(MethodMapper.NewInstance("TaxonFk", this, DescriptionElementBase.class, PesiExportState.class));
952

    
953
		mapping.addMapper(IdMapper.NewInstance("SourceFk"));
954
		mapping.addMapper(DbTextDataMapper.NewInstance(Language.ENGLISH(), "SourceNameCache"));
955

    
956
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseFk", Types.INTEGER, PesiTransformer.NOMENCLATURAL_REFERENCE));
957
		mapping.addMapper(DbConstantMapper.NewInstance("SourceUseCache", Types.VARCHAR, PesiTransformer.STR_NOMENCLATURAL_REFERENCE));
958

    
959
		mapping.addMapper(DbExportIgnoreMapper.NewInstance("SourceDetail", "SourceDetails not available for additional sources"));
960

    
961
		return mapping;
962
	}
963

    
964
	/**
965
	 * Returns the CDM to PESI specific export mappings for common names.
966
	 * @return The {@link PesiExportMapping PesiExportMapping}.
967
	 */
968
	private PesiExportMapping getVernacularNamesMapping() {
969
		PesiExportMapping mapping = new PesiExportMapping(dbVernacularTableName);
970

    
971
		mapping.addMapper(IdMapper.NewInstance("CommonNameId"));
972
		mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("taxonFk"));
973

    
974
		mapping.addMapper(DbStringMapper.NewInstance("Name", "CommonName"));
975
		mapping.addMapper(DbAreaMapper.NewInstance(CommonTaxonName.class, "Area", "Region", IS_CACHE));
976

    
977
		mapping.addMapper(DbLanguageMapper.NewInstance(CommonTaxonName.class, "Language", "LanguageFk", ! IS_CACHE));
978
		mapping.addMapper(DbLanguageMapper.NewInstance(CommonTaxonName.class, "Language", "LanguageCache", IS_CACHE));
979

    
980
		mapping.addMapper(DbSingleSourceMapper.NewInstance("SourceFk", of ( DbSingleSourceMapper.EXCLUDE.WITH_ID) , ! IS_CACHE));
981
		mapping.addMapper(DbSingleSourceMapper.NewInstance("SourceNameCache", of ( DbSingleSourceMapper.EXCLUDE.WITH_ID) , IS_CACHE));
982
		//mapping.addMapper(ExpertsAndLastActionMapper.NewInstance());
983
		return mapping;
984

    
985
	}
986

    
987
	private PesiExportMapping getImageMapping() {
988
		PesiExportMapping mapping = new PesiExportMapping(dbImageTableName);
989
		mapping.addMapper(DbDescriptionElementTaxonMapper.NewInstance("taxonFk"));
990

    
991
		//TODO xxx
992

    
993
		return mapping;
994
	}
995

    
996
}
(3-3/12)