Project

General

Profile

Download (20 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.demo;
11

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

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

    
28
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
29
import eu.etaxonomy.cdm.model.common.CdmBase;
30
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
31
import eu.etaxonomy.cdm.model.common.Language;
32
import eu.etaxonomy.cdm.model.common.RelationshipTermBase;
33
import eu.etaxonomy.cdm.model.description.CategoricalData;
34
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
35
import eu.etaxonomy.cdm.model.description.Distribution;
36
import eu.etaxonomy.cdm.model.description.Feature;
37
import eu.etaxonomy.cdm.model.description.State;
38
import eu.etaxonomy.cdm.model.description.TaxonDescription;
39
import eu.etaxonomy.cdm.model.description.TextData;
40
import eu.etaxonomy.cdm.model.location.NamedArea;
41
import eu.etaxonomy.cdm.model.name.INonViralName;
42
import eu.etaxonomy.cdm.model.reference.Reference;
43
import eu.etaxonomy.cdm.model.taxon.Classification;
44
import eu.etaxonomy.cdm.model.taxon.Synonym;
45
import eu.etaxonomy.cdm.model.taxon.SynonymType;
46
import eu.etaxonomy.cdm.model.taxon.Taxon;
47
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
48
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
49
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
50
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
51

    
52

    
53
/**
54
 * @author a.oppermann
55
 * @since 18.10.2012
56
 */
57

    
58
@Component
59
public class CsvDemoExport extends CsvDemoBase {
60

    
61
    private static final long serialVersionUID = 8265935377927091897L;
62

    
63
    private static final Logger logger = Logger.getLogger(CsvDemoExport.class);
64

    
65
	private static final String ROW_TYPE = "http://rs.tdwg.org/dwc/terms/Taxon";
66
	private static final String fileName = "RedlistCoreTax.csv";
67

    
68
	public CsvDemoExport() {
69
		super();
70
		this.ioName = this.getClass().getSimpleName();
71
	}
72

    
73

    
74
	/** Retrieves data from a CDM DB and serializes them CDM to CSV.
75
	 * Starts with root taxa and traverses the classification to retrieve
76
	 * children taxa, synonyms, relationships, descriptive data, red list
77
	 * status (features).
78
	 * Taxa that are not part of the classification are not found.
79
	 *
80
	 * @param exImpConfig
81
	 * @param dbname
82
	 * @param filename
83
	 */
84
	@Override
85
	protected void doInvoke(CsvDemoExportState state){
86
	    CsvDemoExportConfigurator config = state.getConfig();
87
	    TransactionStatus txStatus = startTransaction(true);
88

    
89
	    List<NamedArea> selectedAreas = config.getNamedAreas();
90
	    Set<Classification> classificationSet = assembleClassificationSet(config);
91

    
92
	    IProgressMonitor progressMonitor = null;
93
	    if(config.getProgressMonitor() != null) {
94
	        progressMonitor = config.getProgressMonitor();
95
	    }
96
	    PrintWriter writer = null;
97
	    try {
98
	        //json/xml result list
99
            List<CsvDemoRecord> recordList = null;
100
            if(config.getRecordList() != null){
101
                recordList = config.getRecordList();
102
                performJsonXMLPagination(state, config, txStatus, classificationSet, recordList);
103
            }
104

    
105
            try {
106
                switch(config.getTarget()) {
107
                case FILE:
108
                    if(!config.getDestination().isDirectory()){
109
                        writer = new PrintWriter(config.getDestination());
110
                    }
111
                    break;
112
                case EXPORT_DATA:
113
                    exportStream = new ByteArrayOutputStream();;
114
                    writer = new PrintWriter(exportStream);
115
                    break;
116
                default :
117
                    break;
118
                }
119
                if(writer != null) {
120
                    performCSVExport(state, config, txStatus, classificationSet, progressMonitor, writer);
121
                }
122

    
123
            } catch (FileNotFoundException e) {
124
                e.printStackTrace();
125
            }
126

    
127
	    } catch (ClassCastException e) {
128
	        e.printStackTrace();
129
	    }
130
	    finally{
131
	        if (exportStream != null){
132
	            state.getResult().addExportData(getByteArray());
133
	        }
134
	        if(writer != null){
135
	            writer.close();
136
	        }
137
	        this.clearExistingRecordIds();
138
	    }
139

    
140
//	    commitTransaction(txStatus);
141
	    return;
142
	}
143

    
144

    
145
    /**
146
     * @param state
147
     * @param config
148
     * @param txStatus
149
     * @param classificationSet
150
     * @param recordList
151
     */
152
    private void performJsonXMLPagination(CsvDemoExportState state, CsvDemoExportConfigurator config,
153
            TransactionStatus txStatus, Set<Classification> classificationSet, List<CsvDemoRecord> recordList) {
154
        // TODO Auto-generated method stub
155
        Classification classification = null;
156
        for(Classification c : classificationSet){
157
            classification = c;
158
            //this sets the total amount of records for pagination
159
            config.setTaxonNodeListSize(getTaxonNodeService().countAllNodesForClassification(c));
160
        }
161
        //calculate pagination
162
        int start = config.getPageSize() * config.getPageNumber();
163
        List<TaxonNode> result = getTaxonNodeService().listAllNodesForClassification(classification, start, config.getPageSize());
164

    
165
        for (TaxonNode node : result){
166
            Taxon taxon = CdmBase.deproxy(node.getTaxon(), Taxon.class);
167
            CsvDemoRecord record = assembleRecord(state);
168
            INonViralName name = taxon.getName();
169
            config.setClassificationTitleCache(classification.getTitleCache());
170
            if (! this.recordExists(taxon)){
171
                handleTaxonBase(record, taxon, name, classification, null, false, false, config, node);
172
                recordList.add(record);
173
                this.addExistingRecord(taxon);
174
            }
175
        }
176
        commitTransaction(txStatus);
177
    }
178

    
179

    
180
    /**
181
     * @param state
182
     * @param config
183
     * @param txStatus
184
     * @param classificationSet
185
     * @param progressMonitor
186
     * @param writer
187
     * @return
188
     */
189
	private void performCSVExport(CsvDemoExportState state, CsvDemoExportConfigurator config,
190
	        TransactionStatus txStatus, Set<Classification> classificationSet, IProgressMonitor progressMonitor,
191
	        PrintWriter writer) {
192
	    //obtain chuncks of taxonNodes
193
	    int totalWork = 0;
194
	    int work = 0;
195
	    int limit = 500;
196
	    int end = 500;
197
	    int start = 0;
198

    
199
	    //TODO: Questionable if this information is really necessary, with respect to memory usage
200
	    Classification classification = null;
201
	    for(Classification c : classificationSet){
202
	    	classification = c;
203
	    	totalWork = getTaxonNodeService().countAllNodesForClassification(c);
204

    
205

    
206
	    	if(progressMonitor != null) {
207
	    		progressMonitor.beginTask("", totalWork);
208
	    	}
209
	    	List<TaxonNode> result = new ArrayList<>();
210
	    	int totalNodes = getTaxonNodeService().count(TaxonNode.class);
211

    
212
	    	for(int i = 0 ; i < totalNodes; i++){
213

    
214
	    		//geographical Filter
215
	    		//	     List<TaxonNode> taxonNodes =  handleGeographicalFilter(state, classificationSet, config, limit, start);
216

    
217
	    		result = getTaxonNodeService().listAllNodesForClassification(classification, start, limit);
218

    
219
	    		logger.info(result.size());
220

    
221

    
222
	    		for (TaxonNode node : result){
223
	    			Taxon taxon = CdmBase.deproxy(node.getTaxon(), Taxon.class);
224
	    			CsvDemoRecord record = assembleRecord(state);
225
	    			INonViralName name = taxon.getName();
226
	    			//	                Classification classification = node.getClassification();
227
	    			config.setClassificationTitleCache(classification.getTitleCache());
228
	    			if (! this.recordExists(taxon)){
229

    
230
	    				handleTaxonBase(record, taxon, name, classification, null, false, false, config, node);
231
	    				//if(config.getDestination() != null){
232
	    					record.write(writer);
233
	    				//}
234
	    				this.addExistingRecord(taxon);
235
	    			}
236
	    			//misapplied names
237
	    			//handleMisapplication(taxon, writer, classification, record, config, node);
238

    
239
	    			if(progressMonitor !=null) {
240
	    				if(work < totalWork-1) {
241
	    					progressMonitor.worked(1);
242
	    				}
243
	    				work++;
244
	    			}
245
	    		}
246
	    		if(writer != null){
247
	    			writer.flush();
248
	    			commitTransaction(txStatus);
249
	    			txStatus = startTransaction(true);
250
	    		}
251
	    		//get next 1000 results
252
	    		if(result.size()%limit == 0){
253
	    			//increase only once to avoid same row
254
	    			if(i==0){
255
	    				start++;
256
	    			}
257
	    			start = start + limit;
258
	    			end = end + limit;
259
	    			result = null;
260
	    		}else{
261
	    			break;
262
	    		}
263
	    	}
264
	    }
265
	}
266

    
267

    
268
    //TODO: Exception handling
269
	/**
270
	 *
271
	 * @param config
272
	 * @return
273
	 */
274
	protected Set<Classification> assembleClassificationSet(CsvDemoExportConfigurator config){
275
		if(config != null){
276
			Set<UUID> classificationUuidSet = config.getClassificationUuids();
277
			List<Classification> classificationList = getClassificationService().find(classificationUuidSet);
278
			Set<Classification> classificationSet = new HashSet<>();
279
			classificationSet.addAll(classificationList);
280
			return classificationSet;
281
		}
282
		return null;
283
	}
284

    
285
	//TODO: Exception handling
286
	/**
287
	 *
288
	 * @param state
289
	 * @return
290
	 */
291
	private CsvDemoRecord assembleRecord(CsvDemoExportState state) {
292
		if(state!=null){
293
			CsvDemoExportConfigurator config = state.getConfig();
294
			CsvDemoMetaDataRecord metaRecord = new CsvDemoMetaDataRecord(true, fileName, ROW_TYPE);
295
			state.addMetaRecord(metaRecord);
296
			CsvDemoRecord record = new CsvDemoRecord(metaRecord, config);
297
			return record;
298
		}
299
		return null;
300
	}
301

    
302

    
303
	/**
304
	 * handles the information record for the actual {@link Taxon} including {@link Classification classification}, Taxon Name, Taxon ID,
305
	 * Taxon Status, Synonyms, {@link Feature features} data
306
	 * @param record the concrete information record
307
	 * @param taxonBase {@link Taxon}
308
	 * @param name
309
	 * @param acceptedTaxon
310
	 * @param parent
311
	 * @param basionym
312
	 * @param isPartial
313
	 * @param isProParte
314
	 * @param config
315
	 * @param node
316
	 * @param node
317
	 * @param type
318
	 */
319
	private void handleTaxonBase(CsvDemoRecord record, TaxonBase<?> taxonBase,
320
			INonViralName name, Classification classification,
321
			RelationshipTermBase<?> relType, boolean isProParte, boolean isPartial,
322
			CsvDemoExportConfigurator config, TaxonNode node) {
323

    
324
		Taxon taxon = (Taxon) taxonBase;
325
		List<Feature> features = config.getFeatures();
326
		if(config.getDestination() != null){
327
		    record.setHeadLinePrinted(config.isHasHeaderLines());
328
		    if(config.isRedlistFeatures()){
329
		        if(features != null){
330
		            record.setPrintFeatures(features);
331
		        }
332
		    }
333
		    config.setHasHeaderLines(false);
334
		}
335
		if(config.isClassification()){
336
			record.setDatasetName(classification.getTitleCache());
337
		}
338
		if(config.isTaxonName()){
339
			record.setScientificName(name.getNameCache());
340
		}
341
		if(config.isTaxonNameID()){
342
			record.setScientificNameId(name.getUuid().toString());
343
		}
344
		if(config.isAuthor()){
345
			String authorshipCache = name.getAuthorshipCache();
346
			if(authorshipCache == null){
347
				authorshipCache = "";
348
			}
349
			record.setAuthorName(authorshipCache);
350
		}
351
		if(config.isRank()){
352
			String rank;
353
			if(taxon.getName().getRank() == null){
354
				rank = "";
355
			}else{
356
				rank = taxon.getName().getRank().toString();
357
			}
358
			record.setRank(rank);
359
		}
360
		if(config.isTaxonStatus()){
361
			handleTaxonomicStatus(record, name, relType, isProParte, isPartial);
362
		}
363
		if(config.isAcceptedName()){
364
			//TODO write routine for accepted Name
365
		}
366
		if(config.isTaxonConceptID()){
367
			UUID taxonUuid = taxonBase.getUuid();
368
			if(taxonUuid == null){
369
				taxonUuid = UUID.fromString("");
370
			}
371
			record.setTaxonConceptID(taxonUuid.toString());
372
		}
373
		if(config.isParentID()){
374
			String parentUUID;
375
			if(node.getParent().getTaxon() == null){
376
				parentUUID = "";
377
			}else{
378
				parentUUID = node.getParent().getTaxon().getUuid().toString();
379
			}
380
			record.setParentUUID(parentUUID);
381
		}
382
		if(config.isLastChange()){
383
			String lastChange;
384
			if(taxon.getUpdated() == null){
385
				lastChange = "";
386
			}else{
387
				lastChange = taxon.getUpdated().toString();
388
			}
389
			record.setLastChange(lastChange);
390
		}
391
		if(config.isSynonyms()){
392
			handleSynonyms(record,taxon);
393
		}
394
		if(config.isDistributions()){
395
			handleDiscriptionData(record, taxon);
396
		}
397
		if(config.isRedlistFeatures()){
398
			if(features!= null) {
399

    
400
				List<List<String>> featureCells = new ArrayList<List<String>>(features.size());
401
				for(int i = 0; i < features.size(); i++) {
402
					featureCells.add(new ArrayList<String>());
403
				}
404
				handleRelatedRedlistStatus(record, taxon, false, featureCells, features);
405
				handleRelatedRedlistStatus(record, taxon, true, featureCells, features);
406

    
407
			}
408
		}
409

    
410
		if(config.isExternalID()){
411
			Set<IdentifiableSource> sources = taxonBase.getSources();
412
			for(IdentifiableSource source:sources){
413
				Reference citation = source.getCitation();
414
				/*
415
				 * TODO: handle this more generic.
416
				 * see ticket #4040
417
				 *
418
				 */
419
				if(citation.getId() == 22){
420
					String idInSource = source.getIdInSource();
421
					if(idInSource == null){
422
						idInSource = "";
423
					}
424
					record.setExternalID(idInSource);
425

    
426
				}
427
			}
428
		}
429
	}
430

    
431
	/**
432
	 * @param record
433
	 * @param name
434
	 * @param type
435
	 * @param isPartial
436
	 * @param isProParte
437
	 */
438
	private void handleTaxonomicStatus(
439
			CsvDemoRecord record,
440
			INonViralName name,
441
			RelationshipTermBase<?> type,
442
			boolean isProParte,
443
			boolean isPartial) {
444
		if (type == null && name.getNomenclaturalCode()!= null && name.getNomenclaturalCode().acceptedTaxonStatusLabel() != null){
445
			String acceptedTaxonStatusLabel = name.getNomenclaturalCode().acceptedTaxonStatusLabel();
446
			if(StringUtils.isEmpty(acceptedTaxonStatusLabel)){
447
				acceptedTaxonStatusLabel="";
448
			}
449
			record.setTaxonomicStatus(acceptedTaxonStatusLabel);
450
		}else if(name.getNomenclaturalCode() != null && name.getNomenclaturalCode().synonymStatusLabel() != null){
451
			String status = name.getNomenclaturalCode().synonymStatusLabel();
452
			if (type.equals(SynonymType.HETEROTYPIC_SYNONYM_OF())){
453
				status = "heterotypicSynonym";
454
			}else if(type.equals(SynonymType.HOMOTYPIC_SYNONYM_OF())){
455
				status = "homotypicSynonym";
456
			}else if(type.equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
457
				status = "misapplied";
458
			}else if(type.equals(TaxonRelationshipType.PRO_PARTE_MISAPPLIED_NAME_FOR())){
459
                status = "proParteMisapplied";
460
            }
461
			if (isProParte){
462
				status = "proParteSynonym";
463
			}else if (isPartial){
464
				String message = "Partial synonym is not part of the gbif toxonomic status vocabulary";
465
				logger.warn(message);
466
				status = "partialSynonym";
467
			}
468

    
469
			record.setTaxonomicStatus(status);
470
		}
471
	}
472

    
473
	/**
474
	 *
475
	 * This method concatenates several synonyms in a list.
476
	 *
477
	 * @param record
478
	 * @param taxon
479
	 */
480
	private void handleSynonyms(CsvDemoRecord record, Taxon taxon) {
481

    
482
		Set<Synonym> synonyms = taxon.getSynonyms();
483
		ArrayList<String> synonymLabels = new ArrayList<>();
484
		for (Synonym synonym :synonyms ){
485
			SynonymType type = synonym.getType();
486
			if (type == null){ // should not happen
487
				type = SynonymType.SYNONYM_OF();
488
			}
489
			INonViralName name = synonym.getName();
490
			synonymLabels.add(name.getTitleCache());
491
		}
492
		record.setSynonyms(synonymLabels);
493
	}
494

    
495
	/**
496
	 *
497
	 * @param record
498
	 * @param taxon
499
	 */
500
	private void handleDiscriptionData(CsvDemoRecord record, Taxon taxon) {
501

    
502
		Set<TaxonDescription> descriptions = taxon.getDescriptions();
503
		ArrayList<String> distributions = new ArrayList<String>();
504
		for (TaxonDescription description : descriptions){
505
			for (DescriptionElementBase el : description.getElements()){
506
				if (el.isInstanceOf(Distribution.class) ){
507
						Distribution distribution = CdmBase.deproxy(el, Distribution.class);
508
						NamedArea area = distribution.getArea();
509
						distributions.add(area.getTitleCache());
510
				}
511

    
512
			}
513
		}
514
		record.setCountryCode(distributions);
515
	}
516
	/**
517
	 *
518
	 * @param record
519
	 * @param taxon
520
	 * @param featureCells
521
	 * @param features
522
	 */
523
	private void handleRedlistStatus(CsvDemoRecord record, Taxon taxon, List<List<String>> featureCells, List<Feature> features){
524
		Set<TaxonDescription> descriptions = taxon.getDescriptions();
525

    
526
		for (TaxonDescription description : descriptions){
527
			for (DescriptionElementBase el : description.getElements()){
528
				if(el.isInstanceOf(CategoricalData.class)){
529
					CategoricalData categoricalData = CdmBase.deproxy(el, CategoricalData.class);
530
					for(State state:categoricalData.getStatesOnly()){
531
						Feature stateFeature = categoricalData.getFeature();
532
						// find matching feature and put data into according cell
533
						for(int i = 0; i < features.size(); i++) {
534
							if(features.get(i).equals(stateFeature)){
535
								List<String> cell = featureCells.get(i);
536
								cell.add(state.toString());
537
							}
538
						}
539
					}
540
				}else if(el.isInstanceOf(TextData.class)){
541
					TextData textData = CdmBase.deproxy(el, TextData.class);
542
					Feature textFeature = textData.getFeature();
543
					// find matching feature and put data into according cell
544
					for(int i = 0; i < features.size(); i++) {
545
						if(features.get(i).equals(textFeature)){
546
							List<String> cell = featureCells.get(i);
547
							String text = textData.getText(Language.GERMAN());
548
							text = text.replaceAll(System.getProperty("line.separator"), "");
549
							text = text.replaceAll("                            ", " ");
550
							cell.add(text);
551

    
552
						}
553
					}
554
				}
555
			}
556
		}
557
		record.setFeatures(featureCells);
558
	}
559

    
560
	/**
561
	 *
562
	 * @param record
563
	 * @param taxon
564
	 * @param relationFrom
565
	 * @param featureCells
566
	 * @param features
567
	 */
568
	private void handleRelatedRedlistStatus(CsvDemoRecord record, Taxon taxon, boolean relationFrom, List<List<String>> featureCells, List<Feature> features) {
569

    
570
		if (relationFrom) {
571
            handleRedlistStatus(record, taxon, featureCells, features);
572
        }
573

    
574

    
575
		Set<TaxonRelationship> taxRels;
576
		if(relationFrom){
577
			taxRels = taxon.getRelationsFromThisTaxon();
578
		}else{
579
			taxRels = taxon.getRelationsToThisTaxon();
580
		}
581
		for (TaxonRelationship taxRel:taxRels){
582
			if(taxRel.getType().equals(TaxonRelationshipType.CONGRUENT_TO())){
583
				Taxon relatedTaxon;
584
				if(relationFrom){
585
					relatedTaxon = taxRel.getToTaxon();
586
				}else{
587
					relatedTaxon = taxRel.getFromTaxon();
588
				}
589
				handleRedlistStatus(record, relatedTaxon, featureCells, features);
590
			}
591
		}
592
	}
593

    
594

    
595

    
596
	/**
597
	 *
598
	 * @param taxonNodes
599
	 */
600
	private void sortTaxonNodes(List<TaxonNode> taxonNodes) {
601
		Collections.sort(taxonNodes, new Comparator<TaxonNode>() {
602

    
603
			@Override
604
			public int compare(TaxonNode tn1, TaxonNode tn2) {
605
				Taxon taxon1 = tn1.getTaxon();
606
				Taxon taxon2 = tn2.getTaxon();
607
				if(taxon1 != null && taxon2 != null){
608
					return taxon1.getTitleCache().compareTo(taxon2.getTitleCache());
609
				}
610
				else{
611
					return 0;
612
				}
613
			}
614
		});
615
	}
616

    
617
	@Override
618
	protected boolean doCheck(CsvDemoExportState state) {
619
		boolean result = true;
620
		logger.warn("No check implemented for " + this.ioName);
621
		return result;
622
	}
623

    
624
	@Override
625
	protected boolean isIgnore(CsvDemoExportState state) {
626
		return ! state.getConfig().isDoTaxa();
627
	}
628

    
629

    
630
    /**
631
     * {@inheritDoc}
632
     */
633
    @Override
634
    public byte[] getByteArray() {
635
        if (this.exportStream != null){
636
            return this.exportStream.toByteArray();
637
        }
638
        return null;
639
    }
640

    
641
}
(2-2/10)