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
.TimePeriod
;
28 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
29 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
30 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
31 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
32 import eu
.etaxonomy
.cdm
.model
.name
.NameTypeDesignationStatus
;
33 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatus
;
34 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatusType
;
35 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
36 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
37 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
38 import eu
.etaxonomy
.cdm
.model
.reference
.IArticle
;
39 import eu
.etaxonomy
.cdm
.model
.reference
.IJournal
;
40 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
41 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceFactory
;
42 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceType
;
43 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
44 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
45 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.UnknownCdmTypeException
;
46 import eu
.etaxonomy
.cdm
.strategy
.parser
.NameTypeParser
;
47 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImpl
;
54 public class MarkupNomenclatureImport
extends MarkupImportBase
{
55 @SuppressWarnings("unused")
56 private static final Logger logger
= Logger
.getLogger(MarkupNomenclatureImport
.class);
59 private NonViralNameParserImpl parser
= new NonViralNameParserImpl();
61 private MarkupKeyImport keyImport
;
62 private MarkupSpecimenImport specimenImport
;
64 public MarkupNomenclatureImport(MarkupDocumentImport docImport
,
65 MarkupKeyImport keyImport
, MarkupSpecimenImport specimenImport
) {
67 this.keyImport
= keyImport
;
68 this.specimenImport
= specimenImport
;
71 public void handleNomenclature(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
)
72 throws XMLStreamException
{
73 checkNoAttributes(parentEvent
);
75 while (reader
.hasNext()) {
76 XMLEvent next
= readNoWhitespace(reader
);
77 if (isStartingElement(next
, HOMOTYPES
)) {
78 handleHomotypes(state
, reader
, next
.asStartElement());
79 } else if (isMyEndingElement(next
, parentEvent
)) {
82 fireSchemaConflictEventExpectedStartTag(HOMOTYPES
, reader
);
83 state
.setUnsuccessfull();
89 private void handleHomotypes(MarkupImportState state
,
90 XMLEventReader reader
, StartElement parentEvent
)
91 throws XMLStreamException
{
92 checkNoAttributes(parentEvent
);
94 HomotypicalGroup homotypicalGroup
= null;
96 boolean hasNom
= false;
97 while (reader
.hasNext()) {
98 XMLEvent next
= readNoWhitespace(reader
);
99 if (next
.isEndElement()) {
100 if (isMyEndingElement(next
, parentEvent
)) {
101 checkMandatoryElement(hasNom
, parentEvent
, NOM
);
104 if (isEndingElement(next
, NAME_TYPE
)) {
105 state
.setNameType(false);
106 } else if (isEndingElement(next
, NOTES
)) {
107 // NOT YET IMPLEMENTED
108 popUnimplemented(next
.asEndElement());
110 handleUnexpectedEndElement(next
.asEndElement());
113 } else if (next
.isStartElement()) {
114 if (isStartingElement(next
, NOM
)) {
115 NonViralName
<?
> name
= handleNom(state
, reader
, next
, homotypicalGroup
);
116 homotypicalGroup
= name
.getHomotypicalGroup();
118 } else if (isStartingElement(next
, NAME_TYPE
)) {
119 state
.setNameType(true);
120 handleNameType(state
, reader
, next
, homotypicalGroup
);
121 } else if (isStartingElement(next
, SPECIMEN_TYPE
)) {
122 specimenImport
.handleSpecimenType(state
, reader
, next
,
124 } else if (isStartingElement(next
, NOTES
)) {
125 handleNotYetImplementedElement(next
);
127 handleUnexpectedStartElement(next
);
130 handleUnexpectedElement(next
);
133 // TODO handle missing end element
134 throw new IllegalStateException("Homotypes has no closing tag");
138 private void handleNameType(MarkupImportState state
, XMLEventReader reader
,
139 XMLEvent parentEvent
, HomotypicalGroup homotypicalGroup
)
140 throws XMLStreamException
{
141 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
142 String typeStatus
= getAndRemoveAttributeValue(attributes
, TYPE_STATUS
);
143 checkNoAttributes(attributes
, parentEvent
);
145 NameTypeDesignationStatus status
;
147 status
= NameTypeParser
.parseNameTypeStatus(typeStatus
);
148 } catch (UnknownCdmTypeException e
) {
149 String message
= "Type status could not be recognized: %s";
150 message
= String
.format(message
, typeStatus
);
151 fireWarningEvent(message
, parentEvent
, 4);
155 boolean hasNom
= false;
156 while (reader
.hasNext()) {
157 XMLEvent next
= readNoWhitespace(reader
);
158 if (next
.isEndElement()) {
159 if (isMyEndingElement(next
, parentEvent
)) {
160 checkMandatoryElement(hasNom
, parentEvent
.asStartElement(),
162 state
.setNameType(false);
165 if (isEndingElement(next
, ACCEPTED_NAME
)) {
166 // NOT YET IMPLEMENTED
167 popUnimplemented(next
.asEndElement());
169 handleUnexpectedEndElement(next
.asEndElement());
172 } else if (next
.isStartElement()) {
173 if (isStartingElement(next
, NOM
)) {
174 // TODO should we check if the type is always a species, is
176 NonViralName
<?
> speciesName
= handleNom(state
, reader
,
178 for (TaxonNameBase
<?
, ?
> name
: homotypicalGroup
179 .getTypifiedNames()) {
180 name
.addNameTypeDesignation(speciesName
, null, null,
181 null, status
, false, false, false, false);
184 } else if (isStartingElement(next
, ACCEPTED_NAME
)) {
185 handleNotYetImplementedElement(next
);
187 handleUnexpectedStartElement(next
);
190 handleUnexpectedElement(next
);
193 // TODO handle missing end element
194 throw new IllegalStateException("Homotypes has no closing tag");
199 * Creates the name defined by a nom tag. Adds it to the given homotypical
200 * group (if not null).
205 * @param homotypicalGroup
207 * @throws XMLStreamException
209 private NonViralName
<?
> handleNom(MarkupImportState state
,
210 XMLEventReader reader
, XMLEvent parentEvent
,
211 HomotypicalGroup homotypicalGroup
) throws XMLStreamException
{
212 boolean isSynonym
= false;
213 boolean isNameType
= state
.isNameType();
215 String classValue
= getClassOnlyAttribute(parentEvent
);
216 NonViralName
<?
> name
;
217 if (!isNameType
&& ACCEPTED
.equalsIgnoreCase(classValue
)) {
219 name
= createName(state
, homotypicalGroup
, isSynonym
);
220 } else if (!isNameType
&& SYNONYM
.equalsIgnoreCase(classValue
)) {
222 name
= createName(state
, homotypicalGroup
, isSynonym
);
223 } else if (isNameType
&& NAME_TYPE
.equalsIgnoreCase(classValue
)) {
224 // TODO do we need to define the rank here?
225 name
= createNameByCode(state
, null);
227 fireUnexpectedAttributeValue(parentEvent
, CLASS
, classValue
);
228 name
= createNameByCode(state
, null);
231 Map
<String
, String
> nameMap
= new HashMap
<String
, String
>();
234 while (reader
.hasNext()) {
235 XMLEvent next
= readNoWhitespace(reader
);
236 if (isMyEndingElement(next
, parentEvent
)) {
237 // fill the name with all data gathered
238 fillName(state
, nameMap
, name
, next
);
239 handleNomText(state
, parentEvent
, text
, isNameType
);
241 } else if (isEndingElement(next
, ANNOTATION
)) {
242 // NOT YET IMPLEMENTED //TODO test
243 // handleSimpleAnnotation
244 popUnimplemented(next
.asEndElement());
245 }else if (isStartingElement(next
, FULL_NAME
)) {
246 handleFullName(state
, reader
, name
, next
);
247 } else if (isStartingElement(next
, NUM
)) {
248 handleNomNum(state
, reader
, next
);
249 } else if (isStartingElement(next
, NAME
)) {
250 handleName(state
, reader
, next
, nameMap
);
251 } else if (isStartingElement(next
, CITATION
)) {
252 handleCitation(state
, reader
, next
, name
);
253 } else if (next
.isCharacters()) {
254 text
+= next
.asCharacters().getData();
255 } else if (isStartingElement(next
, HOMONYM
)) {
256 handleNotYetImplementedElement(next
);
257 } else if (isStartingElement(next
, NOTES
)) {
258 handleNotYetImplementedElement(next
);
259 } else if (isStartingElement(next
, ANNOTATION
)) {
260 handleNotYetImplementedElement(next
);
262 handleUnexpectedElement(next
);
265 // TODO handle missing end element
266 throw new IllegalStateException("Nom has no closing tag");
270 * Handles appearance of text within <nom> tags.
271 * Usually this is not expected except for some information that is already handled
272 * elsewhere, e.g. the string Nametype is holding information that is available already
273 * via the surrounding nametype tag. Therefore this information can be neglected.
274 * This method is open for upcoming cases which need to be handled.
280 private void handleNomText(MarkupImportState state
, XMLEvent event
, String text
, boolean isNameType
) {
285 //neglect known redundant strings
286 if (isNameType
&& text
.matches("(?i)^Esp[\u00E8\u00C8]ce[·\\-\\s]type\\:$")){
288 }//neglect meaningless punctuation
289 else if (isPunctuation(text
)){
292 String message
= "Unhandled text in <nom> tag: \"%s\"";
293 fireWarningEvent(String
.format(message
, text
), event
, 4);
301 * @throws XMLStreamException
303 private void handleNomNum(MarkupImportState state
, XMLEventReader reader
,
304 XMLEvent next
) throws XMLStreamException
{
305 String num
= getCData(state
, reader
, next
);
306 num
= num
.replace(".", "");
307 num
= num
.replace(")", "");
308 if (StringUtils
.isNotBlank(num
)) {
309 if (state
.getCurrentTaxonNum() != null
310 && !state
.getCurrentTaxonNum().equals(num
)) {
311 String message
= "Taxontitle num and homotypes/nom/num differ ( %s <-> %s ). I use the later one.";
312 message
= String
.format(message
,
313 state
.getCurrentTaxonNum(), num
);
314 fireWarningEvent(message
, next
, 4);
316 state
.setCurrentTaxonNum(num
);
325 * @throws XMLStreamException
327 private void handleFullName(MarkupImportState state
, XMLEventReader reader
,
328 NonViralName
<?
> name
, XMLEvent next
) throws XMLStreamException
{
330 Map
<String
, Attribute
> attrs
= getAttributes(next
);
331 String rankStr
= getAndRemoveRequiredAttributeValue(next
,
333 Rank rank
= makeRank(state
, rankStr
, false);
336 String message
= "Rank was computed as null. This must not be.";
337 fireWarningEvent(message
, next
, 6);
338 name
.setRank(Rank
.UNKNOWN_RANK());
340 if (!attrs
.isEmpty()) {
341 handleUnexpectedAttributes(next
.getLocation(), attrs
);
343 fullNameStr
= getCData(state
, reader
, next
);
344 name
.setTitleCache(fullNameStr
, true);
347 private void handleName(MarkupImportState state
, XMLEventReader reader
,
348 XMLEvent parentEvent
, Map
<String
, String
> nameMap
)
349 throws XMLStreamException
{
350 String classValue
= getClassOnlyAttribute(parentEvent
);
353 while (reader
.hasNext()) {
354 XMLEvent next
= readNoWhitespace(reader
);
355 if (isMyEndingElement(next
, parentEvent
)) {
356 nameMap
.put(classValue
, text
);
358 } else if (isStartingElement(next
, ANNOTATION
)) {
359 handleNotYetImplementedElement(next
); // TODO test
360 // handleSimpleAnnotation
361 } else if (next
.isCharacters()) {
362 text
+= next
.asCharacters().getData();
364 handleUnexpectedElement(next
);
367 throw new IllegalStateException("name has no closing tag");
370 private void fillName(MarkupImportState state
, Map
<String
, String
> nameMap
,
371 NonViralName
<?
> name
, XMLEvent event
) {
373 // Ranks: family, subfamily, tribus, genus, subgenus, section,
374 // subsection, species, subspecies, variety, subvariety, forma
375 // infrank, paraut, author, infrparaut, infraut, status, notes
377 String infrank
= getAndRemoveMapKey(nameMap
, INFRANK
);
378 String authorStr
= getAndRemoveMapKey(nameMap
, AUTHOR
);
379 String paraut
= getAndRemoveMapKey(nameMap
, PARAUT
);
381 String infrParAut
= getAndRemoveMapKey(nameMap
, INFRPARAUT
);
382 String infrAut
= getAndRemoveMapKey(nameMap
, INFRAUT
);
384 String statusStr
= getAndRemoveMapKey(nameMap
, STATUS
);
385 String notes
= getAndRemoveMapKey(nameMap
, NOTES
);
387 if (!name
.isProtectedTitleCache()) { // otherwise fullName
389 makeRankDecision(state
, nameMap
, name
, event
, infrank
);
391 // test consistency of rank and authors
392 testRankAuthorConsistency(name
, event
, authorStr
, paraut
,
393 infrParAut
, infrAut
);
396 makeNomenclaturalAuthors(name
, event
, authorStr
, paraut
,
397 infrParAut
, infrAut
);
401 // TODO handle pro parte, pro syn. etc.
402 if (StringUtils
.isNotBlank(statusStr
)) {
403 String proPartePattern
= "(pro parte|p.p.)";
404 if (statusStr
.matches(proPartePattern
)) {
405 state
.setProParte(true);
408 // TODO handle trim earlier
409 statusStr
= statusStr
.trim();
410 NomenclaturalStatusType nomStatusType
= NomenclaturalStatusType
411 .getNomenclaturalStatusTypeByAbbreviation(statusStr
);
412 name
.addStatus(NomenclaturalStatus
.NewInstance(nomStatusType
));
413 } catch (UnknownCdmTypeException e
) {
414 String message
= "Status '%s' could not be recognized";
415 message
= String
.format(message
, statusStr
);
416 fireWarningEvent(message
, event
, 4);
421 if (StringUtils
.isNotBlank(notes
)) {
422 handleNotYetImplementedAttributeValue(event
, CLASS
, NOTES
);
435 private void makeRankDecision(MarkupImportState state
,
436 Map
<String
, String
> nameMap
, NonViralName
<?
> name
, XMLEvent event
,
439 for (String key
: nameMap
.keySet()) {
440 Rank rank
= makeRank(state
, key
, false);
442 handleNotYetImplementedAttributeValue(event
, CLASS
, key
);
444 if (name
.getRank() == null || rank
.isLower(name
.getRank())) {
447 String value
= nameMap
.get(key
);
448 if (rank
.isSupraGeneric() || rank
.isGenus()) {
449 if ((key
.equalsIgnoreCase(GENUS_ABBREVIATION
)
450 && isNotBlank(state
.getLatestGenusEpithet()) || isGenusAbbrev(
451 value
, state
.getLatestGenusEpithet()))) {
452 value
= state
.getLatestGenusEpithet();
454 name
.setGenusOrUninomial(toFirstCapital(value
));
455 } else if (rank
.isInfraGeneric()) {
456 name
.setInfraGenericEpithet(toFirstCapital(value
));
457 } else if (rank
.isSpecies()) {
458 if (state
.getConfig().isAllowCapitalSpeciesEpithet()
459 && isFirstCapitalWord(value
)) { // capital letters
466 name
.setSpecificEpithet(value
);
468 name
.setSpecificEpithet(value
.toLowerCase());
470 } else if (rank
.isInfraSpecific()) {
471 name
.setInfraSpecificEpithet(value
.toLowerCase());
473 String message
= "Invalid rank '%s'. Can't decide which epithet to fill with '%s'";
474 message
= String
.format(message
, rank
.getTitleCache(),
476 fireWarningEvent(message
, event
, 4);
481 // handle given infrank marker
482 if (StringUtils
.isNotBlank(infrankStr
)) {
483 Rank infRank
= makeRank(state
, infrankStr
, true);
485 if (infRank
== null) {
486 String message
= "Infrank '%s' rank not recognized";
487 message
= String
.format(message
, infrankStr
);
488 fireWarningEvent(message
, event
, 4);
490 if (name
.getRank() == null) {
491 name
.setRank(infRank
);
492 } else if (infRank
.isLower(name
.getRank())) {
493 String message
= "InfRank '%s' is lower than existing rank ";
494 message
= String
.format(message
, infrankStr
);
495 fireWarningEvent(message
, event
, 2);
496 name
.setRank(infRank
);
497 } else if (infRank
.equals(name
.getRank())) {
500 String message
= "InfRank '%s' is higher than existing rank ";
501 message
= String
.format(message
, infrankStr
);
502 fireWarningEvent(message
, event
, 2);
516 private void makeNomenclaturalAuthors(NonViralName name
, XMLEvent event
,
517 String authorStr
, String paraut
, String infrParAut
, String infrAut
) {
518 if (name
.getRank() != null && name
.getRank().isInfraSpecific()) {
519 if (StringUtils
.isNotBlank(infrAut
)) {
520 INomenclaturalAuthor
[] authorAndEx
= authorAndEx(infrAut
, event
);
521 name
.setCombinationAuthorTeam(authorAndEx
[0]);
522 name
.setExCombinationAuthorTeam(authorAndEx
[1]);
524 if (StringUtils
.isNotBlank(infrParAut
)) {
525 INomenclaturalAuthor
[] authorAndEx
= authorAndEx(infrParAut
,
527 name
.setBasionymAuthorTeam(authorAndEx
[0]);
528 name
.setExBasionymAuthorTeam(authorAndEx
[1]);
531 if (name
.getRank() == null) {
532 String message
= "No rank defined. Check correct usage of authors!";
533 fireWarningEvent(message
, event
, 4);
534 if (isNotBlank(infrParAut
) || isNotBlank(infrAut
)) {
539 if (StringUtils
.isNotBlank(authorStr
)) {
540 INomenclaturalAuthor
[] authorAndEx
= authorAndEx(authorStr
,
542 name
.setCombinationAuthorTeam(authorAndEx
[0]);
543 name
.setExCombinationAuthorTeam(authorAndEx
[1]);
545 if (StringUtils
.isNotBlank(paraut
)) {
546 INomenclaturalAuthor
[] authorAndEx
= authorAndEx(paraut
, event
);
547 name
.setBasionymAuthorTeam(authorAndEx
[0]);
548 name
.setExBasionymAuthorTeam(authorAndEx
[1]);
553 private TeamOrPersonBase
[] authorAndEx(String authorAndEx
, XMLEvent xmlEvent
) {
554 authorAndEx
= authorAndEx
.trim();
555 TeamOrPersonBase
[] result
= new TeamOrPersonBase
[2];
557 String
[] split
= authorAndEx
.split("\\sex\\s");
558 if (split
.length
> 2) {
559 String message
= "There is more then 1 ' ex ' in author string. Can't separate author and ex-author";
560 fireWarningEvent(message
, xmlEvent
, 4);
561 result
[0] = createAuthor(authorAndEx
);
562 } else if (split
.length
== 2) {
563 result
[0] = createAuthor(split
[1]);
564 result
[1] = createAuthor(split
[0]);
566 result
[0] = createAuthor(split
[0]);
572 * Returns the (empty) name with the correct homotypical group depending on
573 * the taxon status. Throws NPE if no currentTaxon is set in state.
576 * @param homotypicalGroup
580 private NonViralName
<?
> createName(MarkupImportState state
,
581 HomotypicalGroup homotypicalGroup
, boolean isSynonym
) {
582 NonViralName
<?
> name
;
583 Taxon taxon
= state
.getCurrentTaxon();
585 Rank defaultRank
= Rank
.SPECIES(); // can be any
586 name
= createNameByCode(state
, defaultRank
);
587 if (homotypicalGroup
!= null) {
588 name
.setHomotypicalGroup(homotypicalGroup
);
590 SynonymRelationshipType synonymType
= SynonymRelationshipType
591 .HETEROTYPIC_SYNONYM_OF();
592 if (taxon
.getHomotypicGroup().equals(homotypicalGroup
)) {
593 synonymType
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
595 taxon
.addSynonymName(name
, synonymType
);
597 name
= CdmBase
.deproxy(taxon
.getName(), NonViralName
.class);
602 private void handleCitation(MarkupImportState state
, XMLEventReader reader
,
603 XMLEvent parentEvent
, NonViralName name
) throws XMLStreamException
{
604 String classValue
= getClassOnlyAttribute(parentEvent
);
606 state
.setCitation(true);
607 boolean hasRefPart
= false;
608 Map
<String
, String
> refMap
= new HashMap
<String
, String
>();
609 while (reader
.hasNext()) {
610 XMLEvent next
= readNoWhitespace(reader
);
611 if (isMyEndingElement(next
, parentEvent
)) {
612 checkMandatoryElement(hasRefPart
, parentEvent
.asStartElement(),
614 Reference
<?
> reference
= createReference(state
, refMap
, next
);
615 String microReference
= refMap
.get(DETAILS
);
616 doCitation(state
, name
, classValue
, reference
, microReference
,
618 state
.setCitation(false);
620 } else if (isStartingElement(next
, REF_PART
)) {
621 handleRefPart(state
, reader
, next
, refMap
);
624 handleUnexpectedElement(next
);
627 throw new IllegalStateException("Citation has no closing tag");
631 private void handleRefPart(MarkupImportState state
, XMLEventReader reader
,
632 XMLEvent parentEvent
, Map
<String
, String
> refMap
)
633 throws XMLStreamException
{
634 String classValue
= getClassOnlyAttribute(parentEvent
);
637 while (reader
.hasNext()) {
638 XMLEvent next
= readNoWhitespace(reader
);
639 if (isMyEndingElement(next
, parentEvent
)) {
640 refMap
.put(classValue
, text
);
642 } else if (next
.isStartElement()) {
643 if (isStartingElement(next
, ANNOTATION
)) {
644 handleNotYetImplementedElement(next
); // TODO test
645 // handleSimpleAnnotation
646 } else if (isStartingElement(next
, ITALICS
)) {
647 handleNotYetImplementedElement(next
);
648 } else if (isStartingElement(next
, BOLD
)) {
649 handleNotYetImplementedElement(next
);
651 handleUnexpectedStartElement(next
.asStartElement());
653 } else if (next
.isCharacters()) {
654 text
+= next
.asCharacters().getData();
656 handleUnexpectedEndElement(next
.asEndElement());
659 throw new IllegalStateException("RefPart has no closing tag");
663 private void doCitation(MarkupImportState state
, NonViralName name
,
664 String classValue
, Reference reference
, String microCitation
,
665 XMLEvent parentEvent
) {
666 if (PUBLICATION
.equalsIgnoreCase(classValue
)) {
667 name
.setNomenclaturalReference(reference
);
668 name
.setNomenclaturalMicroReference(microCitation
);
669 } else if (USAGE
.equalsIgnoreCase(classValue
)) {
670 Taxon taxon
= state
.getCurrentTaxon();
671 TaxonDescription td
= getTaxonDescription(taxon
, state
.getConfig()
672 .getSourceReference(), false, true);
673 TextData citation
= TextData
.NewInstance(Feature
.CITATION());
674 // TODO name used in source
675 citation
.addSource(null, null, reference
, microCitation
);
676 td
.addElement(citation
);
677 } else if (TYPE
.equalsIgnoreCase(classValue
)) {
678 handleNotYetImplementedAttributeValue(parentEvent
, CLASS
,
681 // TODO Not yet implemented
682 handleNotYetImplementedAttributeValue(parentEvent
, CLASS
,
688 * Tests if the names rank is consistent with the given author strings.
689 * NOTE: Tags for authors are differ depending on the rank.
698 private void testRankAuthorConsistency(NonViralName name
, XMLEvent event
,
699 String authorStr
, String paraut
, String infrParAut
, String infrAut
) {
700 if (name
.getRank() == null) {
703 if (name
.getRank().isInfraSpecific()) {
704 if (StringUtils
.isBlank(infrParAut
)
705 && StringUtils
.isBlank(infrAut
) // was isNotBlank before
707 && (StringUtils
.isNotBlank(paraut
) || StringUtils
708 .isNotBlank(authorStr
)) && !name
.isAutonym()) {
709 String message
= "Rank is infraspecicific but has only specific or higher author(s)";
710 fireWarningEvent(message
, event
, 4);
713 // is not infraspecific
714 if (StringUtils
.isNotBlank(infrParAut
)
715 || StringUtils
.isNotBlank(infrAut
)) {
716 String message
= "Rank is not infraspecicific but name has infra author(s)";
717 fireWarningEvent(message
, event
, 4);
722 private Reference
<?
> createReference(MarkupImportState state
,
723 Map
<String
, String
> refMap
, XMLEvent parentEvent
) {
725 Reference
<?
> reference
;
727 String type
= getAndRemoveMapKey(refMap
, PUBTYPE
);
728 String authorStr
= getAndRemoveMapKey(refMap
, AUTHOR
);
729 String titleStr
= getAndRemoveMapKey(refMap
, PUBTITLE
);
730 String titleCache
= getAndRemoveMapKey(refMap
, PUBFULLNAME
);
731 String volume
= getAndRemoveMapKey(refMap
, VOLUME
);
732 String edition
= getAndRemoveMapKey(refMap
, EDITION
);
733 String editors
= getAndRemoveMapKey(refMap
, EDITORS
);
734 String year
= getAndRemoveMapKey(refMap
, YEAR
);
735 String pubName
= getAndRemoveMapKey(refMap
, PUBNAME
);
736 String pages
= getAndRemoveMapKey(refMap
, PAGES
);
738 if (state
.isCitation()) {
739 if (volume
!= null || "journal".equalsIgnoreCase(type
)) {
740 IArticle article
= ReferenceFactory
.newArticle();
741 if (pubName
!= null) {
742 IJournal journal
= ReferenceFactory
.newJournal();
743 journal
.setTitle(pubName
);
744 article
.setInJournal(journal
);
746 reference
= (Reference
<?
>) article
;
750 if (pubName
!= null) {
751 reference
= ReferenceFactory
.newBookSection();
753 reference
= ReferenceFactory
.newBook();
756 // TODO use existing author from name or before
757 TeamOrPersonBase
<?
> author
= createAuthor(authorStr
);
758 reference
.setAuthorTeam(author
);
760 reference
.setTitle(titleStr
);
761 if (StringUtils
.isNotBlank(titleCache
)) {
762 reference
.setTitleCache(titleCache
, true);
764 reference
.setEdition(edition
);
765 reference
.setEditor(editors
);
767 if (pubName
!= null) {
768 Reference
<?
> inReference
;
769 if (reference
.getType().equals(ReferenceType
.Article
)) {
770 inReference
= ReferenceFactory
.newJournal();
772 inReference
= ReferenceFactory
.newGeneric();
774 inReference
.setTitle(pubName
);
775 reference
.setInReference(inReference
);
778 } else { // no citation
779 if (volume
!= null || "journal".equalsIgnoreCase(type
)) {
780 IArticle article
= ReferenceFactory
.newArticle();
781 if (pubName
!= null) {
782 IJournal journal
= ReferenceFactory
.newJournal();
783 journal
.setTitle(pubName
);
784 article
.setInJournal(journal
);
786 reference
= (Reference
<?
>) article
;
789 Reference
<?
> bookOrPartOf
= ReferenceFactory
.newGeneric();
790 reference
= bookOrPartOf
;
794 TeamOrPersonBase
<?
> author
= createAuthor(authorStr
);
795 reference
.setAuthorTeam(author
);
797 reference
.setTitle(titleStr
);
798 if (StringUtils
.isNotBlank(titleCache
)) {
799 reference
.setTitleCache(titleCache
, true);
801 reference
.setEdition(edition
);
802 reference
.setEditor(editors
);
804 if (pubName
!= null) {
805 Reference
<?
> inReference
;
806 if (reference
.getType().equals(ReferenceType
.Article
)) {
807 inReference
= ReferenceFactory
.newJournal();
809 inReference
= ReferenceFactory
.newGeneric();
811 inReference
.setTitle(pubName
);
812 reference
.setInReference(inReference
);
815 reference
.setVolume(volume
);
816 reference
.setDatePublished(TimePeriod
.parseString(year
));
817 // TODO check if this is handled correctly in FM markup
818 reference
.setPages(pages
);
821 String
[] unhandledList
= new String
[] { ALTERNATEPUBTITLE
, ISSUE
,
823 for (String unhandled
: unhandledList
) {
824 String value
= getAndRemoveMapKey(refMap
, unhandled
);
825 if (isNotBlank(value
)) {
826 this.handleNotYetImplementedAttributeValue(parentEvent
, CLASS
,
831 for (String key
: refMap
.keySet()) {
832 if (!DETAILS
.equalsIgnoreCase(key
)) {
833 this.fireUnexpectedAttributeValue(parentEvent
, CLASS
, key
);
840 public Reference
<?
> handleReference(MarkupImportState state
,
841 XMLEventReader reader
, XMLEvent parentEvent
)
842 throws XMLStreamException
{
843 checkNoAttributes(parentEvent
);
845 boolean hasRefPart
= false;
846 Map
<String
, String
> refMap
= new HashMap
<String
, String
>();
847 while (reader
.hasNext()) {
848 XMLEvent next
= readNoWhitespace(reader
);
849 if (isMyEndingElement(next
, parentEvent
)) {
850 checkMandatoryElement(hasRefPart
, parentEvent
.asStartElement(),
852 Reference
<?
> reference
= createReference(state
, refMap
, next
);
854 } else if (isStartingElement(next
, REF_PART
)) {
855 handleRefPart(state
, reader
, next
, refMap
);
858 handleUnexpectedElement(next
);
861 // TODO handle missing end element
862 throw new IllegalStateException("<Reference> has no closing tag");