2 * Copyright (C) 2009 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
.markup
;
12 import java
.util
.HashMap
;
15 import javax
.xml
.stream
.XMLEventReader
;
16 import javax
.xml
.stream
.XMLStreamException
;
17 import javax
.xml
.stream
.events
.Attribute
;
18 import javax
.xml
.stream
.events
.StartElement
;
19 import javax
.xml
.stream
.events
.XMLEvent
;
21 import org
.apache
.commons
.lang
.StringUtils
;
22 import org
.apache
.log4j
.Logger
;
24 import eu
.etaxonomy
.cdm
.model
.agent
.INomenclaturalAuthor
;
25 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
26 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
27 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
28 import eu
.etaxonomy
.cdm
.model
.common
.TimePeriod
;
29 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
30 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
31 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
32 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
33 import eu
.etaxonomy
.cdm
.model
.name
.NameTypeDesignationStatus
;
34 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatus
;
35 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatusType
;
36 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
37 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
38 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
39 import eu
.etaxonomy
.cdm
.model
.reference
.IArticle
;
40 import eu
.etaxonomy
.cdm
.model
.reference
.IJournal
;
41 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
42 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceFactory
;
43 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceType
;
44 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
45 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
46 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.UnknownCdmTypeException
;
47 import eu
.etaxonomy
.cdm
.strategy
.parser
.NameTypeParser
;
48 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImpl
;
55 public class MarkupNomenclatureImport
extends MarkupImportBase
{
56 @SuppressWarnings("unused")
57 private static final Logger logger
= Logger
.getLogger(MarkupNomenclatureImport
.class);
60 private NonViralNameParserImpl parser
= new NonViralNameParserImpl();
62 private MarkupKeyImport keyImport
;
63 private MarkupSpecimenImport specimenImport
;
65 public MarkupNomenclatureImport(MarkupDocumentImport docImport
,
66 MarkupKeyImport keyImport
, MarkupSpecimenImport specimenImport
) {
68 this.keyImport
= keyImport
;
69 this.specimenImport
= specimenImport
;
72 public void handleNomenclature(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
)
73 throws XMLStreamException
{
74 checkNoAttributes(parentEvent
);
76 while (reader
.hasNext()) {
77 XMLEvent next
= readNoWhitespace(reader
);
78 if (isStartingElement(next
, HOMOTYPES
)) {
79 handleHomotypes(state
, reader
, next
.asStartElement());
80 } else if (isMyEndingElement(next
, parentEvent
)) {
83 fireSchemaConflictEventExpectedStartTag(HOMOTYPES
, reader
);
84 state
.setUnsuccessfull();
90 private void handleHomotypes(MarkupImportState state
,
91 XMLEventReader reader
, StartElement parentEvent
)
92 throws XMLStreamException
{
93 checkNoAttributes(parentEvent
);
95 HomotypicalGroup homotypicalGroup
= null;
97 boolean hasNom
= false;
98 while (reader
.hasNext()) {
99 XMLEvent next
= readNoWhitespace(reader
);
100 if (next
.isEndElement()) {
101 if (isMyEndingElement(next
, parentEvent
)) {
102 checkMandatoryElement(hasNom
, parentEvent
, NOM
);
105 if (isEndingElement(next
, NAME_TYPE
)) {
106 state
.setNameType(false);
107 } else if (isEndingElement(next
, NOTES
)) {
108 // NOT YET IMPLEMENTED
109 popUnimplemented(next
.asEndElement());
111 handleUnexpectedEndElement(next
.asEndElement());
114 } else if (next
.isStartElement()) {
115 if (isStartingElement(next
, NOM
)) {
116 NonViralName
<?
> name
= handleNom(state
, reader
, next
, homotypicalGroup
);
117 homotypicalGroup
= name
.getHomotypicalGroup();
119 } else if (isStartingElement(next
, NAME_TYPE
)) {
120 state
.setNameType(true);
121 handleNameType(state
, reader
, next
, homotypicalGroup
);
122 } else if (isStartingElement(next
, SPECIMEN_TYPE
)) {
123 specimenImport
.handleSpecimenType(state
, reader
, next
,
125 } else if (isStartingElement(next
, NOTES
)) {
126 handleNotYetImplementedElement(next
);
128 handleUnexpectedStartElement(next
);
131 handleUnexpectedElement(next
);
134 // TODO handle missing end element
135 throw new IllegalStateException("Homotypes has no closing tag");
139 private void handleNameType(MarkupImportState state
, XMLEventReader reader
,
140 XMLEvent parentEvent
, HomotypicalGroup homotypicalGroup
)
141 throws XMLStreamException
{
142 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
143 String typeStatus
= getAndRemoveAttributeValue(attributes
, TYPE_STATUS
);
144 checkNoAttributes(attributes
, parentEvent
);
146 NameTypeDesignationStatus status
;
148 status
= NameTypeParser
.parseNameTypeStatus(typeStatus
);
149 } catch (UnknownCdmTypeException e
) {
150 String message
= "Type status could not be recognized: %s";
151 message
= String
.format(message
, typeStatus
);
152 fireWarningEvent(message
, parentEvent
, 4);
156 boolean hasNom
= false;
157 while (reader
.hasNext()) {
158 XMLEvent next
= readNoWhitespace(reader
);
159 if (next
.isEndElement()) {
160 if (isMyEndingElement(next
, parentEvent
)) {
161 checkMandatoryElement(hasNom
, parentEvent
.asStartElement(),
163 state
.setNameType(false);
166 if (isEndingElement(next
, ACCEPTED_NAME
)) {
167 // NOT YET IMPLEMENTED
168 popUnimplemented(next
.asEndElement());
170 handleUnexpectedEndElement(next
.asEndElement());
173 } else if (next
.isStartElement()) {
174 if (isStartingElement(next
, NOM
)) {
175 // TODO should we check if the type is always a species, is
177 NonViralName
<?
> speciesName
= handleNom(state
, reader
,
179 for (TaxonNameBase
<?
, ?
> name
: homotypicalGroup
180 .getTypifiedNames()) {
181 name
.addNameTypeDesignation(speciesName
, null, null,
182 null, status
, false, false, false, false);
185 } else if (isStartingElement(next
, ACCEPTED_NAME
)) {
186 handleNotYetImplementedElement(next
);
188 handleUnexpectedStartElement(next
);
191 handleUnexpectedElement(next
);
194 // TODO handle missing end element
195 throw new IllegalStateException("Homotypes has no closing tag");
200 * Creates the name defined by a nom tag. Adds it to the given homotypical
201 * group (if not null).
206 * @param homotypicalGroup
208 * @throws XMLStreamException
210 private NonViralName
<?
> handleNom(MarkupImportState state
,
211 XMLEventReader reader
, XMLEvent parentEvent
,
212 HomotypicalGroup homotypicalGroup
) throws XMLStreamException
{
213 boolean isSynonym
= false;
214 boolean isNameType
= state
.isNameType();
216 String classValue
= getClassOnlyAttribute(parentEvent
);
217 NonViralName
<?
> name
;
218 if (!isNameType
&& ACCEPTED
.equalsIgnoreCase(classValue
)) {
220 name
= createName(state
, homotypicalGroup
, isSynonym
);
221 } else if (!isNameType
&& SYNONYM
.equalsIgnoreCase(classValue
)) {
223 name
= createName(state
, homotypicalGroup
, isSynonym
);
224 } else if (isNameType
&& NAME_TYPE
.equalsIgnoreCase(classValue
)) {
225 // TODO do we need to define the rank here?
226 name
= createNameByCode(state
, null);
228 fireUnexpectedAttributeValue(parentEvent
, CLASS
, classValue
);
229 name
= createNameByCode(state
, null);
232 Map
<String
, String
> nameMap
= new HashMap
<String
, String
>();
235 while (reader
.hasNext()) {
236 XMLEvent next
= readNoWhitespace(reader
);
237 if (isMyEndingElement(next
, parentEvent
)) {
238 // fill the name with all data gathered
239 fillName(state
, nameMap
, name
, next
);
240 handleNomText(state
, parentEvent
, text
, isNameType
);
242 } else if (isEndingElement(next
, ANNOTATION
)) {
243 // NOT YET IMPLEMENTED //TODO test
244 // handleSimpleAnnotation
245 popUnimplemented(next
.asEndElement());
246 }else if (isStartingElement(next
, FULL_NAME
)) {
247 handleFullName(state
, reader
, name
, next
);
248 } else if (isStartingElement(next
, NUM
)) {
249 handleNomNum(state
, reader
, next
);
250 } else if (isStartingElement(next
, NAME
)) {
251 handleName(state
, reader
, next
, nameMap
);
252 } else if (isStartingElement(next
, CITATION
)) {
253 handleCitation(state
, reader
, next
, name
);
254 } else if (next
.isCharacters()) {
255 text
+= next
.asCharacters().getData();
256 } else if (isStartingElement(next
, HOMONYM
)) {
257 handleNotYetImplementedElement(next
);
258 } else if (isStartingElement(next
, NOTES
)) {
259 handleNotYetImplementedElement(next
);
260 } else if (isStartingElement(next
, ANNOTATION
)) {
261 handleNotYetImplementedElement(next
);
263 handleUnexpectedElement(next
);
266 // TODO handle missing end element
267 throw new IllegalStateException("Nom has no closing tag");
271 * Handles appearance of text within <nom> tags.
272 * Usually this is not expected except for some information that is already handled
273 * elsewhere, e.g. the string Nametype is holding information that is available already
274 * via the surrounding nametype tag. Therefore this information can be neglected.
275 * This method is open for upcoming cases which need to be handled.
281 private void handleNomText(MarkupImportState state
, XMLEvent event
, String text
, boolean isNameType
) {
286 //neglect known redundant strings
287 if (isNameType
&& text
.matches("(?i)^Esp[\u00E8\u00C8]ce[·\\-\\s]type\\:$")){
289 }//neglect meaningless punctuation
290 else if (isPunctuation(text
)){
293 String message
= "Unhandled text in <nom> tag: \"%s\"";
294 fireWarningEvent(String
.format(message
, text
), event
, 4);
302 * @throws XMLStreamException
304 private void handleNomNum(MarkupImportState state
, XMLEventReader reader
,
305 XMLEvent next
) throws XMLStreamException
{
306 String num
= getCData(state
, reader
, next
);
307 num
= num
.replace(".", "");
308 num
= num
.replace(")", "");
309 if (StringUtils
.isNotBlank(num
)) {
310 if (state
.getCurrentTaxonNum() != null
311 && !state
.getCurrentTaxonNum().equals(num
)) {
312 String message
= "Taxontitle num and homotypes/nom/num differ ( %s <-> %s ). I use the later one.";
313 message
= String
.format(message
,
314 state
.getCurrentTaxonNum(), num
);
315 fireWarningEvent(message
, next
, 4);
317 state
.setCurrentTaxonNum(num
);
326 * @throws XMLStreamException
328 private void handleFullName(MarkupImportState state
, XMLEventReader reader
,
329 NonViralName
<?
> name
, XMLEvent next
) throws XMLStreamException
{
331 Map
<String
, Attribute
> attrs
= getAttributes(next
);
332 String rankStr
= getAndRemoveRequiredAttributeValue(next
,
334 Rank rank
= makeRank(state
, rankStr
, false);
337 String message
= "Rank was computed as null. This must not be.";
338 fireWarningEvent(message
, next
, 6);
339 name
.setRank(Rank
.UNKNOWN_RANK());
341 if (!attrs
.isEmpty()) {
342 handleUnexpectedAttributes(next
.getLocation(), attrs
);
344 fullNameStr
= getCData(state
, reader
, next
);
345 name
.setTitleCache(fullNameStr
, true);
348 private void handleName(MarkupImportState state
, XMLEventReader reader
,
349 XMLEvent parentEvent
, Map
<String
, String
> nameMap
)
350 throws XMLStreamException
{
351 String classValue
= getClassOnlyAttribute(parentEvent
);
354 while (reader
.hasNext()) {
355 XMLEvent next
= readNoWhitespace(reader
);
356 if (isMyEndingElement(next
, parentEvent
)) {
357 nameMap
.put(classValue
, text
);
359 } else if (isStartingElement(next
, ANNOTATION
)) {
360 handleNotYetImplementedElement(next
); // TODO test
361 // handleSimpleAnnotation
362 } else if (next
.isCharacters()) {
363 text
+= next
.asCharacters().getData();
365 handleUnexpectedElement(next
);
368 throw new IllegalStateException("name has no closing tag");
371 private void fillName(MarkupImportState state
, Map
<String
, String
> nameMap
,
372 NonViralName
<?
> name
, XMLEvent event
) {
374 // Ranks: family, subfamily, tribus, genus, subgenus, section,
375 // subsection, species, subspecies, variety, subvariety, forma
376 // infrank, paraut, author, infrparaut, infraut, status, notes
378 String infrank
= getAndRemoveMapKey(nameMap
, INFRANK
);
379 String authorStr
= getAndRemoveMapKey(nameMap
, AUTHOR
);
380 String paraut
= getAndRemoveMapKey(nameMap
, PARAUT
);
382 String infrParAut
= getAndRemoveMapKey(nameMap
, INFRPARAUT
);
383 String infrAut
= getAndRemoveMapKey(nameMap
, INFRAUT
);
385 String statusStr
= getAndRemoveMapKey(nameMap
, STATUS
);
386 String notes
= getAndRemoveMapKey(nameMap
, NOTES
);
388 if (!name
.isProtectedTitleCache()) { // otherwise fullName
390 makeRankDecision(state
, nameMap
, name
, event
, infrank
);
392 // test consistency of rank and authors
393 testRankAuthorConsistency(name
, event
, authorStr
, paraut
,
394 infrParAut
, infrAut
);
397 makeNomenclaturalAuthors(name
, event
, authorStr
, paraut
,
398 infrParAut
, infrAut
);
402 // TODO handle pro parte, pro syn. etc.
403 if (StringUtils
.isNotBlank(statusStr
)) {
404 String proPartePattern
= "(pro parte|p.p.)";
405 if (statusStr
.matches(proPartePattern
)) {
406 state
.setProParte(true);
409 // TODO handle trim earlier
410 statusStr
= statusStr
.trim();
411 NomenclaturalStatusType nomStatusType
= NomenclaturalStatusType
412 .getNomenclaturalStatusTypeByAbbreviation(statusStr
);
413 name
.addStatus(NomenclaturalStatus
.NewInstance(nomStatusType
));
414 } catch (UnknownCdmTypeException e
) {
415 String message
= "Status '%s' could not be recognized";
416 message
= String
.format(message
, statusStr
);
417 fireWarningEvent(message
, event
, 4);
422 if (StringUtils
.isNotBlank(notes
)) {
423 handleNotYetImplementedAttributeValue(event
, CLASS
, NOTES
);
436 private void makeRankDecision(MarkupImportState state
,
437 Map
<String
, String
> nameMap
, NonViralName
<?
> name
, XMLEvent event
,
440 for (String key
: nameMap
.keySet()) {
441 Rank rank
= makeRank(state
, key
, false);
443 handleNotYetImplementedAttributeValue(event
, CLASS
, key
);
445 if (name
.getRank() == null || rank
.isLower(name
.getRank())) {
448 String value
= nameMap
.get(key
);
449 if (rank
.isSupraGeneric() || rank
.isGenus()) {
450 if ((key
.equalsIgnoreCase(GENUS_ABBREVIATION
)
451 && isNotBlank(state
.getLatestGenusEpithet()) || isGenusAbbrev(
452 value
, state
.getLatestGenusEpithet()))) {
453 value
= state
.getLatestGenusEpithet();
455 name
.setGenusOrUninomial(toFirstCapital(value
));
456 } else if (rank
.isInfraGeneric()) {
457 name
.setInfraGenericEpithet(toFirstCapital(value
));
458 } else if (rank
.isSpecies()) {
459 if (state
.getConfig().isAllowCapitalSpeciesEpithet()
460 && isFirstCapitalWord(value
)) { // capital letters
467 name
.setSpecificEpithet(value
);
469 name
.setSpecificEpithet(value
.toLowerCase());
471 } else if (rank
.isInfraSpecific()) {
472 name
.setInfraSpecificEpithet(value
.toLowerCase());
474 String message
= "Invalid rank '%s'. Can't decide which epithet to fill with '%s'";
475 message
= String
.format(message
, rank
.getTitleCache(),
477 fireWarningEvent(message
, event
, 4);
482 // handle given infrank marker
483 if (StringUtils
.isNotBlank(infrankStr
)) {
484 Rank infRank
= makeRank(state
, infrankStr
, true);
486 if (infRank
== null) {
487 String message
= "Infrank '%s' rank not recognized";
488 message
= String
.format(message
, infrankStr
);
489 fireWarningEvent(message
, event
, 4);
491 if (name
.getRank() == null) {
492 name
.setRank(infRank
);
493 } else if (infRank
.isLower(name
.getRank())) {
494 String message
= "InfRank '%s' is lower than existing rank ";
495 message
= String
.format(message
, infrankStr
);
496 fireWarningEvent(message
, event
, 2);
497 name
.setRank(infRank
);
498 } else if (infRank
.equals(name
.getRank())) {
501 String message
= "InfRank '%s' is higher than existing rank ";
502 message
= String
.format(message
, infrankStr
);
503 fireWarningEvent(message
, event
, 2);
517 private void makeNomenclaturalAuthors(NonViralName name
, XMLEvent event
,
518 String authorStr
, String paraut
, String infrParAut
, String infrAut
) {
519 if (name
.getRank() != null && name
.getRank().isInfraSpecific()) {
520 if (StringUtils
.isNotBlank(infrAut
)) {
521 INomenclaturalAuthor
[] authorAndEx
= authorAndEx(infrAut
, event
);
522 name
.setCombinationAuthorTeam(authorAndEx
[0]);
523 name
.setExCombinationAuthorTeam(authorAndEx
[1]);
525 if (StringUtils
.isNotBlank(infrParAut
)) {
526 INomenclaturalAuthor
[] authorAndEx
= authorAndEx(infrParAut
,
528 name
.setBasionymAuthorTeam(authorAndEx
[0]);
529 name
.setExBasionymAuthorTeam(authorAndEx
[1]);
532 if (name
.getRank() == null) {
533 String message
= "No rank defined. Check correct usage of authors!";
534 fireWarningEvent(message
, event
, 4);
535 if (isNotBlank(infrParAut
) || isNotBlank(infrAut
)) {
540 if (StringUtils
.isNotBlank(authorStr
)) {
541 INomenclaturalAuthor
[] authorAndEx
= authorAndEx(authorStr
,
543 name
.setCombinationAuthorTeam(authorAndEx
[0]);
544 name
.setExCombinationAuthorTeam(authorAndEx
[1]);
546 if (StringUtils
.isNotBlank(paraut
)) {
547 INomenclaturalAuthor
[] authorAndEx
= authorAndEx(paraut
, event
);
548 name
.setBasionymAuthorTeam(authorAndEx
[0]);
549 name
.setExBasionymAuthorTeam(authorAndEx
[1]);
554 private TeamOrPersonBase
[] authorAndEx(String authorAndEx
, XMLEvent xmlEvent
) {
555 authorAndEx
= authorAndEx
.trim();
556 TeamOrPersonBase
[] result
= new TeamOrPersonBase
[2];
558 String
[] split
= authorAndEx
.split("\\sex\\s");
559 if (split
.length
> 2) {
560 String message
= "There is more then 1 ' ex ' in author string. Can't separate author and ex-author";
561 fireWarningEvent(message
, xmlEvent
, 4);
562 result
[0] = createAuthor(authorAndEx
);
563 } else if (split
.length
== 2) {
564 result
[0] = createAuthor(split
[1]);
565 result
[1] = createAuthor(split
[0]);
567 result
[0] = createAuthor(split
[0]);
573 * Returns the (empty) name with the correct homotypical group depending on
574 * the taxon status. Throws NPE if no currentTaxon is set in state.
577 * @param homotypicalGroup
581 private NonViralName
<?
> createName(MarkupImportState state
,
582 HomotypicalGroup homotypicalGroup
, boolean isSynonym
) {
583 NonViralName
<?
> name
;
584 Taxon taxon
= state
.getCurrentTaxon();
586 Rank defaultRank
= Rank
.SPECIES(); // can be any
587 name
= createNameByCode(state
, defaultRank
);
588 if (homotypicalGroup
!= null) {
589 name
.setHomotypicalGroup(homotypicalGroup
);
591 SynonymRelationshipType synonymType
= SynonymRelationshipType
592 .HETEROTYPIC_SYNONYM_OF();
593 if (taxon
.getHomotypicGroup().equals(homotypicalGroup
)) {
594 synonymType
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
596 taxon
.addSynonymName(name
, synonymType
);
598 name
= CdmBase
.deproxy(taxon
.getName(), NonViralName
.class);
603 private void handleCitation(MarkupImportState state
, XMLEventReader reader
,
604 XMLEvent parentEvent
, NonViralName name
) throws XMLStreamException
{
605 String classValue
= getClassOnlyAttribute(parentEvent
);
607 state
.setCitation(true);
608 boolean hasRefPart
= false;
609 Map
<String
, String
> refMap
= new HashMap
<String
, String
>();
610 while (reader
.hasNext()) {
611 XMLEvent next
= readNoWhitespace(reader
);
612 if (isMyEndingElement(next
, parentEvent
)) {
613 checkMandatoryElement(hasRefPart
, parentEvent
.asStartElement(),
615 Reference
<?
> reference
= createReference(state
, refMap
, next
);
616 String microReference
= refMap
.get(DETAILS
);
617 doCitation(state
, name
, classValue
, reference
, microReference
,
619 state
.setCitation(false);
621 } else if (isStartingElement(next
, REF_PART
)) {
622 handleRefPart(state
, reader
, next
, refMap
);
625 handleUnexpectedElement(next
);
628 throw new IllegalStateException("Citation has no closing tag");
632 private void handleRefPart(MarkupImportState state
, XMLEventReader reader
,
633 XMLEvent parentEvent
, Map
<String
, String
> refMap
)
634 throws XMLStreamException
{
635 String classValue
= getClassOnlyAttribute(parentEvent
);
638 while (reader
.hasNext()) {
639 XMLEvent next
= readNoWhitespace(reader
);
640 if (isMyEndingElement(next
, parentEvent
)) {
641 refMap
.put(classValue
, text
);
643 } else if (next
.isStartElement()) {
644 if (isStartingElement(next
, ANNOTATION
)) {
645 handleNotYetImplementedElement(next
); // TODO test
646 // handleSimpleAnnotation
647 } else if (isStartingElement(next
, ITALICS
)) {
648 handleNotYetImplementedElement(next
);
649 } else if (isStartingElement(next
, BOLD
)) {
650 handleNotYetImplementedElement(next
);
652 handleUnexpectedStartElement(next
.asStartElement());
654 } else if (next
.isCharacters()) {
655 text
+= next
.asCharacters().getData();
657 handleUnexpectedEndElement(next
.asEndElement());
660 throw new IllegalStateException("RefPart has no closing tag");
664 private void doCitation(MarkupImportState state
, NonViralName name
,
665 String classValue
, Reference reference
, String microCitation
,
666 XMLEvent parentEvent
) {
667 if (PUBLICATION
.equalsIgnoreCase(classValue
)) {
668 name
.setNomenclaturalReference(reference
);
669 name
.setNomenclaturalMicroReference(microCitation
);
670 } else if (USAGE
.equalsIgnoreCase(classValue
)) {
671 Taxon taxon
= state
.getCurrentTaxon();
672 TaxonDescription td
= getTaxonDescription(taxon
, state
.getConfig()
673 .getSourceReference(), false, true);
674 TextData citation
= TextData
.NewInstance(Feature
.CITATION());
675 // TODO name used in source
676 citation
.addSource(OriginalSourceType
.PrimaryTaxonomicSource
, null, null, reference
, microCitation
);
677 td
.addElement(citation
);
678 } else if (TYPE
.equalsIgnoreCase(classValue
)) {
679 handleNotYetImplementedAttributeValue(parentEvent
, CLASS
,
682 // TODO Not yet implemented
683 handleNotYetImplementedAttributeValue(parentEvent
, CLASS
,
689 * Tests if the names rank is consistent with the given author strings.
690 * NOTE: Tags for authors are differ depending on the rank.
699 private void testRankAuthorConsistency(NonViralName name
, XMLEvent event
,
700 String authorStr
, String paraut
, String infrParAut
, String infrAut
) {
701 if (name
.getRank() == null) {
704 if (name
.getRank().isInfraSpecific()) {
705 if (StringUtils
.isBlank(infrParAut
)
706 && StringUtils
.isBlank(infrAut
) // was isNotBlank before
708 && (StringUtils
.isNotBlank(paraut
) || StringUtils
709 .isNotBlank(authorStr
)) && !name
.isAutonym()) {
710 String message
= "Rank is infraspecicific but has only specific or higher author(s)";
711 fireWarningEvent(message
, event
, 4);
714 // is not infraspecific
715 if (StringUtils
.isNotBlank(infrParAut
)
716 || StringUtils
.isNotBlank(infrAut
)) {
717 String message
= "Rank is not infraspecicific but name has infra author(s)";
718 fireWarningEvent(message
, event
, 4);
723 private Reference
<?
> createReference(MarkupImportState state
,
724 Map
<String
, String
> refMap
, XMLEvent parentEvent
) {
726 Reference
<?
> reference
;
728 String type
= getAndRemoveMapKey(refMap
, PUBTYPE
);
729 String authorStr
= getAndRemoveMapKey(refMap
, AUTHOR
);
730 String titleStr
= getAndRemoveMapKey(refMap
, PUBTITLE
);
731 String titleCache
= getAndRemoveMapKey(refMap
, PUBFULLNAME
);
732 String volume
= getAndRemoveMapKey(refMap
, VOLUME
);
733 String edition
= getAndRemoveMapKey(refMap
, EDITION
);
734 String editors
= getAndRemoveMapKey(refMap
, EDITORS
);
735 String year
= getAndRemoveMapKey(refMap
, YEAR
);
736 String pubName
= getAndRemoveMapKey(refMap
, PUBNAME
);
737 String pages
= getAndRemoveMapKey(refMap
, PAGES
);
739 if (state
.isCitation()) {
740 if (volume
!= null || "journal".equalsIgnoreCase(type
)) {
741 IArticle article
= ReferenceFactory
.newArticle();
742 if (pubName
!= null) {
743 IJournal journal
= ReferenceFactory
.newJournal();
744 journal
.setTitle(pubName
);
745 article
.setInJournal(journal
);
747 reference
= (Reference
<?
>) article
;
751 if (pubName
!= null) {
752 reference
= ReferenceFactory
.newBookSection();
754 reference
= ReferenceFactory
.newBook();
757 // TODO use existing author from name or before
758 TeamOrPersonBase
<?
> author
= createAuthor(authorStr
);
759 reference
.setAuthorTeam(author
);
761 reference
.setTitle(titleStr
);
762 if (StringUtils
.isNotBlank(titleCache
)) {
763 reference
.setTitleCache(titleCache
, true);
765 reference
.setEdition(edition
);
766 reference
.setEditor(editors
);
768 if (pubName
!= null) {
769 Reference
<?
> inReference
;
770 if (reference
.getType().equals(ReferenceType
.Article
)) {
771 inReference
= ReferenceFactory
.newJournal();
773 inReference
= ReferenceFactory
.newGeneric();
775 inReference
.setTitle(pubName
);
776 reference
.setInReference(inReference
);
779 } else { // no citation
780 if (volume
!= null || "journal".equalsIgnoreCase(type
)) {
781 IArticle article
= ReferenceFactory
.newArticle();
782 if (pubName
!= null) {
783 IJournal journal
= ReferenceFactory
.newJournal();
784 journal
.setTitle(pubName
);
785 article
.setInJournal(journal
);
787 reference
= (Reference
<?
>) article
;
790 Reference
<?
> bookOrPartOf
= ReferenceFactory
.newGeneric();
791 reference
= bookOrPartOf
;
795 TeamOrPersonBase
<?
> author
= createAuthor(authorStr
);
796 reference
.setAuthorTeam(author
);
798 reference
.setTitle(titleStr
);
799 if (StringUtils
.isNotBlank(titleCache
)) {
800 reference
.setTitleCache(titleCache
, true);
802 reference
.setEdition(edition
);
803 reference
.setEditor(editors
);
805 if (pubName
!= null) {
806 Reference
<?
> inReference
;
807 if (reference
.getType().equals(ReferenceType
.Article
)) {
808 inReference
= ReferenceFactory
.newJournal();
810 inReference
= ReferenceFactory
.newGeneric();
812 inReference
.setTitle(pubName
);
813 reference
.setInReference(inReference
);
816 reference
.setVolume(volume
);
817 reference
.setDatePublished(TimePeriod
.parseString(year
));
818 // TODO check if this is handled correctly in FM markup
819 reference
.setPages(pages
);
822 String
[] unhandledList
= new String
[] { ALTERNATEPUBTITLE
, ISSUE
,
824 for (String unhandled
: unhandledList
) {
825 String value
= getAndRemoveMapKey(refMap
, unhandled
);
826 if (isNotBlank(value
)) {
827 this.handleNotYetImplementedAttributeValue(parentEvent
, CLASS
,
832 for (String key
: refMap
.keySet()) {
833 if (!DETAILS
.equalsIgnoreCase(key
)) {
834 this.fireUnexpectedAttributeValue(parentEvent
, CLASS
, key
);
841 public Reference
<?
> handleReference(MarkupImportState state
,
842 XMLEventReader reader
, XMLEvent parentEvent
)
843 throws XMLStreamException
{
844 checkNoAttributes(parentEvent
);
846 boolean hasRefPart
= false;
847 Map
<String
, String
> refMap
= new HashMap
<String
, String
>();
848 while (reader
.hasNext()) {
849 XMLEvent next
= readNoWhitespace(reader
);
850 if (isMyEndingElement(next
, parentEvent
)) {
851 checkMandatoryElement(hasRefPart
, parentEvent
.asStartElement(),
853 Reference
<?
> reference
= createReference(state
, refMap
, next
);
855 } else if (isStartingElement(next
, REF_PART
)) {
856 handleRefPart(state
, reader
, next
, refMap
);
859 handleUnexpectedElement(next
);
862 // TODO handle missing end element
863 throw new IllegalStateException("<Reference> has no closing tag");