cfc0a33548a33d455b470bacf40307d353f44e28
[cdmlib.git] / cdmlib-io / src / main / java / eu / etaxonomy / cdm / io / wfo / out / WfoClassificationExport.java
1 /**
2 * Copyright (C) 2017 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 package eu.etaxonomy.cdm.io.wfo.out;
10
11 import java.io.File;
12 import java.util.ArrayList;
13 import java.util.HashSet;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18
19 import org.apache.commons.lang3.StringUtils;
20 import org.joda.time.DateTime;
21 import org.joda.time.DateTimeFieldType;
22 import org.joda.time.Partial;
23 import org.joda.time.format.DateTimeFormatter;
24 import org.joda.time.format.DateTimeFormatterBuilder;
25 import org.springframework.stereotype.Component;
26
27 import eu.etaxonomy.cdm.common.CdmUtils;
28 import eu.etaxonomy.cdm.common.URI;
29 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
30 import eu.etaxonomy.cdm.filter.TaxonNodeFilter;
31 import eu.etaxonomy.cdm.format.reference.NomenclaturalSourceFormatter;
32 import eu.etaxonomy.cdm.io.common.CdmExportBase;
33 import eu.etaxonomy.cdm.io.common.ExportResult.ExportResultState;
34 import eu.etaxonomy.cdm.io.common.TaxonNodeOutStreamPartitioner;
35 import eu.etaxonomy.cdm.io.common.XmlExportState;
36 import eu.etaxonomy.cdm.io.common.mapping.out.IExportTransformer;
37 import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
38 import eu.etaxonomy.cdm.model.common.Annotation;
39 import eu.etaxonomy.cdm.model.common.AnnotationType;
40 import eu.etaxonomy.cdm.model.common.CdmBase;
41 import eu.etaxonomy.cdm.model.common.ICdmBase;
42 import eu.etaxonomy.cdm.model.common.IIdentifiableEntity;
43 import eu.etaxonomy.cdm.model.common.Identifier;
44 import eu.etaxonomy.cdm.model.common.Language;
45 import eu.etaxonomy.cdm.model.common.LanguageString;
46 import eu.etaxonomy.cdm.model.common.TimePeriod;
47 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
48 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
49 import eu.etaxonomy.cdm.model.name.Rank;
50 import eu.etaxonomy.cdm.model.name.TaxonName;
51 import eu.etaxonomy.cdm.model.reference.Reference;
52 import eu.etaxonomy.cdm.model.taxon.Classification;
53 import eu.etaxonomy.cdm.model.taxon.Synonym;
54 import eu.etaxonomy.cdm.model.taxon.Taxon;
55 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
56 import eu.etaxonomy.cdm.model.term.IdentifierType;
57
58 /**
59 * Classification or taxon tree exporter into WFO DwC-A format.
60 *
61 * @author a.mueller
62 * @since 2023-12-08
63 */
64 @Component
65 public class WfoClassificationExport
66 extends CdmExportBase<WfoExportConfigurator,WfoExportState,IExportTransformer,File>{
67
68 private static final long serialVersionUID = -4560488499411723333L;
69
70 public WfoClassificationExport() {
71 this.ioName = this.getClass().getSimpleName();
72 }
73
74 @Override
75 public long countSteps(WfoExportState state) {
76 TaxonNodeFilter filter = state.getConfig().getTaxonNodeFilter();
77 return getTaxonNodeService().count(filter);
78 }
79
80 @Override
81 protected void doInvoke(WfoExportState state) {
82
83 try {
84 IProgressMonitor monitor = state.getConfig().getProgressMonitor();
85 WfoExportConfigurator config = state.getConfig();
86
87 //set root node
88 if (config.getTaxonNodeFilter().hasClassificationFilter()) {
89 Classification classification = getClassificationService()
90 .load(config.getTaxonNodeFilter().getClassificationFilter().get(0).getUuid());
91 state.setRootId(classification.getRootNode().getUuid());
92 } else if (config.getTaxonNodeFilter().hasSubtreeFilter()) {
93 state.setRootId(config.getTaxonNodeFilter().getSubtreeFilter().get(0).getUuid());
94 }
95
96 @SuppressWarnings({ "unchecked", "rawtypes" })
97 TaxonNodeOutStreamPartitioner<XmlExportState> partitioner = TaxonNodeOutStreamPartitioner.NewInstance(this,
98 state, state.getConfig().getTaxonNodeFilter(), 100, monitor, null);
99
100 // handleMetaData(state); //FIXME metadata;
101 monitor.subTask("Start partitioning");
102
103 TaxonNode node = partitioner.next();
104 while (node != null) {
105 handleTaxonNode(state, node);
106 node = partitioner.next();
107 }
108
109 state.getProcessor().createFinalResult(state);
110 } catch (Exception e) {
111 state.getResult().addException(e,
112 "An unexpected error occurred in main method doInvoke() " + e.getMessage());
113 e.printStackTrace();
114 }
115 }
116
117 private void handleTaxonNode(WfoExportState state, TaxonNode taxonNode) {
118
119 if (taxonNode == null) {
120 // TODO 5 taxon node not found
121 String message = "TaxonNode for given taxon node UUID not found.";
122 state.getResult().addError(message);
123 } else {
124 try {
125 boolean exclude = filterTaxon(state, taxonNode);
126
127 //handle taxon
128 String parentWfoId = getParentWfoId(state, taxonNode);
129 if (taxonNode.hasTaxon()) {
130 if (exclude) {
131 state.putTaxonNodeWfoId(taxonNode, parentWfoId); //always use parent instead
132 } else {
133 String wfoId = handleTaxon(state, taxonNode, parentWfoId);
134 state.putTaxonNodeWfoId(taxonNode, wfoId);
135 }
136 state.putTaxonNodeWfoId(taxonNode, parentWfoId);
137 }
138 } catch (Exception e) {
139 state.getResult().addException(e, "An unexpected error occurred when handling taxonNode "
140 + taxonNode.getUuid() + ": " + e.getMessage() + e.getStackTrace());
141 }
142 }
143 }
144
145 private String getParentWfoId(WfoExportState state, TaxonNode taxonNode) {
146 TaxonNode parentNode = taxonNode.getParent();
147 if (parentNode == null) {
148 return null;
149 }
150 String wfoId = state.getTaxonNodeWfoId(parentNode);
151 if (wfoId != null) {
152 return wfoId;
153 }else {
154 wfoId = parentNode.getTaxon() == null ? null
155 : getWfoId(state, parentNode.getTaxon().getName(), true);
156 if (wfoId != null) {
157 state.putTaxonNodeWfoId(parentNode, wfoId);
158 state.putTaxonWfoId(parentNode.getTaxon(), wfoId);
159 state.putNameWfoId(parentNode.getTaxon().getName(), wfoId);
160 }
161
162 return wfoId;
163 }
164 }
165
166 private Set<Rank> allowedRanks = new HashSet<>();
167 private boolean filterTaxon(WfoExportState state, TaxonNode taxonNode) {
168 Taxon taxon = taxonNode.getTaxon();
169 if (taxon == null) {
170 return true;
171 }
172 TaxonName taxonName = taxon.getName();
173 if (taxonName == null) {
174 return true;
175 }else if (taxonName.isHybridFormula()) {
176 return true;
177 }else {
178 String wfoId = getWfoId(state, taxonName, false);
179 if (wfoId == null) {
180 return true;
181 }
182 }
183
184 Rank rank = taxonName.getRank();
185 if (rank == null) {
186 return true;
187 }else {
188 if (allowedRanks.isEmpty()) {
189 allowedRanks.add(Rank.FAMILY());
190 allowedRanks.add(Rank.SUBFAMILY());
191 allowedRanks.add(Rank.TRIBE());
192 allowedRanks.add(Rank.SUBTRIBE());
193 allowedRanks.add(Rank.GENUS());
194 allowedRanks.add(Rank.SUBGENUS());
195 allowedRanks.add(Rank.SECTION_BOTANY());
196 allowedRanks.add(Rank.SPECIES());
197 allowedRanks.add(Rank.SUBSPECIES());
198 allowedRanks.add(Rank.VARIETY());
199 allowedRanks.add(Rank.SUBVARIETY());
200 allowedRanks.add(Rank.FORM());
201 allowedRanks.add(Rank.SUBFORM());
202 allowedRanks.add(Rank.INFRASPECIFICTAXON());
203 }
204 if (!allowedRanks.contains(rank)) {
205 //TODO 3 warn if this happens as such names should not have a wfo-id neither
206 return true;
207 }
208 }
209 return false;
210 }
211
212 /**
213 * @return the WFO-ID of the taxon
214 */
215 private String handleTaxon(WfoExportState state, TaxonNode taxonNode, String parentWfoId) {
216 //check null
217 if (taxonNode == null) {
218 state.getResult().addError("The taxonNode was null.", "handleTaxon");
219 state.getResult().setState(ExportResultState.INCOMPLETE_WITH_ERROR);
220 return null;
221 }
222 //check no taxon
223 if (taxonNode.getTaxon() == null) {
224 state.getResult().addError("There was a taxon node without a taxon: " + taxonNode.getUuid(),
225 "handleTaxon");
226 state.getResult().setState(ExportResultState.INCOMPLETE_WITH_ERROR);
227 return null;
228 }
229
230 //deproxy, just in case
231 Taxon taxon = CdmBase.deproxy(taxonNode.getTaxon());
232 String wfoId = null;
233
234 //handle taxon
235 try {
236
237 //classification csvLine
238 WfoExportTable table = WfoExportTable.CLASSIFICATION;
239 String[] csvLine = new String[table.getSize()];
240
241 //accepted name
242 TaxonName name = taxon.getName();
243 wfoId = handleName(state, table, csvLine, name);
244
245 //... parentNameUsageID
246 csvLine[table.getIndex(WfoExportTable.TAX_PARENT_ID)] = parentWfoId;
247
248 //... higher taxa
249 csvLine[table.getIndex(WfoExportTable.TAX_SUBFAMILY)] = null;
250 csvLine[table.getIndex(WfoExportTable.TAX_TRIBE)] = null;
251 csvLine[table.getIndex(WfoExportTable.TAX_SUBTRIBE)] = null;
252 csvLine[table.getIndex(WfoExportTable.TAX_SUBGENUS)] = name.isInfraGeneric()? name.getInfraGenericEpithet() : null ;
253
254 //... tax status, TODO 2 are there other status for accepted or other reasons for being ambiguous
255 String taxonStatus = taxon.isDoubtful()? "ambiguous" : "Accepted";
256 csvLine[table.getIndex(WfoExportTable.TAX_STATUS)] = taxonStatus;
257
258 //secundum reference
259 csvLine[table.getIndex(WfoExportTable.NAME_ACCORDING_TO_ID)] = getId(state, taxon.getSec());
260 if (taxon.getSec() != null
261 && (!state.getReferenceStore().contains((taxon.getSec().getUuid())))) {
262 handleReference(state, taxon.getSec());
263 }
264
265 //TODO 2 remarks, what exactly
266 csvLine[table.getIndex(WfoExportTable.TAXON_REMARKS)] = getRemarks(name);
267
268 handleSynonyms(state, taxon);
269
270 //TODO 2 taxon provisional, still an open issue?
271 // csvLine[table.getIndex(WfoExportTable.TAX_PROVISIONAL)] = taxonNode.isDoubtful() ? "1" : "0";
272
273 //TODO 1 taxon only published
274
275 //process taxon line
276 state.getProcessor().put(table, taxon, csvLine);
277
278 } catch (Exception e) {
279 e.printStackTrace();
280 state.getResult().addException(e,
281 "An unexpected problem occurred when trying to export taxon with id " + taxon.getId() + " " + taxon.getTitleCache());
282 state.getResult().setState(ExportResultState.INCOMPLETE_WITH_ERROR);
283 return null;
284 }
285
286 return wfoId;
287 }
288
289 private void handleSynonyms(WfoExportState state, Taxon taxon) {
290 //homotypic group / synonyms
291 HomotypicalGroup homotypicGroup = taxon.getHomotypicGroup();
292 handleHomotypicalGroup(state, homotypicGroup, taxon);
293 for (Synonym syn : taxon.getSynonymsInGroup(homotypicGroup)) {
294 handleSynonym(state, syn);
295 }
296
297 List<HomotypicalGroup> heterotypicHomotypicGroups = taxon.getHeterotypicSynonymyGroups();
298 for (HomotypicalGroup group: heterotypicHomotypicGroups){
299 handleHomotypicalGroup(state, group, taxon);
300 for (Synonym syn : taxon.getSynonymsInGroup(group)) {
301 handleSynonym(state, syn);
302 }
303 }
304 }
305
306 private boolean isUrl(String url) {
307 try {
308 if (url.startsWith("http")) {
309 URI.fromString(url);
310 return true;
311 }
312 } catch (Exception e) {
313 //exception should return false
314 }
315 return false;
316 }
317
318 private String toIsoDate(TimePeriod mediaCreated) {
319 //TODO 2 date, what if end or freetext exist?
320 Partial partial = mediaCreated.getStart();
321 if (partial == null || !partial.isSupported(DateTimeFieldType.year())
322 || !partial.isSupported(DateTimeFieldType.monthOfYear()) && partial.isSupported(DateTimeFieldType.dayOfMonth())) {
323 //TODO 2 date, log warning, also if mediaCreated.getEnd() != null or so
324 return null;
325 } else {
326 DateTimeFormatter formatter = new DateTimeFormatterBuilder()
327 .appendYear(4, 4).appendLiteral('-')
328 .appendMonthOfYear(2).appendLiteral('-')
329 .appendDayOfMonth(2)
330 .toFormatter();
331 return partial.toString(formatter);
332 }
333 }
334
335 /**
336 * transforms the given date to an iso date
337 */
338 protected String toIsoDate(DateTime dateTime) {
339 if (dateTime == null) {
340 return null;
341 }
342 DateTimeFormatter formatter = new DateTimeFormatterBuilder()
343 .appendYear(4, 4).appendLiteral('-')
344 .appendMonthOfYear(2).appendLiteral('-')
345 .appendDayOfMonth(2)
346 .toFormatter();
347 return formatter.print(dateTime);
348 }
349
350 private String getRemarks(AnnotatableEntity entity) {
351 String remarks = null;
352 for (Annotation a : entity.getAnnotations()) {
353 //TODO 3 handle other annotation types
354 if (AnnotationType.EDITORIAL().equals(a.getAnnotationType())
355 && CdmUtils.isNotBlank(a.getText())){
356 remarks = CdmUtils.concat(";", remarks, a.getText());
357 }
358 }
359 return remarks;
360 }
361
362 // private void handleMetaData(ColDpExportState state) {
363 // ColDpExportTable table = ColDpExportTable.METADATA;
364 // String[] csvLine = new String[table.getSize()];
365 //// csvLine[table.getIndex(CdmLightExportTable.INSTANCE_ID)] = state.getConfig().getInctanceId();
366 //// csvLine[table.getIndex(CdmLightExportTable.INSTANCE_NAME)] = state.getConfig().getInstanceName();
367 // csvLine[table.getIndex(ColDpExportTable.DATASET_BASE_URL)] = state.getConfig().getBase_url();
368 // csvLine[table.getIndex(ColDpExportTable.DATASET_CONTRIBUTOR)] = state.getConfig().getContributor();
369 // csvLine[table.getIndex(ColDpExportTable.DATASET_CREATOR)] = state.getConfig().getCreator();
370 // csvLine[table.getIndex(ColDpExportTable.DATASET_DESCRIPTION)] = state.getConfig().getDescription();
371 // csvLine[table.getIndex(ColDpExportTable.DATASET_DOWNLOAD_LINK)] = state.getConfig().getDataset_download_link();
372 // csvLine[table.getIndex(ColDpExportTable.DATASET_KEYWORDS)] = state.getConfig().getKeywords();
373 // csvLine[table.getIndex(ColDpExportTable.DATASET_LANDINGPAGE)] = state.getConfig().getDataSet_landing_page();
374 //
375 // csvLine[table.getIndex(ColDpExportTable.DATASET_LANGUAGE)] = state.getConfig().getLanguage() != null? state.getConfig().getLanguage().getLabel(): null;
376 // csvLine[table.getIndex(ColDpExportTable.DATASET_LICENCE)] = state.getConfig().getLicence();
377 // csvLine[table.getIndex(ColDpExportTable.DATASET_LOCATION)] = state.getConfig().getLocation();
378 // csvLine[table.getIndex(ColDpExportTable.DATASET_RECOMMENDED_CITATTION)] = state.getConfig().getRecommended_citation();
379 // csvLine[table.getIndex(ColDpExportTable.DATASET_TITLE)] = state.getConfig().getTitle();
380 // state.getProcessor().put(table, "", csvLine);
381 // }
382
383 private String createMultilanguageString(Map<Language, LanguageString> multilanguageText) {
384 String text = "";
385 int index = multilanguageText.size();
386 for (LanguageString langString : multilanguageText.values()) {
387 text += langString.getText();
388 if (index > 1) {
389 text += "; ";
390 }
391 index--;
392 }
393 return text;
394 }
395
396 private String createAnnotationsString(Set<Annotation> annotations) {
397 StringBuffer strBuff = new StringBuffer();
398
399 for (Annotation ann : annotations) {
400 if (ann.getAnnotationType() == null || !ann.getAnnotationType().equals(AnnotationType.TECHNICAL())) {
401 strBuff.append(ann.getText());
402 strBuff.append("; ");
403 }
404 }
405
406 if (strBuff.length() > 2) {
407 return strBuff.substring(0, strBuff.length() - 2);
408 } else {
409 return null;
410 }
411 }
412
413 private void handleSource(WfoExportState state, DescriptionElementBase element,
414 WfoExportTable factsTable) {
415 // ColDpExportTable table = ColDpExportTable.FACT_SOURCES;
416 // try {
417 // Set<DescriptionElementSource> sources = element.getSources();
418 //
419 // for (DescriptionElementSource source : sources) {
420 // if (!(source.getType().equals(OriginalSourceType.Import)
421 // && state.getConfig().isExcludeImportSources())) {
422 // String[] csvLine = new String[table.getSize()];
423 // Reference ref = source.getCitation();
424 // if ((ref == null) && (source.getNameUsedInSource() == null)) {
425 // continue;
426 // }
427 // if (ref != null) {
428 // if (!state.getReferenceStore().contains(ref.getUuid())) {
429 // handleReference(state, ref);
430 //
431 // }
432 // csvLine[table.getIndex(ColDpExportTable.REFERENCE_FK)] = getId(state, ref);
433 // }
434 // csvLine[table.getIndex(ColDpExportTable.FACT_FK)] = getId(state, element);
435 //
436 // csvLine[table.getIndex(ColDpExportTable.NAME_IN_SOURCE_FK)] = getId(state,
437 // source.getNameUsedInSource());
438 // csvLine[table.getIndex(ColDpExportTable.FACT_TYPE)] = factsTable.getTableName();
439 // if (StringUtils.isBlank(csvLine[table.getIndex(ColDpExportTable.REFERENCE_FK)])
440 // && StringUtils.isBlank(csvLine[table.getIndex(ColDpExportTable.NAME_IN_SOURCE_FK)])) {
441 // continue;
442 // }
443 // state.getProcessor().put(table, source, csvLine);
444 // }
445 // }
446 // } catch (Exception e) {
447 // state.getResult().addException(e, "An unexpected error occurred when handling single source "
448 // + cdmBaseStr(element) + ": " + e.getMessage());
449 // }
450 }
451
452 private String getTitleCache(IIdentifiableEntity identEntity) {
453 if (identEntity == null) {
454 return "";
455 }
456 // TODO 3 titleCache refresh?
457 return identEntity.getTitleCache();
458 }
459
460 private String getId(WfoExportState state, ICdmBase cdmBase) {
461 if (cdmBase == null) {
462 return "";
463 }
464 // TODO 4 id type, make configurable
465 return cdmBase.getUuid().toString();
466 }
467
468 private void handleSynonym(WfoExportState state, Synonym synonym) {
469 try {
470 if (isUnpublished(state.getConfig(), synonym)) {
471 return;
472 }
473
474 WfoExportTable table = WfoExportTable.CLASSIFICATION;
475 String[] csvLine = new String[table.getSize()];
476
477 TaxonName name = synonym.getName();
478 handleName(state, table, csvLine, name);
479
480 //accepted name id
481 if (synonym.getAcceptedTaxon()!= null && synonym.getAcceptedTaxon().getName() != null) {
482 TaxonName acceptedName = synonym.getAcceptedTaxon().getName();
483 String acceptedWfoId = getWfoId(state, acceptedName, false);
484 if (acceptedWfoId == null) {
485 String message = "WFO-ID for accepted name is missing. This should not happen. Synonym: " + name.getTitleCache() + "; Accepted name: " + acceptedName.getTitleCache();
486 state.getResult().addError(message, "handleName");
487 state.getResult().setState(ExportResultState.INCOMPLETE_WITH_ERROR);
488 }
489 csvLine[table.getIndex(WfoExportTable.TAX_ACCEPTED_NAME_ID)] = acceptedWfoId;
490 }
491
492 state.getProcessor().put(table, synonym, csvLine);
493 } catch (Exception e) {
494 state.getResult().addException(e, "An unexpected error occurred when handling synonym "
495 + cdmBaseStr(synonym) + ": " + e.getMessage());
496 }
497 }
498
499 private String handleName(WfoExportState state, WfoExportTable table, String[] csvLine,
500 TaxonName name) {
501
502 name = CdmBase.deproxy(name);
503 if (name == null || state.getNameStore().containsKey(name.getId())) {
504 if (name == null) {
505 state.getResult().addError("No name was given for taxon.", "handleName");
506 state.getResult().setState(ExportResultState.INCOMPLETE_WITH_ERROR);
507 }
508 return null;
509 }
510 String wfoId = null;
511 try {
512
513 Rank rank = name.getRank();
514 state.getNameStore().put(name.getId(), name.getUuid());
515
516 //taxon ID
517 wfoId = getWfoId(state, name, true);
518 if (isBlank(wfoId)) {
519 String message = "No WFO-ID given for taxon " + name.getTitleCache() + ". Taxon ignored";
520 state.getResult().addError(message);
521 state.getResult().setState(ExportResultState.INCOMPLETE_WITH_ERROR);
522 return null;
523 }else {
524 csvLine[table.getIndex(WfoExportTable.TAXON_ID)] = wfoId;
525 }
526
527 //TODO 9 add IPNI ID if exists, scientific name ID
528 csvLine[table.getIndex(WfoExportTable.NAME_SCIENTIFIC_NAME_ID)] = null;
529
530 //localID
531 csvLine[table.getIndex(WfoExportTable.NAME_LOCAL_ID)] = getId(state, name);
532
533 //scientificName
534 if (name.isProtectedTitleCache()) {
535 //TODO 7 make it configurable if we should always take titleCache if titleCache is protected, as nameCache may not necessarily
536 // have complete data if titleCache is protected as it is considered to be irrelevant or at least preliminary
537 String message = "";
538 if (StringUtils.isNotEmpty(name.getNameCache())) {
539 csvLine[table.getIndex(WfoExportTable.NAME_SCIENTIFIC_NAME)] = name.getNameCache();
540 message = "ScientificName: Name cache " + name.getNameCache() + " used for name with protected titleCache " + name.getTitleCache();
541 }else {
542 csvLine[table.getIndex(WfoExportTable.NAME_SCIENTIFIC_NAME)] = name.getTitleCache();
543 message = "ScientificName: Name has protected titleCache and no explicit nameCache: " + name.getTitleCache();
544 }
545 state.getResult().addWarning(message); //TODO 7 add location to warning
546 } else {
547 csvLine[table.getIndex(WfoExportTable.NAME_SCIENTIFIC_NAME)] = name.getNameCache();
548 }
549
550 //rank
551 String rankStr = state.getTransformer().getCacheByRank(rank);
552 if (rankStr == null) {
553 String message = rank == null ? "No rank" : ("Rank not supported by WFO:" + rank.getLabel())
554 + "Taxon not handled in export: " + name.getTitleCache();
555 state.getResult().addWarning(message); //TODO 2 warning sufficient for missing rank? + location
556 return wfoId;
557 }
558 csvLine[table.getIndex(WfoExportTable.RANK)] = rankStr;
559
560 //authorship
561 //TODO 3 handle empty authorship cache warning
562 csvLine[table.getIndex(WfoExportTable.NAME_AUTHORSHIP)] = name.getAuthorshipCache();
563
564 //family TODO 2 family handling
565 csvLine[table.getIndex(WfoExportTable.TAX_FAMILY)] = state.getFamilyStr();
566
567 //name parts
568 csvLine[table.getIndex(WfoExportTable.NAME_GENUS)] = name.isSupraGeneric()? null : name.getGenusOrUninomial();
569 csvLine[table.getIndex(WfoExportTable.NAME_SPECIFIC_EPITHET)] = name.getSpecificEpithet();
570 csvLine[table.getIndex(WfoExportTable.NAME_INFRASPECIFIC_EPITHET)] = name.getInfraSpecificEpithet();
571
572 //TODO 3 verbatimTaxonRank, is this needed at all?
573 csvLine[table.getIndex(WfoExportTable.NAME_VERBATIM_RANK)] = null;
574
575 //name status
576 csvLine[table.getIndex(WfoExportTable.NAME_STATUS)] = makeNameStatus(state, name);
577
578 //nom. ref
579 String nomRef = NomenclaturalSourceFormatter.INSTANCE().format(name.getNomenclaturalSource());
580 csvLine[table.getIndex(WfoExportTable.NAME_PUBLISHED_IN)] = nomRef;
581
582 //originalNameID
583 TaxonName originalName = name.getBasionym(); //TODO 5 basionym, order in case there are >1 basionyms
584 if (originalName == null) {
585 originalName = name.getReplacedSynonyms().stream().findFirst().orElse(null);
586 }
587
588 if (originalName != null) {
589 if (!state.getNameStore().containsKey(originalName.getId())) {
590 //TODO 1 handle basionym is in file assertion
591 }
592 String basionymId = getWfoId(state, originalName, false);
593 csvLine[table.getIndex(WfoExportTable.NAME_ORIGINAL_NAME_ID)] = basionymId;
594 }
595
596 //TODO 1 created
597 csvLine[table.getIndex(WfoExportTable.CREATED)] = null;
598
599 //TODO 2 modified
600 csvLine[table.getIndex(WfoExportTable.MODIFIED)] = null;
601
602 //TODO 1 URL to taxon
603 csvLine[table.getIndex(WfoExportTable.REFERENCES)] = null;
604
605 //TODO 3 excluded info
606 csvLine[table.getIndex(WfoExportTable.EXCLUDE)] = null;
607
608 //TODO 1 related names like orth. var., original spelling,
609
610 state.getProcessor().put(table, name, csvLine);
611
612 } catch (Exception e) {
613 state.getResult().addException(e,
614 "An unexpected error occurred when handling the name " + cdmBaseStr(name) + ": " + name.getTitleCache() + ": " + e.getMessage());
615
616 e.printStackTrace();
617 }
618
619 return wfoId;
620 }
621
622 private String getWfoId(WfoExportState state, TaxonName name, boolean warnIfNotExists) {
623 Identifier wfoId = name.getIdentifier(IdentifierType.uuidWfoNameIdentifier);
624 if (wfoId == null && warnIfNotExists) {
625 String message = "No wfo-id given for name: " + name.getTitleCache()+"/"+ name.getUuid();
626 state.getResult().addWarning(message); //TODO 5 data location
627 }
628 return wfoId == null ? null : wfoId.getIdentifier();
629 }
630
631 private String makeNameStatus(WfoExportState state, TaxonName name) {
632 try {
633
634 //TODO 1 what is with dubium
635 if (name.isLegitimate()) {
636 if (name.isConserved()) {
637 return "Conserved";
638 }else {
639 return "Valid";
640 }
641 } else if (name.isRejected()) {
642 return "Rejected";
643 } else if (name.isIllegitimate()) {
644 return "Illegitimate";
645 } else if (name.isInvalid()) {
646 //TODO 2 handle original spellings for name status
647 if (name.isOrthographicVariant()) {
648 return "orthografia";
649 }else {
650 return "invalid";
651 }
652 } else {
653 String message = "Unhandled name status case for name: " + name.getTitleCache() +
654 ". Status not handled correctly.";
655 state.getResult().addWarning(message);
656 return "undefined";
657 }
658 } catch (Exception e) {
659 state.getResult().addException(e, "An unexpected error occurred when extracting status string for "
660 + cdmBaseStr(name) + ": " + e.getMessage());
661 return "";
662 }
663 }
664
665 private void handleHomotypicalGroup(WfoExportState state, HomotypicalGroup group, Taxon acceptedTaxon) {
666 try {
667
668 List<TaxonName> typifiedNames = new ArrayList<>();
669 if (acceptedTaxon != null){
670 List<Synonym> synonymsInGroup = acceptedTaxon.getSynonymsInGroup(group);
671 if (group.equals(acceptedTaxon.getHomotypicGroup())){
672 typifiedNames.add(acceptedTaxon.getName());
673 }
674 synonymsInGroup.stream().forEach(synonym -> typifiedNames.add(CdmBase.deproxy(synonym.getName())));
675 }
676
677
678 TaxonName firstname = null;
679 for (TaxonName name: typifiedNames){
680 Iterator<Taxon> taxa = name.getTaxa().iterator();
681 while(taxa.hasNext()){
682 Taxon taxon = taxa.next();
683 if(!(taxon.isMisapplication() || taxon.isProparteSynonym())){
684 firstname = name;
685 break;
686 }
687 }
688 }
689
690 } catch (Exception e) {
691 state.getResult().addException(e, "An unexpected error occurred when handling homotypic group "
692 + cdmBaseStr(group) + ": " + e.getMessage());
693 }
694 }
695
696 private void handleReference(WfoExportState state, Reference reference) {
697 try {
698 if (state.getReferenceStore().contains(reference.getUuid())) {
699 return;
700 }
701 reference = CdmBase.deproxy(reference);
702
703 state.addReferenceToStore(reference);
704 WfoExportTable table = WfoExportTable.REFERECE;
705 String[] csvLine = new String[table.getSize()];
706
707 csvLine[table.getIndex(WfoExportTable.IDENTIFIER)] = getId(state, reference);
708
709 //TODO 2 correct?, ref biblio citation
710 csvLine[table.getIndex(WfoExportTable.REF_BIBLIO_CITATION)] = reference.getCitation();
711
712 //TODO 1 uri (doi, uri or ext_link
713 // csvLine[table.getIndex(WfoExportTable.REF_DOI)] = reference.getDoiString();
714 //
715 // //TODO 2 reference link link (=> external link)
716 //// csvLine[table.getIndex(ColDpExportTable.LINK)] = null;
717 // if (reference.getUri() != null) {
718 // csvLine[table.getIndex(WfoExportTable.LINK)] = reference.getUri().toString();
719 // }
720
721 state.getProcessor().put(table, reference, csvLine);
722 } catch (Exception e) {
723 e.printStackTrace();
724 state.getResult().addException(e, "An unexpected error occurred when handling reference "
725 + cdmBaseStr(reference) + ": " + e.getMessage());
726 }
727 }
728
729 /**
730 * Returns a string representation of the {@link CdmBase cdmBase} object for
731 * result messages.
732 */
733 private String cdmBaseStr(CdmBase cdmBase) {
734 if (cdmBase == null) {
735 return "-no object available-";
736 } else {
737 return cdmBase.getClass().getSimpleName() + ": " + cdmBase.getUuid();
738 }
739 }
740
741 @Override
742 protected boolean doCheck(WfoExportState state) {
743 return false;
744 }
745
746 @Override
747 protected boolean isIgnore(WfoExportState state) {
748 return false;
749 }
750 }