Project

General

Profile

Download (14.3 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9

    
10
package eu.etaxonomy.cdm.io.dwca.out;
11

    
12
import java.io.FileNotFoundException;
13
import java.io.IOException;
14
import java.io.PrintWriter;
15
import java.io.UnsupportedEncodingException;
16
import java.util.HashSet;
17
import java.util.List;
18
import java.util.Set;
19
import java.util.UUID;
20

    
21
import org.apache.commons.lang.StringUtils;
22
import org.apache.log4j.Logger;
23
import org.springframework.stereotype.Component;
24
import org.springframework.transaction.TransactionStatus;
25

    
26
import eu.etaxonomy.cdm.common.CdmUtils;
27
import eu.etaxonomy.cdm.io.common.ExportDataWrapper;
28
import eu.etaxonomy.cdm.model.common.Annotation;
29
import eu.etaxonomy.cdm.model.common.AnnotationType;
30
import eu.etaxonomy.cdm.model.common.CdmBase;
31
import eu.etaxonomy.cdm.model.common.RelationshipTermBase;
32
import eu.etaxonomy.cdm.model.name.INonViralName;
33
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
34
import eu.etaxonomy.cdm.model.name.Rank;
35
import eu.etaxonomy.cdm.model.name.TaxonName;
36
import eu.etaxonomy.cdm.model.reference.Reference;
37
import eu.etaxonomy.cdm.model.taxon.Classification;
38
import eu.etaxonomy.cdm.model.taxon.Synonym;
39
import eu.etaxonomy.cdm.model.taxon.SynonymType;
40
import eu.etaxonomy.cdm.model.taxon.Taxon;
41
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
42
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
43
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
44

    
45
/**
46
 * @author a.mueller
47
 * @created 18.04.2011
48
 */
49
@Component
50
public class DwcaTaxExport extends DwcaExportBase {
51
    private static final long serialVersionUID = -3770976064909193441L;
52

    
53
    private static final Logger logger = Logger.getLogger(DwcaTaxExport.class);
54

    
55
	private static final String ROW_TYPE = "http://rs.tdwg.org/dwc/terms/Taxon";
56
	private static final String fileName = "coreTax.txt";
57

    
58
	/**
59
	 *
60
	 */
61
	public DwcaTaxExport() {
62
		super();
63
		this.ioName = this.getClass().getSimpleName();
64
		this.exportData = ExportDataWrapper.NewByteArrayInstance();
65
	}
66

    
67
	/** Retrieves data from a CDM DB and serializes the CDM to XML.
68
	 * Starts with root taxa and traverses the classification to retrieve children taxa, synonyms and relationships.
69
	 * Taxa that are not part of the classification are not found.
70
	 *
71
	 * @param exImpConfig
72
	 * @param dbname
73
	 * @param filename
74
	 */
75
	@Override
76
	protected void doInvoke(DwcaTaxExportState state){
77
		DwcaTaxExportConfigurator config = state.getConfig();
78
		TransactionStatus txStatus = startTransaction(true);
79

    
80
		DwcaMetaDataRecord metaRecord = new DwcaMetaDataRecord(true, fileName, ROW_TYPE);
81
		state.addMetaRecord(metaRecord);
82

    
83
		Set<UUID> classificationUuidSet = config.getClassificationUuids();
84
		List<Classification> classificationList;
85
		if (classificationUuidSet.isEmpty()){
86
		    classificationList = getClassificationService().list(Classification.class, null, 0, null, null);
87
		}else{
88
		    classificationList = getClassificationService().find(classificationUuidSet);
89
		}
90

    
91
		Set<Classification> classificationSet = new HashSet<Classification>();
92
		classificationSet.addAll(classificationList);
93

    
94

    
95
		PrintWriter writer = null;
96
		try {
97

    
98
			writer = createPrintWriter(fileName, state);
99
			List<TaxonNode> allNodes;
100
			if (state.getAllNodes().isEmpty()){
101
			    getAllNodes(state, classificationSet);
102
			}
103
			allNodes = state.getAllNodes();
104
			int i = 0;
105
			for (TaxonNode node : allNodes){
106
				i++;
107
				Taxon taxon = CdmBase.deproxy(node.getTaxon(), Taxon.class);
108
				DwcaTaxRecord record = new DwcaTaxRecord(metaRecord, config);
109

    
110
				TaxonName name = taxon.getName();
111
				Taxon parent = node.getParent() == null ? null : node.getParent().getTaxon();
112
				TaxonName basionym = name.getBasionym();
113
				Classification classification = node.getClassification();
114
				if (! this.recordExists(taxon)){
115
					handleTaxonBase(record, taxon, name, taxon, parent, basionym, classification, null, false, false, config);
116
					record.write(writer);
117
					this.addExistingRecord(taxon);
118
				}
119

    
120
				node.getClassification().getName();
121
				//synonyms
122
				handleSynonyms(taxon, writer, classification, metaRecord, config);
123

    
124
				//misapplied names
125
				handleMisapplication(taxon, writer, classification, metaRecord, config);
126

    
127
				writer.flush();
128

    
129
			}
130
		} catch (FileNotFoundException e) {
131
			e.printStackTrace();
132
		} catch (UnsupportedEncodingException e) {
133
			e.printStackTrace();
134
		} catch (ClassCastException e) {
135
			e.printStackTrace();
136
		} catch (IOException e) {
137
			e.printStackTrace();
138
		}
139
		finally{
140

    
141
			closeWriter(writer, state);
142
		}
143
		commitTransaction(txStatus);
144
		return;
145

    
146
	}
147

    
148
	private void handleSynonyms(Taxon taxon, PrintWriter writer, Classification classification, DwcaMetaDataRecord metaRecord, DwcaTaxExportConfigurator config) {
149
		for (Synonym synonym :taxon.getSynonyms() ){
150
			DwcaTaxRecord record = new DwcaTaxRecord(metaRecord, config);
151
			SynonymType type = synonym.getType();
152
			boolean isProParte = synonym.isProParte();
153
			boolean isPartial = synonym.isPartial();
154
			if (type == null){ // should not happen
155
				type = SynonymType.SYNONYM_OF();
156
			}
157
			TaxonName name = synonym.getName();
158
			//????
159
			Taxon parent = null;
160
			TaxonName basionym = name.getBasionym();
161

    
162
			if (! this.recordExists(synonym)){
163
				handleTaxonBase(record, synonym, name, taxon, parent, basionym, classification, type, isProParte, isPartial, config);
164
				record.write(writer);
165
				this.addExistingRecord(synonym);
166
			}
167
		}
168
	}
169

    
170
	private void handleMisapplication(Taxon taxon, PrintWriter writer, Classification classification, DwcaMetaDataRecord metaRecord, DwcaTaxExportConfigurator config) {
171
		Set<Taxon> misappliedNames = taxon.getMisappliedNames();
172
		for (Taxon misappliedName : misappliedNames ){
173
			DwcaTaxRecord record = new DwcaTaxRecord(metaRecord, config);
174
			TaxonRelationshipType relType = TaxonRelationshipType.MISAPPLIED_NAME_FOR();
175
			TaxonName name = misappliedName.getName();
176
			//????
177
			Taxon parent = null;
178
			TaxonName basionym = name.getBasionym();
179

    
180
			if (! this.recordExists(misappliedName)){
181
				handleTaxonBase(record, misappliedName, name, taxon, parent, basionym, classification, relType, false, false, config);
182
				record.write(writer);
183
				this.addExistingRecord(misappliedName);
184
			}
185
		}
186
	}
187

    
188
	/**
189
	 * @param record
190
	 * @param taxonBase
191
	 * @param name
192
	 * @param acceptedTaxon
193
	 * @param parent
194
	 * @param basionym
195
	 * @param isPartial
196
	 * @param isProParte
197
	 * @param config
198
	 * @param type
199
	 */
200
	private void handleTaxonBase(DwcaTaxRecord record, TaxonBase<?> taxonBase, TaxonName name,
201
			Taxon acceptedTaxon, Taxon parent, TaxonName basionym, Classification classification,
202
			RelationshipTermBase<?> relType, boolean isProParte, boolean isPartial, DwcaTaxExportConfigurator config) {
203
		record.setId(taxonBase.getId());
204
		record.setUuid(taxonBase.getUuid());
205

    
206
		//maybe wrong as according to the DwC-A documentation only resolvable ids are allowed, this differs from DwC documentation
207
		record.setScientificNameId(name);
208
		record.setScientificName(name.getTitleCache());
209

    
210
		record.setAcceptedNameUsageId(acceptedTaxon.getUuid());
211
		record.setAcceptedNameUsage(acceptedTaxon.getName() == null? acceptedTaxon.getTitleCache() : acceptedTaxon.getName().getTitleCache());
212

    
213
		//parentNameUsage
214
		if (parent != null){
215
			record.setParentNameUsageId(parent.getUuid());
216
			record.setParentNameUsage(parent.getTitleCache());
217
		}
218

    
219
		//originalNameUsage
220
		// ??? - is not a name usage (concept)
221
		if (basionym != null){
222
			//FIXME needs to be a coreID otherwise use string only
223
//			record.setOriginalNameUsageId(basionym.getUuid());
224
			record.setOriginalNameUsageId(null);
225
			record.setOriginalNameUsage(basionym.getTitleCache());
226
		}
227

    
228
		//nameAccordingTo
229
		Reference sec = taxonBase.getSec();
230
		if (sec == null){
231
			String message = "There is a taxon without sec " + taxonBase.getTitleCache() + "( " + taxonBase.getId() + ")";
232
			logger.warn(message);
233
		}else{
234
			record.setNameAccordingToId(taxonBase.getSec().getUuid());
235
			record.setNameAccordingTo(taxonBase.getSec().getTitleCache());
236
		}
237

    
238
		//namePublishedIn
239
		// ??? is not a nameUsage (concept)
240
		if (name.getNomenclaturalReference() != null){
241
			record.setNamePublishedInId(name.getNomenclaturalReference().getUuid());
242
			record.setNamePublishedIn(name.getNomenclaturalReference() == null ? null : name.getNomenclaturalReference().getTitleCache());
243
		}
244

    
245
		// what is the exact difference to id and acceptedNameUsageId
246
		record.setTaxonConceptId(taxonBase.getUuid());
247

    
248
		//Classification
249
		if (config.isWithHigherClassification()){
250
			//FIXME all classification and rank specific fields are meant to represent the classification
251
			//currently the information is only compiled for the exact same range but it should be compiled
252
			//for all ranks above the rank of this taxon
253
			//TODO we do not support this yet
254
			record.setHigherClassification(null);
255
			//... higher ranks
256
			handleUninomialOrGenus(record, name);
257
			if (name.getRank() != null &&  name.getRank().equals(Rank.SUBGENUS())){
258
				record.setSubgenus(name.getNameCache());
259
			}
260
			//record.setSubgenus(name.getInfraGenericEpithet());
261
		}
262
		if (name.getRank() != null &&  (name.getRank().isSupraGeneric() || name.getRank().isGenus())){
263
			record.setUninomial(name.getGenusOrUninomial());
264
		}else{
265
			record.setGenusPart(name.getGenusOrUninomial());
266
		}
267
		record.setInfraGenericEpithet(name.getInfraGenericEpithet());
268

    
269
		record.setSpecificEpithet(name.getSpecificEpithet());
270
		record.setInfraspecificEpithet(name.getInfraSpecificEpithet());
271

    
272
		record.setTaxonRank(name.getRank());
273
		if (name.getRank() != null){
274
			record.setVerbatimTaxonRank(name.getRank().getAbbreviation());
275
		}else{
276
			String message = "No rank available for " + name.getTitleCache() + "(" + name.getId() + ")";
277
			logger.warn(message);
278
		}
279

    
280
		record.setScientificNameAuthorship(name.getAuthorshipCache());
281

    
282
		// ??? - use for TextData common names?
283
		record.setVernacularName(null);
284

    
285
		record.setNomenclaturalCode(name.getNomenclaturalCode());
286
		// ??? TODO Misapplied Names, inferred synonyms
287
		handleTaxonomicStatus(record, name, relType, isProParte, isPartial);
288
		handleNomStatus(record, taxonBase, name);
289

    
290
		// TODO we need to differentiate technical
291
		String taxonRemarks = "";
292
		for (Annotation annotation : taxonBase.getAnnotations()){
293
			if (AnnotationType.EDITORIAL().equals(annotation.getAnnotationType())){
294
				taxonRemarks += CdmUtils.Nz(annotation.getText());
295
			}
296
		}
297
		for (Annotation annotation : name.getAnnotations()){
298
			if (AnnotationType.EDITORIAL().equals(annotation.getAnnotationType())){
299
				taxonRemarks += CdmUtils.Nz(annotation.getText());
300
			}
301
		}
302
		if (StringUtils.isNotBlank(taxonRemarks)){
303
			record.setTaxonRemarks(taxonRemarks);
304
		}
305

    
306
		// TODO which date is needed here (taxon, name, sec, ... ?)
307
		record.setModified(taxonBase.getUpdated());
308

    
309
		// ???
310
		record.setLanguage(null);
311

    
312
		record.setRights(taxonBase.getRights());
313

    
314
		//TODO
315
		record.setRightsHolder(null);
316
		record.setAccessRights(null);
317

    
318
		//TODO currently only via default value
319
		record.setBibliographicCitation(null);
320
		record.setInformationWithheld(null);
321

    
322
		record.setDatasetId(classification);
323
		record.setDatasetName(classification.getTitleCache());
324

    
325
		//TODO
326
		record.setSource(null);
327

    
328
		return;
329
	}
330

    
331
	/**
332
	 * @param record
333
	 * @param name
334
	 * @param type
335
	 * @param isPartial
336
	 * @param isProParte
337
	 */
338
	private void handleTaxonomicStatus(DwcaTaxRecord record,
339
			INonViralName name, RelationshipTermBase<?> type,
340
			boolean isProParte, boolean isPartial) {
341
		if (type == null){
342
			record.setTaxonomicStatus(name.getNomenclaturalCode().acceptedTaxonStatusLabel());
343
		}else{
344
			String status = name.getNomenclaturalCode().synonymStatusLabel();
345
			if (type.equals(SynonymType.HETEROTYPIC_SYNONYM_OF())){
346
				status = "heterotypicSynonym";
347
			}else if(type.equals(SynonymType.HOMOTYPIC_SYNONYM_OF())){
348
				status = "homotypicSynonym";
349
			}else if(type.equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
350
				status = "misapplied";
351
			}
352
			if (isProParte){
353
				status = "proParteSynonym";
354
			}else if (isPartial){
355
				String message = "Partial synonym is not part of the gbif toxonomic status vocabulary";
356
				logger.warn(message);
357
				status = "partialSynonym";
358
			}
359

    
360
			record.setTaxonomicStatus(status);
361
		}
362
	}
363

    
364
	/**
365
	 * @param record
366
	 * @param name
367
	 */
368
	private void handleUninomialOrGenus(DwcaTaxRecord record, INonViralName name) {
369
		//epethita
370
		String firstEpi = name.getGenusOrUninomial();
371
		if (!StringUtils.isBlank(firstEpi)){
372
			Rank rank = name.getRank();
373
			if (rank != null){
374
				if (rank.isLower(Rank.GENUS())){
375
					record.setGenus(firstEpi);
376
				}else if (rank.equals(Rank.GENUS())){
377
					record.setGenus(firstEpi);
378
				}else if (rank.equals(Rank.KINGDOM())){
379
					record.setKingdom(firstEpi);
380
				}else if (rank.equals(Rank.PHYLUM())){
381
					record.setPhylum(firstEpi);
382
				}else if (rank.equals(Rank.CLASS())){
383
					record.setClazz(firstEpi);
384
				}else if (rank.equals(Rank.ORDER())){
385
					record.setOrder(firstEpi);
386
				}else if (rank.equals(Rank.FAMILY())){
387
					record.setFamily(firstEpi);
388
				}else{
389
					// !!!
390
					String message = "Rank not covered. Set uninomial as genus instead: " + rank.getLabel();
391
					logger.warn(message);
392
//					record.setGenus(firstEpi);
393
				}
394

    
395
			}
396
		}
397
	}
398

    
399

    
400
	/**
401
	 * @param record
402
	 * @param taxon
403
	 * @param name
404
	 */
405
	private void handleNomStatus(DwcaTaxRecord record, TaxonBase<?> taxon,
406
			INonViralName name) {
407
		int nStatus = name.getStatus().size();
408
		if (nStatus > 0){
409
			if (name.getStatus().size()> 1){
410
				String warning = "There is more than 1 nomenclatural status ( " + name.getStatus().size()+ "): " + taxon.getTitleCache();
411
				logger.warn(warning);
412
			}
413
			NomenclaturalStatusType status = name.getStatus().iterator().next().getType();
414
			record.setNomenclaturalStatus(status);
415
		}else{
416
			record.setNomenclaturalStatus(null);
417
		}
418
	}
419

    
420

    
421
	@Override
422
	protected boolean doCheck(DwcaTaxExportState state) {
423
		boolean result = true;
424
		logger.warn("No check implemented for " + this.ioName);
425
		return result;
426
	}
427

    
428

    
429
	@Override
430
	protected boolean isIgnore(DwcaTaxExportState state) {
431
		return ! state.getConfig().isDoTaxa();
432
	}
433

    
434

    
435
}
(18-18/29)