Project

General

Profile

Download (14.5 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.csv.redlist.out;
11

    
12
import java.io.ByteArrayOutputStream;
13
import java.io.PrintWriter;
14
import java.util.ArrayList;
15
import java.util.Collections;
16
import java.util.Comparator;
17
import java.util.HashSet;
18
import java.util.List;
19
import java.util.Set;
20
import java.util.UUID;
21

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

    
26
import eu.etaxonomy.cdm.model.common.CdmBase;
27
import eu.etaxonomy.cdm.model.common.Language;
28
import eu.etaxonomy.cdm.model.common.RelationshipTermBase;
29
import eu.etaxonomy.cdm.model.description.CategoricalData;
30
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
31
import eu.etaxonomy.cdm.model.description.Distribution;
32
import eu.etaxonomy.cdm.model.description.Feature;
33
import eu.etaxonomy.cdm.model.description.State;
34
import eu.etaxonomy.cdm.model.description.TaxonDescription;
35
import eu.etaxonomy.cdm.model.description.TextData;
36
import eu.etaxonomy.cdm.model.location.NamedArea;
37
import eu.etaxonomy.cdm.model.name.NonViralName;
38
import eu.etaxonomy.cdm.model.taxon.Classification;
39
import eu.etaxonomy.cdm.model.taxon.Synonym;
40
import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
41
import eu.etaxonomy.cdm.model.taxon.Taxon;
42
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
43
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
44
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
45
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
46

    
47

    
48
/**
49
 * @author a.oppermann
50
 * @created 18.10.2012
51
 */
52

    
53
@Component
54
public class CsvTaxExportRedlist extends CsvExportBaseRedlist {
55
    private static final long serialVersionUID = 841703025922543361L;
56

    
57
    private static final Logger logger = Logger.getLogger(CsvTaxExportRedlist.class);
58

    
59
	private static final String ROW_TYPE = "http://rs.tdwg.org/dwc/terms/Taxon";
60
	private static final String fileName = "RedlistCoreTax.csv";
61

    
62
	public CsvTaxExportRedlist() {
63
		super();
64
		this.ioName = this.getClass().getSimpleName();
65
	}
66

    
67

    
68
	/** Retrieves data from a CDM DB and serializes them CDM to CSV.
69
	 * Starts with root taxa and traverses the classification to retrieve
70
	 * children taxa, synonyms, relationships, descriptive data, red list
71
	 * status (features).
72
	 * Taxa that are not part of the classification are not found.
73
	 *
74
	 * @param exImpConfig
75
	 * @param dbname
76
	 * @param filename
77
	 */
78
	@Override
79
	protected void doInvoke(CsvTaxExportStateRedlist state){
80
		CsvTaxExportConfiguratorRedlist config = state.getConfig();
81
		TransactionStatus txStatus = startTransaction(true);
82
		List<NamedArea> selectedAreas = config.getNamedAreas();
83
		Set<TaxonNode> taxonNodes = assembleTaxonNodeSet(config);
84

    
85
		PrintWriter writer = null;
86
		ByteArrayOutputStream byteArrayOutputStream;
87
		try {
88
			byteArrayOutputStream = config.getByteArrayOutputStream();
89
			writer = new PrintWriter(byteArrayOutputStream);
90
			//geographical Filter
91
			List<TaxonNode> filteredNodes = handleGeographicalFilter(selectedAreas, taxonNodes);
92

    
93
			//sorting List
94
			Collections.sort(filteredNodes, new Comparator<TaxonNode>() {
95

    
96
				@Override
97
				public int compare(TaxonNode tn1, TaxonNode tn2) {
98
					Taxon taxon1 = tn1.getTaxon();
99
					Taxon taxon2 = tn2.getTaxon();
100
					if(taxon1 != null && taxon2 != null){
101
						return taxon1.getTitleCache().compareTo(taxon2.getTitleCache());
102
					}
103
					else{
104
						return 0;
105
					}
106
				}
107
			});
108
			for (TaxonNode node : filteredNodes){
109
				Taxon taxon = CdmBase.deproxy(node.getTaxon(), Taxon.class);
110
				CsvTaxRecordRedlist record = assembleRecord(state);
111
				NonViralName<?> name = CdmBase.deproxy(taxon.getName(), NonViralName.class);
112
				Classification classification = node.getClassification();
113
				config.setClassificationTitleCache(classification.getTitleCache());
114
				if (! this.recordExists(taxon)){
115
					handleTaxonBase(record, taxon, name, taxon, classification, null, false, false, config);
116
					record.write(writer);
117
					this.addExistingRecord(taxon);
118
				}
119
				//misapplied names
120
				handleMisapplication(taxon, writer, classification, record, config);
121
				writer.flush();
122
			}
123
		} catch (ClassCastException e) {
124
			e.printStackTrace();
125
		}
126
		finally{
127
		    if(writer!=null){
128
		        writer.close();
129
		    }
130
			this.clearExistingRecordIds();
131
		}
132
		commitTransaction(txStatus);
133
		return;
134

    
135
	}
136

    
137

    
138
	//TODO: Exception handling
139
	protected Set<TaxonNode> assembleTaxonNodeSet(CsvTaxExportConfiguratorRedlist config){
140
		Set<TaxonNode> taxonNodes = new HashSet<>();
141
		if(config != null){
142
			Set<UUID> taxonNodeUuidSet = config.getTaxonNodeUuids();
143
			taxonNodes.addAll(getTaxonNodeService().find(taxonNodeUuidSet));
144
		}
145
		return taxonNodes;
146
	}
147

    
148
	//TODO: Exception handling
149
	private CsvTaxRecordRedlist assembleRecord(CsvTaxExportStateRedlist state) {
150
		if(state!=null){
151
			CsvTaxExportConfiguratorRedlist config = state.getConfig();
152
			CsvMetaDataRecordRedlist metaRecord = new CsvMetaDataRecordRedlist(true, fileName, ROW_TYPE);
153
			state.addMetaRecord(metaRecord);
154
			CsvTaxRecordRedlist record = new CsvTaxRecordRedlist(metaRecord, config);
155
			return record;
156
		}
157
		return null;
158
	}
159

    
160
	/**
161
	 * Takes positive List of areas and iterates over a given taxon node
162
	 * and their {@link Taxon} to return all {@link Taxon} with the desired
163
	 * geographical attribute.
164
	 *
165
	 * <p><p>
166
	 *
167
	 * If selectedAreas is null all child {@link TaxonNode}s of the given taxon node will be returned.
168
	 *
169
	 * @param selectedAreas
170
	 * @param taxonNodes
171
	 * @return
172
	 */
173
	protected List<TaxonNode> handleGeographicalFilter(List<NamedArea> selectedAreas,
174
	        Set<TaxonNode> taxonNodes) {
175
	    List<TaxonNode> filteredNodes = new ArrayList<TaxonNode>();
176
	    List<TaxonNode> allNodes = new ArrayList<TaxonNode>();
177
	    for (TaxonNode node : taxonNodes){
178
	        allNodes.addAll(getTaxonNodeService().loadChildNodesOfTaxonNode(node, null, true, null));
179
	    }
180
	    //Geographical filter
181
	    if(selectedAreas != null && !selectedAreas.isEmpty() && selectedAreas.size() < 16){
182
	        for (TaxonNode node : allNodes){
183
	            Taxon taxon = CdmBase.deproxy(node.getTaxon(), Taxon.class);
184
	            Set<TaxonDescription> descriptions = taxon.getDescriptions();
185
	            for (TaxonDescription description : descriptions){
186
	                for (DescriptionElementBase el : description.getElements()){
187
	                    if (el.isInstanceOf(Distribution.class) ){
188
	                        Distribution distribution = CdmBase.deproxy(el, Distribution.class);
189
	                        NamedArea area = distribution.getArea();
190
	                        for(NamedArea selectedArea:selectedAreas){
191
	                            if(selectedArea.getUuid().equals(area.getUuid())){
192
	                                filteredNodes.add(node);
193
	                            }
194
	                        }
195
	                    }
196
	                }
197
	            }
198
	        }
199
	    }else{
200
	        filteredNodes = allNodes;
201
	    }
202
	    return filteredNodes;
203
	}
204

    
205
	/**
206
	 * handles misapplied {@link Taxon}
207
	 * @param taxon
208
	 * @param writer
209
	 * @param classification
210
	 * @param metaRecord
211
	 * @param config
212
	 */
213
	private void handleMisapplication(Taxon taxon, PrintWriter writer, Classification classification, CsvTaxRecordRedlist record, CsvTaxExportConfiguratorRedlist config) {
214
		Set<Taxon> misappliedNames = taxon.getMisappliedNames();
215
		for (Taxon misappliedName : misappliedNames ){
216
//			CsvTaxRecordRedlist record = new CsvTaxRecordRedlist(metaRecord, config);
217
			TaxonRelationshipType relType = TaxonRelationshipType.MISAPPLIED_NAME_FOR();
218
			NonViralName<?> name = CdmBase.deproxy(misappliedName.getName(), NonViralName.class);
219

    
220
			if (! this.recordExists(misappliedName)){
221
				handleTaxonBase(record, misappliedName, name, taxon, classification, relType, false, false, config);
222
				record.write(writer);
223
				this.addExistingRecord(misappliedName);
224
			}
225
		}
226
	}
227

    
228
	/**
229
	 * handles the information record for the actual {@link Taxon} including {@link Classification classification}, Taxon Name, Taxon ID,
230
	 * Taxon Status, Synonyms, {@link Feature features} data
231
	 * @param record the concrete information record
232
	 * @param taxonBase {@link Taxon}
233
	 * @param name
234
	 * @param acceptedTaxon
235
	 * @param parent
236
	 * @param basionym
237
	 * @param isPartial
238
	 * @param isProParte
239
	 * @param config
240
	 * @param type
241
	 */
242
	private void handleTaxonBase(CsvTaxRecordRedlist record,TaxonBase<?> taxonBase,
243
			NonViralName<?> name, Taxon acceptedTaxon, Classification classification,
244
			RelationshipTermBase<?> relType, boolean isProParte, boolean isPartial,
245
			CsvTaxExportConfiguratorRedlist config) {
246

    
247
		List<Feature> features = config.getFeatures();
248
		record.setHeadLinePrinted(config.isHasHeaderLines());
249
		if(features != null) {
250
            record.setPrintFeatures(features);
251
        }
252
		config.setHasHeaderLines(false);
253

    
254
		record.setDatasetName(classification.getTitleCache());
255
		record.setScientificName(name.getTitleCache());
256
		record.setScientificNameId(name.getUuid().toString());
257
		handleTaxonomicStatus(record, name, relType, isProParte, isPartial);
258
		//synonyms
259
		handleSynonyms(record,(Taxon) taxonBase);
260
		//distribution
261
		handleDiscriptionData(record, (Taxon) taxonBase);
262
		if(features!= null) {
263

    
264
			List<List<String>> featureCells = new ArrayList<List<String>>(features.size());
265
			for(int i = 0; i < features.size(); i++) {
266
				featureCells.add(new ArrayList<String>());
267
			}
268
			handleRelatedRedlistStatus(record, (Taxon)taxonBase, false, featureCells, features);
269
			handleRelatedRedlistStatus(record, (Taxon)taxonBase, true, featureCells, features);
270

    
271
		}
272
		return;
273
	}
274

    
275
	private void handleTaxonomicStatus(
276
			CsvTaxRecordRedlist record,
277
			NonViralName<?> name,
278
			RelationshipTermBase<?> type,
279
			boolean isProParte,
280
			boolean isPartial) {
281
		if (type == null){
282
			record.setTaxonomicStatus(name.getNomenclaturalCode().acceptedTaxonStatusLabel());
283
		}else{
284
			String status = name.getNomenclaturalCode().synonymStatusLabel();
285
			if (type.equals(SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF())){
286
				status = "heterotypicSynonym";
287
			}else if(type.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){
288
				status = "homotypicSynonym";
289
			}else if(type.equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
290
				status = "misapplied";
291
			}
292
			if (isProParte){
293
				status = "proParteSynonym";
294
			}else if (isPartial){
295
				String message = "Partial synonym is not part of the gbif toxonomic status vocabulary";
296
				logger.warn(message);
297
				status = "partialSynonym";
298
			}
299

    
300
			record.setTaxonomicStatus(status);
301
		}
302
	}
303

    
304
	private void handleSynonyms(CsvTaxRecordRedlist record, Taxon taxon) {
305

    
306
		Set<Synonym> synonyms = taxon.getSynonyms();
307
		ArrayList<String> synonymLabels = new ArrayList<>();
308
		for (Synonym synonym :synonyms ){
309
			SynonymRelationshipType type = synonym.getType();
310
			if (type == null){ // should not happen
311
				type = SynonymRelationshipType.SYNONYM_OF();
312
			}
313
			NonViralName<?> name = CdmBase.deproxy(synonym.getName(), NonViralName.class);
314
			synonymLabels.add(name.getTitleCache());
315
		}
316
		record.setSynonyms(synonymLabels);
317
	}
318

    
319
	private void handleDiscriptionData(CsvTaxRecordRedlist record, Taxon taxon) {
320

    
321
		Set<TaxonDescription> descriptions = taxon.getDescriptions();
322
		ArrayList<String> distributions = new ArrayList<String>();
323
		for (TaxonDescription description : descriptions){
324
			for (DescriptionElementBase el : description.getElements()){
325
				if (el.isInstanceOf(Distribution.class) ){
326
						Distribution distribution = CdmBase.deproxy(el, Distribution.class);
327
						NamedArea area = distribution.getArea();
328
						distributions.add(area.getTitleCache());
329
				}
330

    
331
			}
332
		}
333
		record.setCountryCode(distributions);
334
	}
335

    
336
	private void handleRedlistStatus(CsvTaxRecordRedlist record, Taxon taxon, List<List<String>> featureCells, List<Feature> features){
337
		Set<TaxonDescription> descriptions = taxon.getDescriptions();
338

    
339
		for (TaxonDescription description : descriptions){
340
			for (DescriptionElementBase el : description.getElements()){
341
				if(el.isInstanceOf(CategoricalData.class)){
342
					CategoricalData categoricalData = CdmBase.deproxy(el, CategoricalData.class);
343
					for(State state:categoricalData.getStatesOnly()){
344
						Feature stateFeature = categoricalData.getFeature();
345
						// find matching feature and put data into according cell
346
						for(int i = 0; i < features.size(); i++) {
347
							if(features.get(i).equals(stateFeature)){
348
								List<String> cell = featureCells.get(i);
349
								cell.add(state.toString());
350
							}
351
						}
352
					}
353
				}else if(el.isInstanceOf(TextData.class)){
354
					TextData textData = CdmBase.deproxy(el, TextData.class);
355
					Feature textFeature = textData.getFeature();
356
					// find matching feature and put data into according cell
357
					for(int i = 0; i < features.size(); i++) {
358
						if(features.get(i).equals(textFeature)){
359
							List<String> cell = featureCells.get(i);
360
							String text = textData.getText(Language.GERMAN());
361
							text = text.replaceAll(System.getProperty("line.separator"), "");
362
							text = text.replaceAll("                            ", " ");
363
							cell.add(text);
364

    
365
						}
366
					}
367
				}
368
			}
369
		}
370
		record.setFeatures(featureCells);
371
	}
372

    
373

    
374
	private void handleRelatedRedlistStatus(CsvTaxRecordRedlist record, Taxon taxon, boolean relationFrom, List<List<String>> featureCells, List<Feature> features) {
375

    
376
		if (relationFrom) {
377
            handleRedlistStatus(record, taxon, featureCells, features);
378
        }
379

    
380

    
381
		Set<TaxonRelationship> taxRels;
382
		if(relationFrom){
383
			taxRels = taxon.getRelationsFromThisTaxon();
384
		}else{
385
			taxRels = taxon.getRelationsToThisTaxon();
386
		}
387
		for (TaxonRelationship taxRel:taxRels){
388
			if(taxRel.getType().equals(TaxonRelationshipType.CONGRUENT_TO())){
389
				Taxon relatedTaxon;
390
				if(relationFrom){
391
					relatedTaxon = taxRel.getToTaxon();
392
				}else{
393
					relatedTaxon = taxRel.getFromTaxon();
394
				}
395
				handleRedlistStatus(record, relatedTaxon, featureCells, features);
396
			}
397
		}
398
	}
399

    
400
	@Override
401
	protected boolean doCheck(CsvTaxExportStateRedlist state) {
402
		boolean result = true;
403
		logger.warn("No check implemented for " + this.ioName);
404
		return result;
405
	}
406

    
407
	@Override
408
	protected boolean isIgnore(CsvTaxExportStateRedlist state) {
409
		return ! state.getConfig().isDoTaxa();
410
	}
411

    
412
}
(6-6/10)