2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.io
.csv
.redlist
.demo
;
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
;
20 import java
.util
.UUID
;
22 import org
.apache
.log4j
.Logger
;
23 import org
.springframework
.stereotype
.Component
;
24 import org
.springframework
.transaction
.TransactionStatus
;
26 import eu
.etaxonomy
.cdm
.io
.csv
.redlist
.out
.CsvTaxExportStateRedlist
;
27 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
28 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
29 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
30 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipTermBase
;
31 import eu
.etaxonomy
.cdm
.model
.description
.CategoricalData
;
32 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
33 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
34 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
35 import eu
.etaxonomy
.cdm
.model
.description
.State
;
36 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
37 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
38 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
39 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
40 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
41 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
42 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
43 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
44 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
45 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
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
;
59 public class CsvDemoExport
extends CsvDemoBase
{
60 private static final Logger logger
= Logger
.getLogger(CsvDemoExport
.class);
62 private static final String ROW_TYPE
= "http://rs.tdwg.org/dwc/terms/Taxon";
63 private static final String fileName
= "RedlistCoreTax.csv";
65 public CsvDemoExport() {
67 this.ioName
= this.getClass().getSimpleName();
71 /** Retrieves data from a CDM DB and serializes them CDM to CSV.
72 * Starts with root taxa and traverses the classification to retrieve
73 * children taxa, synonyms, relationships, descriptive data, red list
75 * Taxa that are not part of the classification are not found.
82 protected void doInvoke(CsvDemoExportState state
){
83 CsvDemoExportConfigurator config
= state
.getConfig();
84 TransactionStatus txStatus
= startTransaction(true);
85 List
<NamedArea
> selectedAreas
= config
.getNamedAreas();
86 Set
<Classification
> classificationSet
= assembleClassificationSet(config
);
88 PrintWriter writer
= null;
89 ByteArrayOutputStream byteArrayOutputStream
;
91 byteArrayOutputStream
= config
.getByteArrayOutputStream();
92 writer
= new PrintWriter(byteArrayOutputStream
);
95 List
<TaxonNode
> taxonNodes
= handleGeographicalFilter(state
, classificationSet
);
98 sortTaxonNodes(taxonNodes
);
100 for (TaxonNode node
: taxonNodes
){
101 Taxon taxon
= CdmBase
.deproxy(node
.getTaxon(), Taxon
.class);
102 CsvDemoRecord record
= assembleRecord(state
);
103 NonViralName
<?
> name
= CdmBase
.deproxy(taxon
.getName(), NonViralName
.class);
104 Classification classification
= node
.getClassification();
105 config
.setClassificationTitleCache(classification
.getTitleCache());
106 if (! this.recordExists(taxon
)){
107 handleTaxonBase(record
, taxon
, name
, taxon
, classification
, null, false, false, config
, node
);
108 record
.write(writer
);
109 this.addExistingRecord(taxon
);
112 handleMisapplication(taxon
, writer
, classification
, record
, config
, node
);
115 } catch (ClassCastException e
) {
120 this.clearExistingRecordIds();
122 commitTransaction(txStatus
);
129 //TODO: Exception handling
135 protected Set
<Classification
> assembleClassificationSet(CsvDemoExportConfigurator config
){
137 Set
<UUID
> classificationUuidSet
= config
.getClassificationUuids();
138 List
<Classification
> classificationList
= getClassificationService().find(classificationUuidSet
);
139 Set
<Classification
> classificationSet
= new HashSet
<Classification
>();
140 classificationSet
.addAll(classificationList
);
141 return classificationSet
;
146 //TODO: Exception handling
152 private CsvDemoRecord
assembleRecord(CsvDemoExportState state
) {
154 CsvDemoExportConfigurator config
= state
.getConfig();
155 CsvDemoMetaDataRecord metaRecord
= new CsvDemoMetaDataRecord(true, fileName
, ROW_TYPE
);
156 state
.addMetaRecord(metaRecord
);
157 CsvDemoRecord record
= new CsvDemoRecord(metaRecord
, config
);
164 * Takes positive List of areas and iterates over a given classification
165 * and their {@link Taxon} to return all {@link Taxon} with the desired
166 * geographical attribute.
170 * If selectedAreas is null all {@link TaxonNode}s of the given {@link Classification} will be returned.
172 * @param selectedAreas
173 * @param classificationSet
176 protected List
<TaxonNode
> handleGeographicalFilter(CsvDemoExportState state
,
177 Set
<Classification
> classificationSet
) {
178 List
<TaxonNode
> filteredNodes
= new ArrayList
<TaxonNode
>();
179 List
<TaxonNode
> allNodes
= getAllNodes(classificationSet
);
180 //Geographical filter
181 if(state
.getConfig().isDoGeographicalFilter()){
182 List
<NamedArea
> selectedAreas
= state
.getConfig().getNamedAreas();
183 logger
.info(selectedAreas
.size());
184 if(selectedAreas
!= null && !selectedAreas
.isEmpty() && selectedAreas
.size() < 16){
185 // if(selectedAreas.size() == 16){
186 // //Germany TDWG Level 3
187 // String germany="uu7b7c2db5-aa44-4302-bdec-6556fd74b0b9id";
188 // selectedAreas.add((NamedArea) getTermService().find(UUID.fromString(germany)));
190 for (TaxonNode node
: allNodes
){
191 Taxon taxon
= CdmBase
.deproxy(node
.getTaxon(), Taxon
.class);
192 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
193 for (TaxonDescription description
: descriptions
){
194 for (DescriptionElementBase el
: description
.getElements()){
195 if (el
.isInstanceOf(Distribution
.class) ){
196 Distribution distribution
= CdmBase
.deproxy(el
, Distribution
.class);
197 NamedArea area
= distribution
.getArea();
198 for(NamedArea selectedArea
:selectedAreas
){
199 if(selectedArea
.getUuid().equals(area
.getUuid())){
200 filteredNodes
.add(node
);
208 filteredNodes
= allNodes
;
211 return filteredNodes
;
215 * handles misapplied {@link Taxon}
218 * @param classification
222 private void handleMisapplication(Taxon taxon
, PrintWriter writer
, Classification classification
, CsvDemoRecord record
, CsvDemoExportConfigurator config
, TaxonNode node
) {
223 Set
<Taxon
> misappliedNames
= taxon
.getMisappliedNames();
224 for (Taxon misappliedName
: misappliedNames
){
225 // CsvTaxRecordRedlist record = new CsvTaxRecordRedlist(metaRecord, config);
226 TaxonRelationshipType relType
= TaxonRelationshipType
.MISAPPLIED_NAME_FOR();
227 NonViralName
<?
> name
= CdmBase
.deproxy(misappliedName
.getName(), NonViralName
.class);
229 if (! this.recordExists(misappliedName
)){
230 handleTaxonBase(record
, misappliedName
, name
, taxon
, classification
, relType
, false, false, config
, node
);
231 record
.write(writer
);
232 this.addExistingRecord(misappliedName
);
238 * handles the information record for the actual {@link Taxon} including {@link Classification classification}, Taxon Name, Taxon ID,
239 * Taxon Status, Synonyms, {@link Feature features} data
240 * @param record the concrete information record
241 * @param taxonBase {@link Taxon}
243 * @param acceptedTaxon
252 private void handleTaxonBase(CsvDemoRecord record
,TaxonBase
<?
> taxonBase
,
253 NonViralName
<?
> name
, Taxon acceptedTaxon
, Classification classification
,
254 RelationshipTermBase
<?
> relType
, boolean isProParte
, boolean isPartial
,
255 CsvDemoExportConfigurator config
, TaxonNode node
) {
257 Taxon taxon
= (Taxon
) taxonBase
;
258 List
<Feature
> features
= config
.getFeatures();
259 record
.setHeadLinePrinted(config
.isHasHeaderLines());
260 if(config
.isRedlistFeatures()){
261 if(features
!= null){
262 record
.setPrintFeatures(features
);
265 config
.setHasHeaderLines(false);
267 if(config
.isClassification()){
268 record
.setDatasetName(classification
.getTitleCache());
270 if(config
.isTaxonName()){
271 record
.setScientificName(name
.getTitleCache());
273 if(config
.isTaxonNameID()){
274 record
.setScientificNameId(name
.getUuid().toString());
276 if(config
.isAuthor()){
277 String authorshipCache
= name
.getAuthorshipCache();
278 if(authorshipCache
== null){
279 authorshipCache
= "";
281 record
.setAuthorName(authorshipCache
);
285 if(taxon
.getName().getRank() == null){
288 rank
= taxon
.getName().getRank().toString();
290 record
.setRank(rank
);
292 if(config
.isTaxonStatus()){
293 handleTaxonomicStatus(record
, name
, relType
, isProParte
, isPartial
);
295 if(config
.isAcceptedName()){
296 //TODO write routine for accepted Name
298 if(config
.isTaxonConceptID()){
299 UUID taxonUuid
= taxonBase
.getUuid();
300 if(taxonUuid
== null){
301 taxonUuid
= UUID
.fromString("");
303 record
.setTaxonConceptID(taxonUuid
.toString());
305 if(config
.isParentID()){
307 if(node
.getParent().getTaxon() == null){
310 parentUUID
= node
.getParent().getTaxon().getUuid().toString();
312 record
.setParentUUID(parentUUID
);
314 if(config
.isLastChange()){
316 if(taxon
.getUpdated() == null){
319 lastChange
= taxon
.getUpdated().toString();
321 record
.setLastChange(lastChange
);
323 if(config
.isSynonyms()){
324 handleSynonyms(record
,taxon
);
326 if(config
.isDistributions()){
327 handleDiscriptionData(record
, taxon
);
329 if(config
.isRedlistFeatures()){
330 if(features
!= null) {
332 List
<List
<String
>> featureCells
= new ArrayList
<List
<String
>>(features
.size());
333 for(int i
= 0; i
< features
.size(); i
++) {
334 featureCells
.add(new ArrayList
<String
>());
336 handleRelatedRedlistStatus(record
, taxon
, false, featureCells
, features
);
337 handleRelatedRedlistStatus(record
, taxon
, true, featureCells
, features
);
342 if(config
.isExternalID()){
343 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
344 for(IdentifiableSource source
:sources
){
345 Reference
<?
> citation
= source
.getCitation();
347 * TODO: handle this more generic.
351 if(citation
.getId() == 22){
352 String idInSource
= source
.getIdInSource();
353 if(idInSource
== null){
356 record
.setExternalID(idInSource
);
370 private void handleTaxonomicStatus(
371 CsvDemoRecord record
,
372 NonViralName
<?
> name
,
373 RelationshipTermBase
<?
> type
,
377 record
.setTaxonomicStatus(name
.getNomenclaturalCode().acceptedTaxonStatusLabel());
379 String status
= name
.getNomenclaturalCode().synonymStatusLabel();
380 if (type
.equals(SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF())){
381 status
= "heterotypicSynonym";
382 }else if(type
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
383 status
= "homotypicSynonym";
384 }else if(type
.equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())){
385 status
= "misapplied";
388 status
= "proParteSynonym";
389 }else if (isPartial
){
390 String message
= "Partial synonym is not part of the gbif toxonomic status vocabulary";
391 logger
.warn(message
);
392 status
= "partialSynonym";
395 record
.setTaxonomicStatus(status
);
401 * This method concatenates several synonyms in a list.
406 private void handleSynonyms(CsvDemoRecord record
, Taxon taxon
) {
408 Set
<SynonymRelationship
> synRels
= taxon
.getSynonymRelations();
409 ArrayList
<String
> synonyms
= new ArrayList
<String
>();
410 for (SynonymRelationship synRel
:synRels
){
411 Synonym synonym
= synRel
.getSynonym();
412 SynonymRelationshipType type
= synRel
.getType();
413 if (type
== null){ // should not happen
414 type
= SynonymRelationshipType
.SYNONYM_OF();
416 NonViralName
<?
> name
= CdmBase
.deproxy(synonym
.getName(), NonViralName
.class);
417 synonyms
.add(name
.getTitleCache());
419 record
.setSynonyms(synonyms
);
427 private void handleDiscriptionData(CsvDemoRecord record
, Taxon taxon
) {
429 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
430 ArrayList
<String
> distributions
= new ArrayList
<String
>();
431 for (TaxonDescription description
: descriptions
){
432 for (DescriptionElementBase el
: description
.getElements()){
433 if (el
.isInstanceOf(Distribution
.class) ){
434 Distribution distribution
= CdmBase
.deproxy(el
, Distribution
.class);
435 NamedArea area
= distribution
.getArea();
436 distributions
.add(area
.getTitleCache());
441 record
.setCountryCode(distributions
);
447 * @param featureCells
450 private void handleRedlistStatus(CsvDemoRecord record
, Taxon taxon
, List
<List
<String
>> featureCells
, List
<Feature
> features
){
451 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
453 for (TaxonDescription description
: descriptions
){
454 for (DescriptionElementBase el
: description
.getElements()){
455 if(el
.isInstanceOf(CategoricalData
.class)){
456 CategoricalData categoricalData
= CdmBase
.deproxy(el
, CategoricalData
.class);
457 for(State state
:categoricalData
.getStatesOnly()){
458 Feature stateFeature
= categoricalData
.getFeature();
459 // find matching feature and put data into according cell
460 for(int i
= 0; i
< features
.size(); i
++) {
461 if(features
.get(i
).equals(stateFeature
)){
462 List
<String
> cell
= featureCells
.get(i
);
463 cell
.add(state
.toString());
467 }else if(el
.isInstanceOf(TextData
.class)){
468 TextData textData
= CdmBase
.deproxy(el
, TextData
.class);
469 Feature textFeature
= textData
.getFeature();
470 // find matching feature and put data into according cell
471 for(int i
= 0; i
< features
.size(); i
++) {
472 if(features
.get(i
).equals(textFeature
)){
473 List
<String
> cell
= featureCells
.get(i
);
474 String text
= textData
.getText(Language
.GERMAN());
475 text
= text
.replaceAll(System
.getProperty("line.separator"), "");
476 text
= text
.replaceAll(" ", " ");
484 record
.setFeatures(featureCells
);
491 * @param relationFrom
492 * @param featureCells
495 private void handleRelatedRedlistStatus(CsvDemoRecord record
, Taxon taxon
, boolean relationFrom
, List
<List
<String
>> featureCells
, List
<Feature
> features
) {
497 if (relationFrom
)handleRedlistStatus(record
, taxon
, featureCells
, features
);
500 Set
<TaxonRelationship
> taxRels
;
502 taxRels
= taxon
.getRelationsFromThisTaxon();
504 taxRels
= taxon
.getRelationsToThisTaxon();
506 for (TaxonRelationship taxRel
:taxRels
){
507 if(taxRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO())){
510 relatedTaxon
= taxRel
.getToTaxon();
512 relatedTaxon
= taxRel
.getFromTaxon();
514 handleRedlistStatus(record
, relatedTaxon
, featureCells
, features
);
525 private void sortTaxonNodes(List
<TaxonNode
> taxonNodes
) {
526 Collections
.sort(taxonNodes
, new Comparator
<TaxonNode
>() {
529 public int compare(TaxonNode tn1
, TaxonNode tn2
) {
530 Taxon taxon1
= tn1
.getTaxon();
531 Taxon taxon2
= tn2
.getTaxon();
532 if(taxon1
!= null && taxon2
!= null){
533 return taxon1
.getTitleCache().compareTo(taxon2
.getTitleCache());
543 protected boolean doCheck(CsvDemoExportState state
) {
544 boolean result
= true;
545 logger
.warn("No check implemented for " + this.ioName
);
550 protected boolean isIgnore(CsvDemoExportState state
) {
551 return ! state
.getConfig().isDoTaxa();